1 /**
2  * @file sipe-ocs2007.c
3  *
4  * pidgin-sipe
5  *
6  * Copyright (C) 2011-2018 SIPE Project <http://sipe.sourceforge.net/>
7  *
8  *
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.
13  *
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.
18  *
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
22  *
23  *
24  * OCS2007+ specific code
25  *
26  */
27 
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31 
32 #include <stdlib.h>
33 #include <string.h>
34 #include <time.h>
35 
36 #include <glib.h>
37 
38 #include "sipe-common.h"
39 #include "sipmsg.h"
40 #include "sip-csta.h"
41 #include "sip-transport.h"
42 #include "sipe-backend.h"
43 #include "sipe-buddy.h"
44 #include "sipe-cal.h"
45 #include "sipe-core.h"
46 #include "sipe-core-private.h"
47 #include "sipe-appshare.h"
48 #include "sipe-ews.h"
49 #include "sipe-media.h"
50 #include "sipe-nls.h"
51 #include "sipe-ocs2007.h"
52 #include "sipe-schedule.h"
53 #include "sipe-status.h"
54 #include "sipe-utils.h"
55 #include "sipe-xml.h"
56 
57 /** MS-PRES publication */
58 struct sipe_publication {
59 	gchar *category;
60 	guint instance;
61 	guint container;
62 	guint version;
63 	/** for 'state' category */
64 	int availability;
65 	/** for 'state:calendarState' category */
66 	char *cal_event_hash;
67 	/** for 'note' category */
68 	gchar *note;
69 	/** for 'calendarData' category; 300(Team) container */
70 	char *working_hours_xml_str;
71 	char *fb_start_str;
72 	char *free_busy_base64;
73 };
74 
75 /**
76  * 2007-style Activity and Availability.
77  *
78  * [MS-PRES] 3.7.5.5
79  *
80  * Conversion of legacyInterop availability ranges and activity tokens into
81  * SIPE activity tokens. The descriptions of availability ranges are defined at:
82  *
83  * http://msdn.microsoft.com/en-us/library/lync/dd941370%28v=office.13%29.aspx
84  *
85  * The values define the starting point of a range.
86  */
87 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_AVAILABLE      3000
88 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_AVAILABLE_IDLE 4500
89 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSY           6000
90 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSYIDLE       7500
91 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_DND            9000 /* do not disturb */
92 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_BRB           12000 /* be right back */
93 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_AWAY          15000
94 #define SIPE_OCS2007_LEGACY_AVAILIBILITY_OFFLINE       18000
95 
sipe_ocs2007_status_from_legacy_availability(guint availability,const gchar * activity)96 const gchar *sipe_ocs2007_status_from_legacy_availability(guint availability,
97 							  const gchar *activity)
98 {
99 	guint type;
100 
101 	if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_AVAILABLE) {
102 		type = SIPE_ACTIVITY_OFFLINE;
103 	} else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_AVAILABLE_IDLE) {
104 		type = SIPE_ACTIVITY_AVAILABLE;
105 	} else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSY) {
106 		type = SIPE_ACTIVITY_INACTIVE;
107 	} else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSYIDLE) {
108 		type = sipe_status_token_to_activity(activity);
109 		if ((type != SIPE_ACTIVITY_ON_PHONE) &&
110 		    (type != SIPE_ACTIVITY_IN_CONF))
111 			type = SIPE_ACTIVITY_BUSY;
112 	} else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_DND) {
113 		type = SIPE_ACTIVITY_BUSYIDLE;
114 	} else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_BRB) {
115 		type = sipe_status_token_to_activity(activity);
116 		if (type != SIPE_ACTIVITY_IN_PRES) {
117 			type = SIPE_ACTIVITY_DND;
118 		}
119 	} else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_AWAY) {
120 		type = SIPE_ACTIVITY_BRB;
121 	} else if (availability < SIPE_OCS2007_LEGACY_AVAILIBILITY_OFFLINE) {
122 		type = SIPE_ACTIVITY_AWAY;
123 	} else {
124 		type = SIPE_ACTIVITY_OFFLINE;
125 	}
126 
127 	return sipe_status_activity_to_token(type);
128 }
129 
sipe_ocs2007_legacy_activity_description(guint availability)130 const gchar *sipe_ocs2007_legacy_activity_description(guint availability)
131 {
132 	if ((availability >= SIPE_OCS2007_LEGACY_AVAILIBILITY_AVAILABLE_IDLE) &&
133 	    (availability <  SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSY)) {
134 		return(sipe_core_activity_description(SIPE_ACTIVITY_INACTIVE));
135 	} else if ((availability >= SIPE_OCS2007_LEGACY_AVAILIBILITY_BUSYIDLE) &&
136 		   (availability <  SIPE_OCS2007_LEGACY_AVAILIBILITY_DND)) {
137 		return(sipe_core_activity_description(SIPE_ACTIVITY_BUSYIDLE));
138 	} else {
139 		return(NULL);
140 	}
141 }
142 
143 /**
144  * @param sipe_status_id (in)
145  * @param activity_token (out) [only sipe-ocs2005.c/send_presence_soap()
146  *                              requests this token]
147  */
148 #define SIPE_OCS2007_AVAILABILITY_UNKNOWN     0
149 #define SIPE_OCS2007_AVAILABILITY_ONLINE   3500
150 #define SIPE_OCS2007_AVAILABILITY_BUSY     6500
151 #define SIPE_OCS2007_AVAILABILITY_DND      9500 /* do not disturb */
152 #define SIPE_OCS2007_AVAILABILITY_BRB     12500 /* be right back */
153 #define SIPE_OCS2007_AVAILABILITY_AWAY    15500
154 #define SIPE_OCS2007_AVAILABILITY_OFFLINE 18500
sipe_ocs2007_availability_from_status(const gchar * sipe_status_id,const gchar ** activity_token)155 guint sipe_ocs2007_availability_from_status(const gchar *sipe_status_id,
156 					    const gchar **activity_token)
157 {
158 	guint availability;
159 	guint activity;
160 
161 	if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_AWAY))) {
162 		availability = SIPE_OCS2007_AVAILABILITY_AWAY;
163 		activity     = SIPE_ACTIVITY_AWAY;
164 	} else if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_BRB))) {
165 		availability = SIPE_OCS2007_AVAILABILITY_BRB;
166 		activity     = SIPE_ACTIVITY_BRB;
167 	} else if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_DND))) {
168 		availability = SIPE_OCS2007_AVAILABILITY_DND;
169 		activity     = SIPE_ACTIVITY_DND;
170 	} else if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_BUSY))) {
171 		availability = SIPE_OCS2007_AVAILABILITY_BUSY;
172 		activity     = SIPE_ACTIVITY_BUSY;
173 	} else if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_AVAILABLE))) {
174 		availability = SIPE_OCS2007_AVAILABILITY_ONLINE;
175 		activity     = SIPE_ACTIVITY_ONLINE;
176 	} else if (sipe_strequal(sipe_status_id, sipe_status_activity_to_token(SIPE_ACTIVITY_UNSET))) {
177 		availability = SIPE_OCS2007_AVAILABILITY_UNKNOWN;
178 		activity     = SIPE_ACTIVITY_UNSET;
179 	} else {
180 		/* Offline or invisible */
181 		availability = SIPE_OCS2007_AVAILABILITY_OFFLINE;
182 		activity     = SIPE_ACTIVITY_OFFLINE;
183 	}
184 
185 	if (activity_token) {
186 		*activity_token = sipe_status_activity_to_token(activity);
187 	}
188 
189 	return(availability);
190 }
191 
sipe_ocs2007_status_is_busy(const gchar * status_id)192 gboolean sipe_ocs2007_status_is_busy(const gchar *status_id)
193 {
194 	return(SIPE_OCS2007_AVAILABILITY_BUSY >=
195 	       sipe_ocs2007_availability_from_status(status_id, NULL));
196 
197 }
198 
sipe_ocs2007_availability_is_away(guint availability)199 gboolean sipe_ocs2007_availability_is_away(guint availability)
200 {
201 	return(availability >= SIPE_OCS2007_LEGACY_AVAILIBILITY_AWAY);
202 }
203 
204 static void send_presence_publish(struct sipe_core_private *sipe_private,
205 				  const char *publications);
206 
free_publication(struct sipe_publication * publication)207 static void free_publication(struct sipe_publication *publication)
208 {
209 	g_free(publication->category);
210 	g_free(publication->cal_event_hash);
211 	g_free(publication->note);
212 
213 	g_free(publication->working_hours_xml_str);
214 	g_free(publication->fb_start_str);
215 	g_free(publication->free_busy_base64);
216 
217 	g_free(publication);
218 }
219 
220 struct hash_table_delete_payload {
221 	GHashTable *hash_table;
222 	guint container;
223 };
224 
sipe_remove_category_container_publications_cb(const gchar * name,struct sipe_publication * publication,struct hash_table_delete_payload * payload)225 static void sipe_remove_category_container_publications_cb(const gchar *name,
226 							   struct sipe_publication *publication,
227 							   struct hash_table_delete_payload *payload)
228 {
229 	if (publication->container == payload->container) {
230 		g_hash_table_remove(payload->hash_table, name);
231 	}
232 }
233 
sipe_remove_category_container_publications(GHashTable * our_publications,const gchar * category,guint container)234 static void sipe_remove_category_container_publications(GHashTable *our_publications,
235 							const gchar *category,
236 							guint container)
237 {
238 	struct hash_table_delete_payload payload;
239 	payload.hash_table = g_hash_table_lookup(our_publications, category);
240 
241 	if (!payload.hash_table) return;
242 
243 	payload.container = container;
244 	g_hash_table_foreach(payload.hash_table,
245 			     (GHFunc)sipe_remove_category_container_publications_cb,
246 			     &payload);
247 }
248 
249 /** MS-PRES container */
250 struct sipe_container {
251 	guint id;
252 	guint version;
253 	GSList *members;
254 };
255 
256 /** MS-PRES container member */
257 struct sipe_container_member {
258 	/** user, domain, sameEnterprise, federated, publicCloud; everyone */
259 	gchar *type;
260 	gchar *value;
261 };
262 
263 static const guint containers[] = {32000, 400, 300, 200, 100};
264 #define CONTAINERS_LEN (sizeof(containers) / sizeof(guint))
265 
free_container_member(struct sipe_container_member * member)266 static void free_container_member(struct sipe_container_member *member)
267 {
268 	if (!member) return;
269 
270 	g_free(member->type);
271 	g_free(member->value);
272 	g_free(member);
273 }
274 
sipe_ocs2007_free_container(struct sipe_container * container)275 static void sipe_ocs2007_free_container(struct sipe_container *container)
276 {
277 	GSList *entry;
278 
279 	if (!container) return;
280 
281 	entry = container->members;
282 	while (entry) {
283 		void *data = entry->data;
284 		entry = g_slist_remove(entry, data);
285 		free_container_member((struct sipe_container_member *)data);
286 	}
287 	g_free(container);
288 }
289 
sipe_core_buddy_menu_free(struct sipe_core_public * sipe_public)290 void sipe_core_buddy_menu_free(struct sipe_core_public *sipe_public)
291 {
292 	struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
293 	sipe_utils_slist_free_full(sipe_private->blist_menu_containers,
294 				   (GDestroyNotify) sipe_ocs2007_free_container);
295 	sipe_private->blist_menu_containers = NULL;
296 }
297 
blist_menu_remember_container(struct sipe_core_private * sipe_private,struct sipe_container * container)298 static void blist_menu_remember_container(struct sipe_core_private *sipe_private,
299 					  struct sipe_container *container)
300 {
301 	sipe_private->blist_menu_containers = g_slist_prepend(sipe_private->blist_menu_containers,
302 							      container);
303 }
304 
create_container(guint index,const gchar * member_type,const gchar * member_value,gboolean is_group)305 static struct sipe_container *create_container(guint index,
306 					       const gchar *member_type,
307 					       const gchar *member_value,
308 					       gboolean is_group)
309 {
310 	struct sipe_container *container = g_new0(struct sipe_container, 1);
311 	struct sipe_container_member *member = g_new0(struct sipe_container_member, 1);
312 
313 	container->id = is_group ? (guint) -1 : containers[index];
314 	container->members = g_slist_append(container->members, member);
315 	member->type = g_strdup(member_type);
316 	member->value = g_strdup(member_value);
317 
318 	return(container);
319 }
320 
sipe_ocs2007_free(struct sipe_core_private * sipe_private)321 void sipe_ocs2007_free(struct sipe_core_private *sipe_private)
322 {
323 	sipe_utils_slist_free_full(sipe_private->containers,
324 				   (GDestroyNotify) sipe_ocs2007_free_container);
325 }
326 
327 /**
328  * Finds locally stored MS-PRES container member
329  */
330 static struct sipe_container_member *
sipe_find_container_member(struct sipe_container * container,const gchar * type,const gchar * value)331 sipe_find_container_member(struct sipe_container *container,
332 			   const gchar *type,
333 			   const gchar *value)
334 {
335 	struct sipe_container_member *member;
336 	GSList *entry;
337 
338 	if (container == NULL || type == NULL) {
339 		return NULL;
340 	}
341 
342 	entry = container->members;
343 	while (entry) {
344 		member = entry->data;
345 		if (sipe_strcase_equal(member->type, type) &&
346 		    sipe_strcase_equal(member->value, value))
347 		{
348 			return member;
349 		}
350 		entry = entry->next;
351 	}
352 	return NULL;
353 }
354 
355 /**
356  * Finds locally stored MS-PRES container by id
357  */
sipe_find_container(struct sipe_core_private * sipe_private,guint id)358 static struct sipe_container *sipe_find_container(struct sipe_core_private *sipe_private,
359 						  guint id)
360 {
361 	GSList *entry = sipe_private->containers;
362 	while (entry) {
363 		struct sipe_container *container = entry->data;
364 		if (id == container->id) {
365 			return container;
366 		}
367 		entry = entry->next;
368 	}
369 	return NULL;
370 }
371 
sipe_find_member_access_level(struct sipe_core_private * sipe_private,const gchar * type,const gchar * value)372 static int sipe_find_member_access_level(struct sipe_core_private *sipe_private,
373 					 const gchar *type,
374 					 const gchar *value)
375 {
376 	unsigned int i = 0;
377 	const gchar *value_mod = value;
378 
379 	if (!type) return -1;
380 
381 	if (sipe_strequal("user", type)) {
382 		value_mod = sipe_get_no_sip_uri(value);
383 	}
384 
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;
389 
390 		member = sipe_find_container_member(container, type, value_mod);
391 		if (member) return containers[i];
392 	}
393 
394 	return -1;
395 }
396 
397 /**
398  * Returns pointer to domain part in provided Email URL
399  *
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
402  *
403  * Doesn't allocate memory
404  */
sipe_get_domain(const gchar * email)405 static const gchar *sipe_get_domain(const gchar *email)
406 {
407 	gchar *tmp;
408 
409 	if (!email) return NULL;
410 
411 	tmp = strstr(email, "@");
412 
413 	if (tmp && ((tmp+1) < (email + strlen(email)))) {
414 		return tmp+1;
415 	} else {
416 		return NULL;
417 	}
418 }
419 
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",
436 	"yahoo.com",
437 	NULL};
438 
sipe_is_public_domain(const gchar * domain)439 static gboolean sipe_is_public_domain(const gchar *domain)
440 {
441 	int i = 0;
442 	while (public_domains[i]) {
443 		if (sipe_strcase_equal(public_domains[i], domain)) {
444 			return TRUE;
445 		}
446 		i++;
447 	}
448 	return FALSE;
449 }
450 
451 /**
452  * Access Levels
453  * 32000 - Blocked
454  * 400   - Personal
455  * 300   - Team
456  * 200   - Company
457  * 100   - Public
458  */
sipe_ocs2007_access_level_name(guint id)459 const gchar *sipe_ocs2007_access_level_name(guint id)
460 {
461 	switch (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");
467 	}
468 	return _("Unknown");
469 }
470 
471 /** Member type: user, domain, sameEnterprise, federated, publicCloud; everyone */
sipe_ocs2007_find_access_level(struct sipe_core_private * sipe_private,const gchar * type,const gchar * value,gboolean * is_group_access)472 int sipe_ocs2007_find_access_level(struct sipe_core_private *sipe_private,
473 				   const gchar *type,
474 				   const gchar *value,
475 				   gboolean *is_group_access)
476 {
477 	int container_id = -1;
478 
479 	if (sipe_strequal("user", type)) {
480 		const char *domain;
481 		const char *no_sip_uri = sipe_get_no_sip_uri(value);
482 
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;
486 			return container_id;
487 		}
488 
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;
493 			return container_id;
494 		}
495 
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;
499 			return container_id;
500 		}
501 
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;
505 			return container_id;
506 		}
507 
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;
511 			return container_id;
512 		}
513 	} else {
514 		container_id = sipe_find_member_access_level(sipe_private, type, value);
515 		if (is_group_access) *is_group_access = FALSE;
516 	}
517 
518 	return container_id;
519 }
520 
get_access_domains(struct sipe_core_private * sipe_private)521 static GSList *get_access_domains(struct sipe_core_private *sipe_private)
522 {
523 	struct sipe_container *container;
524 	struct sipe_container_member *member;
525 	GSList *entry;
526 	GSList *entry2;
527 	GSList *res = NULL;
528 
529 	entry = sipe_private->containers;
530 	while (entry) {
531 		container = entry->data;
532 
533 		entry2 = container->members;
534 		while (entry2) {
535 			member = entry2->data;
536 			if (sipe_strcase_equal(member->type, "domain"))
537 			{
538 				res = sipe_utils_slist_insert_unique_sorted(res,
539 									    g_strdup(member->value),
540 									    (GCompareFunc)g_ascii_strcasecmp,
541 									    g_free);
542 			}
543 			entry2 = entry2->next;
544 		}
545 		entry = entry->next;
546 	}
547 	return res;
548 }
549 
sipe_send_container_members_prepare(const guint container_id,const guint container_version,const gchar * action,const gchar * type,const gchar * value,char ** container_xmls)550 static void sipe_send_container_members_prepare(const guint container_id,
551 						const guint container_version,
552 						const gchar *action,
553 						const gchar *type,
554 						const gchar *value,
555 						char **container_xmls)
556 {
557 	gchar *value_str = value ? g_strdup_printf(" value=\"%s\"", value) : g_strdup("");
558 	gchar *body;
559 
560 	if (!container_xmls) return;
561 
562 	body = g_strdup_printf(
563 		"<container id=\"%d\" version=\"%d\"><member action=\"%s\" type=\"%s\"%s/></container>",
564 		container_id,
565 		container_version,
566 		action,
567 		type,
568 		value_str);
569 	g_free(value_str);
570 
571 	if ((*container_xmls) == NULL) {
572 		*container_xmls = body;
573 	} else {
574 		char *tmp = *container_xmls;
575 
576 		*container_xmls = g_strconcat(*container_xmls, body, NULL);
577 		g_free(tmp);
578 		g_free(body);
579 	}
580 }
581 
sipe_send_set_container_members(struct sipe_core_private * sipe_private,char * container_xmls)582 static void sipe_send_set_container_members(struct sipe_core_private *sipe_private,
583 					    char *container_xmls)
584 {
585 	gchar *self;
586 	gchar *contact;
587 	gchar *hdr;
588 	gchar *body;
589 
590 	if (!container_xmls) return;
591 
592 	self = sip_uri_self(sipe_private);
593 	body = g_strdup_printf(
594 		"<setContainerMembers xmlns=\"http://schemas.microsoft.com/2006/09/sip/container-management\">"
595 		"%s"
596 		"</setContainerMembers>",
597 		container_xmls);
598 
599 	contact = get_contact(sipe_private);
600 	hdr = g_strdup_printf("Contact: %s\r\n"
601 			      "Content-Type: application/msrtc-setcontainermembers+xml\r\n", contact);
602 	g_free(contact);
603 
604 	sip_transport_service(sipe_private,
605 			      self,
606 			      hdr,
607 			      body,
608 			      NULL);
609 
610 	g_free(hdr);
611 	g_free(body);
612 	g_free(self);
613 }
614 
615 /**
616   * @param container_id	a new access level. If -1 then current access level
617   * 			is just removed (I.e. the member is removed from all containers).
618   * @param type		a type of member. E.g. "user", "sameEnterprise", etc.
619   * @param value	a value for member. E.g. SIP URI for "user" member type.
620   */
sipe_ocs2007_change_access_level(struct sipe_core_private * sipe_private,const int container_id,const gchar * type,const gchar * value)621 void sipe_ocs2007_change_access_level(struct sipe_core_private *sipe_private,
622 				      const int container_id,
623 				      const gchar *type,
624 				      const gchar *value)
625 {
626 	unsigned int i;
627 	int current_container_id = -1;
628 	char *container_xmls = NULL;
629 
630 	/* for each container: find/delete */
631 	for (i = 0; i < CONTAINERS_LEN; i++) {
632 		struct sipe_container_member *member;
633 		struct sipe_container *container = sipe_find_container(sipe_private, containers[i]);
634 
635 		if (!container) continue;
636 
637 		member = sipe_find_container_member(container, type, value);
638 		if (member) {
639 			current_container_id = containers[i];
640 			/* delete/publish current access level */
641 			if (container_id < 0 || container_id != current_container_id) {
642 				sipe_send_container_members_prepare(current_container_id, container->version, "remove", type, value, &container_xmls);
643 				/* remove member from our cache, to be able to recalculate AL below */
644 				container->members = g_slist_remove(container->members, member);
645 			}
646 		}
647 	}
648 
649 	/* recalculate AL below */
650 	current_container_id = sipe_ocs2007_find_access_level(sipe_private, type, value, NULL);
651 
652 	/* assign/publish new access level */
653 	if (container_id != current_container_id && container_id >= 0) {
654 		struct sipe_container *container = sipe_find_container(sipe_private, container_id);
655 		guint version = container ? container->version : 0;
656 
657 		sipe_send_container_members_prepare(container_id, version, "add", type, value, &container_xmls);
658 	}
659 
660 	if (container_xmls) {
661 		sipe_send_set_container_members(sipe_private, container_xmls);
662 	}
663 	g_free(container_xmls);
664 }
665 
sipe_core_change_access_level_from_container(struct sipe_core_public * sipe_public,gpointer parameter)666 void sipe_core_change_access_level_from_container(struct sipe_core_public *sipe_public,
667 						  gpointer parameter)
668 {
669 	struct sipe_container *container = parameter;
670 	struct sipe_container_member *member;
671 
672 	if (!container || !container->members) return;
673 
674 	member = ((struct sipe_container_member *)container->members->data);
675 
676 	if (!member->type) return;
677 
678 	SIPE_DEBUG_INFO("sipe_ocs2007_change_access_level_from_container: container->id=%d, member->type=%s, member->value=%s",
679 			container->id, member->type, member->value ? member->value : "");
680 
681 	sipe_ocs2007_change_access_level(SIPE_CORE_PRIVATE,
682 					 container->id,
683 					 member->type,
684 					 member->value);
685 
686 }
687 
sipe_core_change_access_level_for_domain(struct sipe_core_public * sipe_public,const gchar * domain,guint index)688 void sipe_core_change_access_level_for_domain(struct sipe_core_public *sipe_public,
689 					      const gchar *domain,
690 					      guint index)
691 {
692 	/* move Blocked first */
693 	guint i            = (index == 4) ? 0 : index + 1;
694 	guint container_id = containers[i];
695 
696 	SIPE_DEBUG_INFO("sipe_core_change_access_level_from_id: domain=%s, container_id=(%d)%d",
697 			domain ? domain : "", index, container_id);
698 
699 	sipe_ocs2007_change_access_level(SIPE_CORE_PRIVATE,
700 					 container_id,
701 					 "domain",
702 					 domain);
703 }
704 
705 /**
706  * Schedules process of self status publish
707  * based on own calendar information.
708  * Should be scheduled to the beginning of every
709  * 15 min interval, like:
710  * 13:00, 13:15, 13:30, 13:45, etc.
711  *
712  */
schedule_publish_update(struct sipe_core_private * sipe_private,time_t calculate_from)713 static void schedule_publish_update(struct sipe_core_private *sipe_private,
714 				    time_t calculate_from)
715 {
716 	int interval = 5*60;
717 	/** start of the beginning of closest 5 min interval. */
718 	time_t next_start = ((time_t)((int)((int)calculate_from)/interval + 1)*interval);
719 
720 	SIPE_DEBUG_INFO("sipe_sched_calendar_status_self_publish: calculate_from time: %s",
721 			sipe_utils_time_to_debug_str(localtime(&calculate_from)));
722 	SIPE_DEBUG_INFO("sipe_sched_calendar_status_self_publish: next start time    : %s",
723 			sipe_utils_time_to_debug_str(localtime(&next_start)));
724 
725 	sipe_schedule_seconds(sipe_private,
726 			      "<+2007-cal-status>",
727 			      NULL,
728 			      next_start - time(NULL),
729 			      sipe_ocs2007_presence_publish,
730 			      NULL);
731 }
732 
733 /**
734  * An availability XML entry for SIPE_PUB_XML_STATE_CALENDAR
735  * @param availability		(%d) Ex.: 6500
736  */
737 #define SIPE_PUB_XML_STATE_CALENDAR_AVAIL \
738 "<availability>%d</availability>"
739 /**
740  * An activity XML entry for SIPE_PUB_XML_STATE_CALENDAR
741  * @param token			(%s) Ex.: in-a-meeting
742  * @param minAvailability_attr	(%s) Ex.: minAvailability="6500"
743  * @param maxAvailability_attr	(%s) Ex.: maxAvailability="8999" or none
744  */
745 #define SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY \
746 "<activity token=\"%s\" %s %s></activity>"
747 /**
748  * Publishes 'calendarState' category.
749  * @param instance		(%u) Ex.: 1339299275
750  * @param version		(%u) Ex.: 1
751  * @param uri			(%s) Ex.: john@contoso.com
752  * @param start_time_str	(%s) Ex.: 2008-01-11T19:00:00Z
753  * @param availability		(%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_AVAIL
754  * @param activity		(%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
755  * @param meeting_subject	(%s) Ex.: Customer Meeting
756  * @param meeting_location	(%s) Ex.: Conf Room 100
757  *
758  * @param instance		(%u) Ex.: 1339299275
759  * @param version		(%u) Ex.: 1
760  * @param uri			(%s) Ex.: john@contoso.com
761  * @param start_time_str	(%s) Ex.: 2008-01-11T19:00:00Z
762  * @param availability		(%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_AVAIL
763  * @param activity		(%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
764  * @param meeting_subject	(%s) Ex.: Customer Meeting
765  * @param meeting_location	(%s) Ex.: Conf Room 100
766  */
767 #define SIPE_PUB_XML_STATE_CALENDAR \
768 	"<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\">"\
769 		"<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 			"%s"\
771 			"%s"\
772 			"<endpointLocation/>"\
773 			"<meetingSubject>%s</meetingSubject>"\
774 			"<meetingLocation>%s</meetingLocation>"\
775 		"</state>"\
776 	"</publication>"\
777 	"<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"endpoint\">"\
778 		"<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 			"%s"\
780 			"%s"\
781 			"<endpointLocation/>"\
782 			"<meetingSubject>%s</meetingSubject>"\
783 			"<meetingLocation>%s</meetingLocation>"\
784 		"</state>"\
785 	"</publication>"
786 /**
787  * Publishes to clear 'calendarState' and 'phoneState' category
788  * @param instance		(%u) Ex.: 1251210982
789  * @param version		(%u) Ex.: 1
790  */
791 #define SIPE_PUB_XML_STATE_CALENDAR_PHONE_CLEAR \
792 	"<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\" expires=\"0\"/>"\
793 	"<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"endpoint\" expires=\"0\"/>"
794 
795 /**
796  * Publishes to clear any category
797  * @param category_name		(%s) Ex.: state
798  * @param instance		(%u) Ex.: 536870912
799  * @param container		(%u) Ex.: 3
800  * @param version		(%u) Ex.: 1
801  * @param expireType		(%s) Ex.: static
802  */
803 #define SIPE_PUB_XML_PUBLICATION_CLEAR \
804 	"<publication categoryName=\"%s\" instance=\"%u\" container=\"%u\" version=\"%u\" expireType=\"%s\" expires=\"0\"/>"
805 
806 /**
807  * Publishes 'note' category.
808  * @param instance		(%u) Ex.: 2135971629; 0 for personal
809  * @param container		(%u) Ex.: 200
810  * @param version		(%u) Ex.: 2
811  * @param type			(%s) Ex.: personal or OOF
812  * @param startTime_attr	(%s) Ex.: startTime="2008-01-11T19:00:00Z"
813  * @param endTime_attr		(%s) Ex.: endTime="2008-01-15T19:00:00Z"
814  * @param body			(%s) Ex.: In the office
815  */
816 #define SIPE_PUB_XML_NOTE \
817 	"<publication categoryName=\"note\" instance=\"%u\" container=\"%u\" version=\"%d\" expireType=\"static\">"\
818 		"<note xmlns=\"http://schemas.microsoft.com/2006/09/sip/note\">"\
819 			"<body type=\"%s\" uri=\"\"%s%s>%s</body>"\
820 		"</note>"\
821 	"</publication>"
822 /**
823  * Publishes 'phoneState' category.
824  * @param instance		(%u) Ex.: 1339299275
825  * @param version		(%u) Ex.: 1
826  * @param availability		(%u) Ex.: 6500
827  * @param token			(%s) Ex.: on-the-phone
828  * @param minAvailability	(%u) generally same as availability
829  *
830  * @param instance		(%u) Ex.: 1339299275
831  * @param version		(%u) Ex.: 1
832  * @param availability          (%u) Ex.: 6500
833  * @param token			(%s) Ex.: on-the-phone
834  * @param minAvailability	(%u) generally same as availability
835  */
836 #define SIPE_PUB_XML_STATE_PHONE \
837 	"<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\">"\
838 		"<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"phoneState\">"\
839 			"<availability>%u</availability>"\
840 			"<activity token=\"%s\" minAvailability=\"%u\" maxAvailability=\"%u\"/>"\
841 		"</state>"\
842 	"</publication>"\
843 	"<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"endpoint\">"\
844 		"<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"phoneState\">"\
845 			"<availability>%u</availability>"\
846 			"<activity token=\"%s\" minAvailability=\"%u\" maxAvailability=\"%u\"/>"\
847 		"</state>"\
848 	"</publication>"
849 
850 /**
851  * Only Busy and OOF calendar event are published.
852  * Different instances are used for that.
853  *
854  * Must be g_free'd after use.
855  */
sipe_publish_get_category_state_calendar(struct sipe_core_private * sipe_private,struct sipe_cal_event * event,const char * uri,int cal_satus)856 static gchar *sipe_publish_get_category_state_calendar(struct sipe_core_private *sipe_private,
857 						       struct sipe_cal_event *event,
858 						       const char *uri,
859 						       int cal_satus)
860 {
861 	gchar *start_time_str;
862 	int availability = 0;
863 	gchar *res;
864 	gchar *tmp = NULL;
865 	guint instance = (cal_satus == SIPE_CAL_OOF) ?
866 		sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_CALENDAR_OOF) :
867 		sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_CALENDAR);
868 
869 	/* key is <category><instance><container> */
870 	gchar *key_2 = g_strdup_printf("<%s><%u><%u>", "state", instance, 2);
871 	gchar *key_3 = g_strdup_printf("<%s><%u><%u>", "state", instance, 3);
872 	gpointer state = g_hash_table_lookup(sipe_private->our_publications, "state");
873 	struct sipe_publication *publication_2 = state ? g_hash_table_lookup(state, key_2) : NULL;
874 	struct sipe_publication *publication_3 = state ? g_hash_table_lookup(state, key_3) : NULL;
875 
876 	g_free(key_2);
877 	g_free(key_3);
878 
879 	if (!publication_3 && !event) { /* was nothing, have nothing, exiting */
880 		SIPE_DEBUG_INFO("sipe_publish_get_category_state_calendar: "
881 				"Exiting as no publication and no event for cal_satus:%d", cal_satus);
882 		return NULL;
883 	}
884 
885 	if (event &&
886 	    publication_3 &&
887 	    (publication_3->availability == availability) &&
888 	    sipe_strequal(publication_3->cal_event_hash, (tmp = sipe_cal_event_hash(event))))
889 	{
890 		g_free(tmp);
891 		SIPE_DEBUG_INFO("sipe_publish_get_category_state_calendar: "
892 				"cal state has NOT changed for cal_satus:%d. Exiting.", cal_satus);
893 		return NULL; /* nothing to update */
894 	}
895 	g_free(tmp);
896 
897 	if (event &&
898 	    (event->cal_status == SIPE_CAL_BUSY ||
899 	     event->cal_status == SIPE_CAL_OOF))
900 	{
901 		gchar *availability_xml_str = NULL;
902 		gchar *activity_xml_str = NULL;
903 		gchar *escaped_subject  = event->subject  ? g_markup_escape_text(event->subject,  -1) : NULL;
904 		gchar *escaped_location = event->location ? g_markup_escape_text(event->location, -1) : NULL;
905 
906 		if (event->cal_status == SIPE_CAL_BUSY) {
907 			availability_xml_str = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_AVAIL,
908 							       SIPE_OCS2007_AVAILABILITY_BUSY);
909 		}
910 
911 		if (event->cal_status == SIPE_CAL_BUSY && event->is_meeting) {
912 			activity_xml_str = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY,
913 							   sipe_status_activity_to_token(SIPE_ACTIVITY_IN_MEETING),
914 							   "minAvailability=\"6500\"",
915 							   "maxAvailability=\"8999\"");
916 		} else if (event->cal_status == SIPE_CAL_OOF) {
917 			activity_xml_str = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY,
918 							   sipe_status_activity_to_token(SIPE_ACTIVITY_OOF),
919 							   "minAvailability=\"12000\"",
920 							   "");
921 		}
922 		start_time_str = sipe_utils_time_to_str(event->start_time);
923 
924 		res = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR,
925 					instance,
926 					publication_2 ? publication_2->version : 0,
927 					uri,
928 					start_time_str,
929 					availability_xml_str ? availability_xml_str : "",
930 					activity_xml_str ? activity_xml_str : "",
931 					escaped_subject  ? escaped_subject  : "",
932 					escaped_location ? escaped_location : "",
933 
934 					instance,
935 					publication_3 ? publication_3->version : 0,
936 					uri,
937 					start_time_str,
938 					availability_xml_str ? availability_xml_str : "",
939 					activity_xml_str ? activity_xml_str : "",
940 					escaped_subject  ? escaped_subject  : "",
941 					escaped_location ? escaped_location : ""
942 					);
943 		g_free(escaped_location);
944 		g_free(escaped_subject);
945 		g_free(start_time_str);
946 		g_free(availability_xml_str);
947 		g_free(activity_xml_str);
948 
949 	}
950 	else /* including !event, SIPE_CAL_FREE, SIPE_CAL_TENTATIVE */
951 	{
952 		res = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_PHONE_CLEAR,
953 					instance,
954 					publication_2 ? publication_2->version : 0,
955 
956 					instance,
957 					publication_3 ? publication_3->version : 0
958 					);
959 	}
960 
961 	return res;
962 }
963 
964 /**
965  * Returns 'note' XML part for publication.
966  * Must be g_free'd after use.
967  *
968  * Protocol format for Note is plain text.
969  *
970  * @param note a note in Sipe internal HTML format
971  * @param note_type either personal or OOF
972  */
sipe_publish_get_category_note(struct sipe_core_private * sipe_private,const char * note,const char * note_type,time_t note_start,time_t note_end,gboolean force_publish)973 static gchar *sipe_publish_get_category_note(struct sipe_core_private *sipe_private,
974 					     const char *note, /* html */
975 					     const char *note_type,
976 					     time_t note_start,
977 					     time_t note_end,
978 					     gboolean force_publish)
979 {
980 	guint instance = sipe_strequal("OOF", note_type) ? sipe_get_pub_instance(sipe_private, SIPE_PUB_NOTE_OOF) : 0;
981 	/* key is <category><instance><container> */
982 	gchar *key_note_200 = g_strdup_printf("<%s><%u><%u>", "note", instance, 200);
983 	gchar *key_note_300 = g_strdup_printf("<%s><%u><%u>", "note", instance, 300);
984 	gchar *key_note_400 = g_strdup_printf("<%s><%u><%u>", "note", instance, 400);
985 
986 	gpointer notes = g_hash_table_lookup(sipe_private->our_publications, "note");
987 	struct sipe_publication *publication_note_200 = notes ? g_hash_table_lookup(notes, key_note_200) : NULL;
988 	struct sipe_publication *publication_note_300 = notes ? g_hash_table_lookup(notes, key_note_300) : NULL;
989 	struct sipe_publication *publication_note_400 = notes ? g_hash_table_lookup(notes, key_note_400) : NULL;
990 
991 	char *tmp = note ? sipe_backend_markup_strip_html(note) : NULL;
992 	char *n1 = tmp ? g_markup_escape_text(tmp, -1) : NULL;
993 	const char *n2 = publication_note_200 ? publication_note_200->note : NULL;
994 	char *res, *tmp1, *tmp2, *tmp3;
995 	char *start_time_attr;
996 	char *end_time_attr;
997 
998 	g_free(tmp);
999 	tmp = NULL;
1000 	g_free(key_note_200);
1001 	g_free(key_note_300);
1002 	g_free(key_note_400);
1003 
1004 	/* we even need to republish empty note */
1005 	if (!force_publish && sipe_strequal(n1, n2))
1006 	{
1007 		SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_note: note has NOT changed. Exiting.");
1008 		g_free(n1);
1009 		return NULL; /* nothing to update */
1010 	}
1011 
1012 	start_time_attr = note_start ? g_strdup_printf(" startTime=\"%s\"", (tmp = sipe_utils_time_to_str(note_start))) : NULL;
1013 	g_free(tmp);
1014 	tmp = NULL;
1015 	end_time_attr = note_end ? g_strdup_printf(" endTime=\"%s\"", (tmp = sipe_utils_time_to_str(note_end))) : NULL;
1016 	g_free(tmp);
1017 
1018 	if (n1) {
1019 		tmp1 = g_strdup_printf(SIPE_PUB_XML_NOTE,
1020 				       instance,
1021 				       200,
1022 				       publication_note_200 ? publication_note_200->version : 0,
1023 				       note_type,
1024 				       start_time_attr ? start_time_attr : "",
1025 				       end_time_attr ? end_time_attr : "",
1026 				       n1);
1027 
1028 		tmp2 = g_strdup_printf(SIPE_PUB_XML_NOTE,
1029 				       instance,
1030 				       300,
1031 				       publication_note_300 ? publication_note_300->version : 0,
1032 				       note_type,
1033 				       start_time_attr ? start_time_attr : "",
1034 				       end_time_attr ? end_time_attr : "",
1035 				       n1);
1036 
1037 		tmp3 = g_strdup_printf(SIPE_PUB_XML_NOTE,
1038 				       instance,
1039 				       400,
1040 				       publication_note_400 ? publication_note_400->version : 0,
1041 				       note_type,
1042 				       start_time_attr ? start_time_attr : "",
1043 				       end_time_attr ? end_time_attr : "",
1044 				       n1);
1045 	} else {
1046 		tmp1 = g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR,
1047 					"note",
1048 					instance,
1049 					200,
1050 					publication_note_200 ? publication_note_200->version : 0,
1051 					"static");
1052 		tmp2 = g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR,
1053 					"note",
1054 					instance,
1055 					300,
1056 					publication_note_200 ? publication_note_200->version : 0,
1057 					"static");
1058 		tmp3 = g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR,
1059 					"note",
1060 					instance,
1061 					400,
1062 					publication_note_200 ? publication_note_200->version : 0,
1063 					"static");
1064 	}
1065 	res =  g_strconcat(tmp1, tmp2, tmp3, NULL);
1066 
1067 	g_free(start_time_attr);
1068 	g_free(end_time_attr);
1069 	g_free(tmp1);
1070 	g_free(tmp2);
1071 	g_free(tmp3);
1072 	g_free(n1);
1073 
1074 	return res;
1075 }
1076 
1077 /**
1078  * Publishes 'calendarData' category's WorkingHours.
1079  *
1080  * @param version	        (%u)  Ex.: 1
1081  * @param email	                (%s)  Ex.: alice@cosmo.local
1082  * @param working_hours_xml_str	(%s)  Ex.: <WorkingHours xmlns=.....
1083  *
1084  * @param version	        (%u)
1085  *
1086  * @param version	        (%u)
1087  * @param email	                (%s)
1088  * @param working_hours_xml_str	(%s)
1089  *
1090  * @param version	        (%u)
1091  * @param email	                (%s)
1092  * @param working_hours_xml_str	(%s)
1093  *
1094  * @param version	        (%u)
1095  * @param email	                (%s)
1096  * @param working_hours_xml_str	(%s)
1097  *
1098  * @param version	        (%u)
1099  */
1100 #define SIPE_PUB_XML_WORKING_HOURS \
1101 	"<publication categoryName=\"calendarData\" instance=\"0\" container=\"1\" version=\"%d\" expireType=\"static\">"\
1102 		"<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">%s"\
1103 		"</calendarData>"\
1104 	"</publication>"\
1105 	"<publication categoryName=\"calendarData\" instance=\"0\" container=\"100\" version=\"%d\" expireType=\"static\">"\
1106 		"<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1107 	"</publication>"\
1108 	"<publication categoryName=\"calendarData\" instance=\"0\" container=\"200\" version=\"%d\" expireType=\"static\">"\
1109 		"<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">%s"\
1110 		"</calendarData>"\
1111 	"</publication>"\
1112 	"<publication categoryName=\"calendarData\" instance=\"0\" container=\"300\" version=\"%d\" expireType=\"static\">"\
1113 		"<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">%s"\
1114 		"</calendarData>"\
1115 	"</publication>"\
1116 	"<publication categoryName=\"calendarData\" instance=\"0\" container=\"400\" version=\"%d\" expireType=\"static\">"\
1117 		"<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">%s"\
1118 		"</calendarData>"\
1119 	"</publication>"\
1120 	"<publication categoryName=\"calendarData\" instance=\"0\" container=\"32000\" version=\"%d\" expireType=\"static\">"\
1121 		"<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1122 	"</publication>"
1123 
1124 /**
1125  * Returns 'calendarData' XML part with WorkingHours for publication.
1126  * Must be g_free'd after use.
1127  */
sipe_publish_get_category_cal_working_hours(struct sipe_core_private * sipe_private)1128 static gchar *sipe_publish_get_category_cal_working_hours(struct sipe_core_private *sipe_private)
1129 {
1130 	struct sipe_calendar* cal = sipe_private->calendar;
1131 
1132 	/* key is <category><instance><container> */
1133 	gchar *key_cal_1     = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1);
1134 	gchar *key_cal_100   = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100);
1135 	gchar *key_cal_200   = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200);
1136 	gchar *key_cal_300   = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300);
1137 	gchar *key_cal_400   = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400);
1138 	gchar *key_cal_32000 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000);
1139 
1140 	gpointer tmp = g_hash_table_lookup(sipe_private->our_publications, "calendarData");
1141 	struct sipe_publication *publication_cal_1 = tmp ? g_hash_table_lookup(tmp, key_cal_1) : NULL;
1142 	struct sipe_publication *publication_cal_100 = tmp ? g_hash_table_lookup(tmp, key_cal_100) : NULL;
1143 	struct sipe_publication *publication_cal_200 = tmp ? g_hash_table_lookup(tmp, key_cal_200) : NULL;
1144 	struct sipe_publication *publication_cal_300 = tmp ? g_hash_table_lookup(tmp, key_cal_300) : NULL;
1145 	struct sipe_publication *publication_cal_400 = tmp ? g_hash_table_lookup(tmp, key_cal_400) : NULL;
1146 	struct sipe_publication *publication_cal_32000 = tmp ? g_hash_table_lookup(tmp, key_cal_32000) : NULL;
1147 
1148 	const char *n1 = cal ? cal->working_hours_xml_str : NULL;
1149 	const char *n2 = publication_cal_300 ? publication_cal_300->working_hours_xml_str : NULL;
1150 
1151 	g_free(key_cal_1);
1152 	g_free(key_cal_100);
1153 	g_free(key_cal_200);
1154 	g_free(key_cal_300);
1155 	g_free(key_cal_400);
1156 	g_free(key_cal_32000);
1157 
1158 	if (!cal || is_empty(cal->email) || is_empty(cal->working_hours_xml_str)) {
1159 		SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_working_hours: no data to publish, exiting");
1160 		return NULL;
1161 	}
1162 
1163 	if (sipe_strequal(n1, n2))
1164 	{
1165 		SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_working_hours: WorkingHours has NOT changed. Exiting.");
1166 		return NULL; /* nothing to update */
1167 	}
1168 
1169 	return g_strdup_printf(SIPE_PUB_XML_WORKING_HOURS,
1170 				/* 1 */
1171 				publication_cal_1 ? publication_cal_1->version : 0,
1172 				cal->email,
1173 				cal->working_hours_xml_str,
1174 				/* 100 - Public */
1175 				publication_cal_100 ? publication_cal_100->version : 0,
1176 				/* 200 - Company */
1177 				publication_cal_200 ? publication_cal_200->version : 0,
1178 				cal->email,
1179 				cal->working_hours_xml_str,
1180 				/* 300 - Team */
1181 				publication_cal_300 ? publication_cal_300->version : 0,
1182 				cal->email,
1183 				cal->working_hours_xml_str,
1184 				/* 400 - Personal */
1185 				publication_cal_400 ? publication_cal_400->version : 0,
1186 				cal->email,
1187 				cal->working_hours_xml_str,
1188 				/* 32000 - Blocked */
1189 				publication_cal_32000 ? publication_cal_32000->version : 0
1190 			      );
1191 }
1192 
1193 /**
1194  * Publishes 'calendarData' category's FreeBusy.
1195  *
1196  * @param instance	        (%u)  Ex.: 1300372959
1197  * @param version	        (%u)  Ex.: 1
1198  *
1199  * @param instance	        (%u)  Ex.: 1300372959
1200  * @param version	        (%u)  Ex.: 1
1201  *
1202  * @param instance	        (%u)  Ex.: 1300372959
1203  * @param version	        (%u)  Ex.: 1
1204  * @param email	                (%s)  Ex.: alice@cosmo.local
1205  * @param fb_start_time_str	(%s)  Ex.: 2009-12-03T00:00:00Z
1206  * @param free_busy_base64	(%s)  Ex.: AAAAAAAAAAAAAAAAAAAAA.....
1207  *
1208  * @param instance	        (%u)  Ex.: 1300372959
1209  * @param version	        (%u)  Ex.: 1
1210  * @param email	                (%s)  Ex.: alice@cosmo.local
1211  * @param fb_start_time_str	(%s)  Ex.: 2009-12-03T00:00:00Z
1212  * @param free_busy_base64	(%s)  Ex.: AAAAAAAAAAAAAAAAAAAAA.....
1213  *
1214  * @param instance	        (%u)  Ex.: 1300372959
1215  * @param version	        (%u)  Ex.: 1
1216  * @param email	                (%s)  Ex.: alice@cosmo.local
1217  * @param fb_start_time_str	(%s)  Ex.: 2009-12-03T00:00:00Z
1218  * @param free_busy_base64	(%s)  Ex.: AAAAAAAAAAAAAAAAAAAAA.....
1219  *
1220  * @param instance	        (%u)  Ex.: 1300372959
1221  * @param version	        (%u)  Ex.: 1
1222  */
1223 #define SIPE_PUB_XML_FREE_BUSY \
1224 	"<publication categoryName=\"calendarData\" instance=\"%u\" container=\"1\" version=\"%d\" expireType=\"endpoint\">"\
1225 		"<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1226 	"</publication>"\
1227 	"<publication categoryName=\"calendarData\" instance=\"%u\" container=\"100\" version=\"%d\" expireType=\"endpoint\">"\
1228 		"<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1229 	"</publication>"\
1230 	"<publication categoryName=\"calendarData\" instance=\"%u\" container=\"200\" version=\"%d\" expireType=\"endpoint\">"\
1231 		"<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">"\
1232 			"<freeBusy startTime=\"%s\" granularity=\"PT15M\" encodingVersion=\"1\">%s</freeBusy>"\
1233 		"</calendarData>"\
1234 	"</publication>"\
1235 	"<publication categoryName=\"calendarData\" instance=\"%u\" container=\"300\" version=\"%d\" expireType=\"endpoint\">"\
1236 		"<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">"\
1237 			"<freeBusy startTime=\"%s\" granularity=\"PT15M\" encodingVersion=\"1\">%s</freeBusy>"\
1238 		"</calendarData>"\
1239 	"</publication>"\
1240 	"<publication categoryName=\"calendarData\" instance=\"%u\" container=\"400\" version=\"%d\" expireType=\"endpoint\">"\
1241 		"<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">"\
1242 			"<freeBusy startTime=\"%s\" granularity=\"PT15M\" encodingVersion=\"1\">%s</freeBusy>"\
1243 		"</calendarData>"\
1244 	"</publication>"\
1245 	"<publication categoryName=\"calendarData\" instance=\"%u\" container=\"32000\" version=\"%d\" expireType=\"endpoint\">"\
1246 		"<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
1247 	"</publication>"
1248 
1249 /**
1250  * Returns 'calendarData' XML part with FreeBusy for publication.
1251  * Must be g_free'd after use.
1252  */
sipe_publish_get_category_cal_free_busy(struct sipe_core_private * sipe_private)1253 static gchar *sipe_publish_get_category_cal_free_busy(struct sipe_core_private *sipe_private)
1254 {
1255 	struct sipe_calendar* cal = sipe_private->calendar;
1256 	guint cal_data_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_CALENDAR_DATA);
1257 	char *fb_start_str;
1258 	char *free_busy_base64;
1259 	/* const char *st; */
1260 	/* const char *fb; */
1261 	char *res;
1262 
1263 	/* key is <category><instance><container> */
1264 	gchar *key_cal_1     = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 1);
1265 	gchar *key_cal_100   = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 100);
1266 	gchar *key_cal_200   = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 200);
1267 	gchar *key_cal_300   = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 300);
1268 	gchar *key_cal_400   = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 400);
1269 	gchar *key_cal_32000 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 32000);
1270 
1271 	gpointer tmp = g_hash_table_lookup(sipe_private->our_publications, "calendarData");
1272 	struct sipe_publication *publication_cal_1 = tmp ? g_hash_table_lookup(tmp, key_cal_1) : NULL;
1273 	struct sipe_publication *publication_cal_100 = tmp ? g_hash_table_lookup(tmp, key_cal_100) : NULL;
1274 	struct sipe_publication *publication_cal_200 = tmp ? g_hash_table_lookup(tmp, key_cal_200) : NULL;
1275 	struct sipe_publication *publication_cal_300 = tmp ? g_hash_table_lookup(tmp, key_cal_300) : NULL;
1276 	struct sipe_publication *publication_cal_400 = tmp ? g_hash_table_lookup(tmp, key_cal_400) : NULL;
1277 	struct sipe_publication *publication_cal_32000 = tmp ? g_hash_table_lookup(tmp, key_cal_32000) : NULL;
1278 
1279 	g_free(key_cal_1);
1280 	g_free(key_cal_100);
1281 	g_free(key_cal_200);
1282 	g_free(key_cal_300);
1283 	g_free(key_cal_400);
1284 	g_free(key_cal_32000);
1285 
1286 	if (!cal || is_empty(cal->email) || !cal->fb_start || is_empty(cal->free_busy)) {
1287 		SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_free_busy: no data to publish, exiting");
1288 		return NULL;
1289 	}
1290 
1291 	fb_start_str = sipe_utils_time_to_str(cal->fb_start);
1292 	free_busy_base64 = sipe_cal_get_freebusy_base64(cal->free_busy);
1293 
1294 	/* we will rebuplish the same data to refresh publication time,
1295 	 * so if data from multiple sources, most recent will be choosen
1296 	 */
1297 	// st = publication_cal_300 ? publication_cal_300->fb_start_str : NULL;
1298 	// fb = publication_cal_300 ? publication_cal_300->free_busy_base64 : NULL;
1299 	//
1300 	//if (sipe_strequal(st, fb_start_str) && sipe_strequal(fb, free_busy_base64))
1301 	//{
1302 	//	SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_free_busy: FreeBusy has NOT changed. Exiting.");
1303 	//	g_free(fb_start_str);
1304 	//	g_free(free_busy_base64);
1305 	//	return NULL; /* nothing to update */
1306 	//}
1307 
1308 	res = g_strdup_printf(SIPE_PUB_XML_FREE_BUSY,
1309 				/* 1 */
1310 				cal_data_instance,
1311 				publication_cal_1 ? publication_cal_1->version : 0,
1312 				/* 100 - Public */
1313 				cal_data_instance,
1314 				publication_cal_100 ? publication_cal_100->version : 0,
1315 				/* 200 - Company */
1316 				cal_data_instance,
1317 				publication_cal_200 ? publication_cal_200->version : 0,
1318 				cal->email,
1319 				fb_start_str,
1320 				free_busy_base64,
1321 				/* 300 - Team */
1322 				cal_data_instance,
1323 				publication_cal_300 ? publication_cal_300->version : 0,
1324 				cal->email,
1325 				fb_start_str,
1326 				free_busy_base64,
1327 				/* 400 - Personal */
1328 				cal_data_instance,
1329 				publication_cal_400 ? publication_cal_400->version : 0,
1330 				cal->email,
1331 				fb_start_str,
1332 				free_busy_base64,
1333 				/* 32000 - Blocked */
1334 				cal_data_instance,
1335 				publication_cal_32000 ? publication_cal_32000->version : 0
1336 			     );
1337 
1338 	g_free(fb_start_str);
1339 	g_free(free_busy_base64);
1340 	return res;
1341 }
1342 
1343 #ifdef HAVE_VV
1344 #define SIPE_PUB_XML_DEVICE_VV \
1345 				"<voice capture=\"true\" render=\"true\" publish=\"false\"/>"\
1346 				"<video capture=\"true\" render=\"true\" publish=\"false\"/>"
1347 #else
1348 #define SIPE_PUB_XML_DEVICE_VV
1349 #endif
1350 
1351 #ifdef HAVE_FREERDP
1352 #define SIPE_PUB_XML_DEVICE_APPSHARE \
1353 				"<applicationSharing capture=\"true\" render=\"true\" publish=\"false\"/>"\
1354 				"<contentPowerPoint capture=\"true\" render=\"true\" publish=\"false\"/>"
1355 #else
1356 #define SIPE_PUB_XML_DEVICE_APPSHARE
1357 #endif
1358 
1359 /**
1360  * Publishes 'device' category.
1361  * @param instance	(%u) Ex.: 1938468728
1362  * @param version	(%u) Ex.: 1
1363  * @param endpointId	(%s) Ex.: C707E38E-1E10-5413-94D9-ECAC260A0269
1364  * @param uri		(%s) Self URI. Ex.: sip:alice7@boston.local
1365  * @param timezone	(%s) Ex.: 00:00:00+01:00
1366  * @param machineName	(%s) Ex.: BOSTON-OCS07
1367  */
1368 #define SIPE_PUB_XML_DEVICE \
1369 	"<publication categoryName=\"device\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\">"\
1370 		"<device xmlns=\"http://schemas.microsoft.com/2006/09/sip/device\" endpointId=\"%s\">"\
1371 			"<capabilities preferred=\"false\" uri=\"%s\">"\
1372 				"<text capture=\"true\" render=\"true\" publish=\"false\"/>"\
1373 				"<gifInk capture=\"false\" render=\"true\" publish=\"false\"/>"\
1374 				"<isfInk capture=\"false\" render=\"true\" publish=\"false\"/>"\
1375 				SIPE_PUB_XML_DEVICE_VV\
1376 				SIPE_PUB_XML_DEVICE_APPSHARE\
1377 			"</capabilities>"\
1378 			"<timezone>%s</timezone>"\
1379 			"<machineName>%s</machineName>"\
1380 		"</device>"\
1381 	"</publication>"
1382 
1383 /**
1384  * Returns 'device' XML part for publication.
1385  * Must be g_free'd after use.
1386  */
sipe_publish_get_category_device(struct sipe_core_private * sipe_private)1387 static gchar *sipe_publish_get_category_device(struct sipe_core_private *sipe_private)
1388 {
1389 	gchar *uri;
1390 	gchar *doc;
1391 	gchar *uuid = get_uuid(sipe_private);
1392 	guint device_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_DEVICE);
1393 	/* key is <category><instance><container> */
1394 	gchar *key = g_strdup_printf("<%s><%u><%u>", "device", device_instance, 2);
1395 	GHashTable *tmp = g_hash_table_lookup(sipe_private->our_publications, "device");
1396 	struct sipe_publication *publication = tmp ? g_hash_table_lookup(tmp, key) : NULL;
1397 
1398 	g_free(key);
1399 
1400 	uri = sip_uri_self(sipe_private);
1401 	doc = g_strdup_printf(SIPE_PUB_XML_DEVICE,
1402 		device_instance,
1403 		publication ? publication->version : 0,
1404 		uuid,
1405 		uri,
1406 		"00:00:00+01:00", /* @TODO make timezone real*/
1407 		g_get_host_name()
1408 	);
1409 
1410 	g_free(uri);
1411 	g_free(uuid);
1412 
1413 	return doc;
1414 }
1415 
1416 /**
1417  * Publishes 'machineState' category.
1418  * @param instance	(%u) Ex.: 926460663
1419  * @param version	(%u) Ex.: 22
1420  * @param availability	(%d) Ex.: 3500
1421  * @param instance	(%u) Ex.: 926460663
1422  * @param version	(%u) Ex.: 22
1423  * @param availability	(%d) Ex.: 3500
1424  */
1425 #define SIPE_PUB_XML_STATE_MACHINE \
1426 	"<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\">"\
1427 		"<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"machineState\">"\
1428 			"<availability>%d</availability>"\
1429 			"<endpointLocation/>"\
1430 		"</state>"\
1431 	"</publication>"\
1432 	"<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"endpoint\">"\
1433 		"<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"machineState\">"\
1434 			"<availability>%d</availability>"\
1435 			"<endpointLocation/>"\
1436 		"</state>"\
1437 	"</publication>"
1438 
1439 /**
1440  * Publishes 'userState' category.
1441  * @param instance	(%u) User. Ex.: 536870912
1442  * @param version	(%u) User Container 2. Ex.: 22
1443  * @param availability	(%d) User Container 2. Ex.: 15500
1444  * @param instance	(%u) User. Ex.: 536870912
1445  * @param version	(%u) User Container 3.Ex.: 22
1446  * @param availability	(%d) User Container 3. Ex.: 15500
1447  */
1448 #define SIPE_PUB_XML_STATE_USER \
1449 	"<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"static\">"\
1450 		"<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"true\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"userState\">"\
1451 			"<availability>%d</availability>"\
1452 			"<endpointLocation/>"\
1453 		"</state>"\
1454 	"</publication>"\
1455 	"<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"static\">"\
1456 		"<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"true\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"userState\">"\
1457 			"<availability>%d</availability>"\
1458 			"<endpointLocation/>"\
1459 		"</state>"\
1460 	"</publication>"
1461 
1462 /**
1463  * A service method - use
1464  * - send_publish_get_category_state_machine and
1465  * - send_publish_get_category_state_user instead.
1466  * Must be g_free'd after use.
1467  */
sipe_publish_get_category_state(struct sipe_core_private * sipe_private,gboolean force_publish,gboolean is_user_state)1468 static gchar *sipe_publish_get_category_state(struct sipe_core_private *sipe_private,
1469 					      gboolean force_publish,
1470 					      gboolean is_user_state)
1471 {
1472 	int availability = sipe_ocs2007_availability_from_status(sipe_private->status, NULL);
1473 	guint instance = is_user_state ? sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_USER) :
1474 					 sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_MACHINE);
1475 	/* key is <category><instance><container> */
1476 	gchar *key_2 = g_strdup_printf("<%s><%u><%u>", "state", instance, 2);
1477 	gchar *key_3 = g_strdup_printf("<%s><%u><%u>", "state", instance, 3);
1478 	gpointer state = g_hash_table_lookup(sipe_private->our_publications, "state");
1479 	struct sipe_publication *publication_2 = state ? g_hash_table_lookup(state, key_2) : NULL;
1480 	struct sipe_publication *publication_3 = state ? g_hash_table_lookup(state, key_3) : NULL;
1481 
1482 	g_free(key_2);
1483 	g_free(key_3);
1484 
1485 	if (!force_publish && publication_2 && (publication_2->availability == availability))
1486 	{
1487 		SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_state: state has NOT changed. Exiting.");
1488 		return NULL; /* nothing to update */
1489 	}
1490 
1491 	return g_strdup_printf( is_user_state ? SIPE_PUB_XML_STATE_USER : SIPE_PUB_XML_STATE_MACHINE,
1492 				instance,
1493 				publication_2 ? publication_2->version : 0,
1494 				availability,
1495 				instance,
1496 				publication_3 ? publication_3->version : 0,
1497 				availability);
1498 }
1499 
1500 /**
1501  * Returns 'machineState' XML part for publication.
1502  * Must be g_free'd after use.
1503  */
sipe_publish_get_category_state_machine(struct sipe_core_private * sipe_private,gboolean force_publish)1504 static gchar *sipe_publish_get_category_state_machine(struct sipe_core_private *sipe_private,
1505 						      gboolean force_publish)
1506 {
1507 	return sipe_publish_get_category_state(sipe_private, force_publish, FALSE);
1508 }
1509 
1510 /**
1511  * Returns 'userState' XML part for publication.
1512  * Must be g_free'd after use.
1513  */
sipe_publish_get_category_state_user(struct sipe_core_private * sipe_private,gboolean force_publish)1514 static gchar *sipe_publish_get_category_state_user(struct sipe_core_private *sipe_private,
1515 						   gboolean force_publish)
1516 {
1517 	return sipe_publish_get_category_state(sipe_private, force_publish, TRUE);
1518 }
1519 
send_publish_category_initial(struct sipe_core_private * sipe_private)1520 static void send_publish_category_initial(struct sipe_core_private *sipe_private)
1521 {
1522 	gchar *pub_device   = sipe_publish_get_category_device(sipe_private);
1523 	gchar *pub_machine;
1524 	gchar *pub_user;
1525 	gchar *publications;
1526 
1527 	sipe_status_set_activity(sipe_private,
1528 				 sipe_backend_status(SIPE_CORE_PUBLIC));
1529 
1530 	pub_machine  = sipe_publish_get_category_state_machine(sipe_private,
1531 							       TRUE);
1532 	pub_user = sipe_publish_get_category_state_user(sipe_private, TRUE);
1533 
1534 	publications = g_strdup_printf("%s%s%s",
1535 				       pub_device,
1536 				       pub_machine ? pub_machine : "",
1537 				       pub_user ? pub_user : "");
1538 	g_free(pub_device);
1539 	g_free(pub_machine);
1540 	g_free(pub_user);
1541 
1542 	send_presence_publish(sipe_private, publications);
1543 	g_free(publications);
1544 }
1545 
process_send_presence_category_publish_response(struct sipe_core_private * sipe_private,struct sipmsg * msg,struct transaction * trans)1546 static gboolean process_send_presence_category_publish_response(struct sipe_core_private *sipe_private,
1547 								struct sipmsg *msg,
1548 								struct transaction *trans)
1549 {
1550 	const gchar *contenttype = sipmsg_find_header(msg, "Content-Type");
1551 
1552 	if (msg->response == 200 && g_str_has_prefix(contenttype, "application/vnd-microsoft-roaming-self+xml")) {
1553 		sipe_ocs2007_process_roaming_self(sipe_private, msg);
1554 	} else if (msg->response == 409 && g_str_has_prefix(contenttype, "application/msrtc-fault+xml")) {
1555 		sipe_xml *xml;
1556 		const sipe_xml *node;
1557 		gchar *fault_code;
1558 		GHashTable *faults;
1559 		int index_our;
1560 		gboolean has_device_publication = FALSE;
1561 
1562 		xml = sipe_xml_parse(msg->body, msg->bodylen);
1563 
1564 		/* test if version mismatch fault */
1565 		fault_code = sipe_xml_data(sipe_xml_child(xml, "Faultcode"));
1566 		if (!sipe_strequal(fault_code, "Client.BadCall.WrongDelta")) {
1567 			SIPE_DEBUG_INFO("process_send_presence_category_publish_response: unsupported fault code:%s returning.", fault_code);
1568 			g_free(fault_code);
1569 			sipe_xml_free(xml);
1570 			return TRUE;
1571 		}
1572 		g_free(fault_code);
1573 
1574 		/* accumulating information about faulty versions */
1575 		faults = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
1576 		for (node = sipe_xml_child(xml, "details/operation");
1577 		     node;
1578 		     node = sipe_xml_twin(node))
1579 		{
1580 			const gchar *index = sipe_xml_attribute(node, "index");
1581 			const gchar *curVersion = sipe_xml_attribute(node, "curVersion");
1582 
1583 			g_hash_table_insert(faults, g_strdup(index), g_strdup(curVersion));
1584 			SIPE_DEBUG_INFO("fault added: index:%s curVersion:%s", index, curVersion);
1585 		}
1586 		sipe_xml_free(xml);
1587 
1588 		/* here we are parsing our own request to figure out what publication
1589 		 * referenced here only by index went wrong
1590 		 */
1591 		xml = sipe_xml_parse(trans->msg->body, trans->msg->bodylen);
1592 
1593 		/* publication */
1594 		for (node = sipe_xml_child(xml, "publications/publication"),
1595 		     index_our = 1; /* starts with 1 - our first publication */
1596 		     node;
1597 		     node = sipe_xml_twin(node), index_our++)
1598 		{
1599 			gchar *idx = g_strdup_printf("%d", index_our);
1600 			const gchar *curVersion = g_hash_table_lookup(faults, idx);
1601 			const gchar *categoryName = sipe_xml_attribute(node, "categoryName");
1602 			g_free(idx);
1603 
1604 			if (sipe_strequal("device", categoryName)) {
1605 				has_device_publication = TRUE;
1606 			}
1607 
1608 			if (curVersion) { /* fault exist on this index */
1609 				const gchar *container = sipe_xml_attribute(node, "container");
1610 				const gchar *instance = sipe_xml_attribute(node, "instance");
1611 				/* key is <category><instance><container> */
1612 				gchar *key = g_strdup_printf("<%s><%s><%s>", categoryName, instance, container);
1613 				GHashTable *category = g_hash_table_lookup(sipe_private->our_publications, categoryName);
1614 
1615 				if (category) {
1616 					struct sipe_publication *publication =
1617 						g_hash_table_lookup(category, key);
1618 
1619 					SIPE_DEBUG_INFO("key is %s", key);
1620 
1621 					if (publication) {
1622 						SIPE_DEBUG_INFO("Updating %s with version %s. Was %d before.",
1623 								key, curVersion, publication->version);
1624 						/* updating publication's version to the correct one */
1625 						publication->version = atoi(curVersion);
1626 					}
1627 				} else {
1628 					/* We somehow lost this category from our publications... */
1629 					struct sipe_publication *publication = g_new0(struct sipe_publication, 1);
1630 					publication->category  = g_strdup(categoryName);
1631 					publication->instance  = atoi(instance);
1632 					publication->container = atoi(container);
1633 					publication->version   = atoi(curVersion);
1634 					category = g_hash_table_new_full(g_str_hash, g_str_equal,
1635 									 g_free, (GDestroyNotify)free_publication);
1636 					g_hash_table_insert(category, g_strdup(key), publication);
1637 					g_hash_table_insert(sipe_private->our_publications, g_strdup(categoryName), category);
1638 					SIPE_DEBUG_INFO("added lost category '%s' key '%s'", categoryName, key);
1639 				}
1640 				g_free(key);
1641 			}
1642 		}
1643 		sipe_xml_free(xml);
1644 		g_hash_table_destroy(faults);
1645 
1646 		/* rebublishing with right versions */
1647 		if (has_device_publication) {
1648 			send_publish_category_initial(sipe_private);
1649 		} else {
1650 			sipe_ocs2007_category_publish(sipe_private, TRUE);
1651 		}
1652 	}
1653 	return TRUE;
1654 }
1655 
1656 /**
1657  * Publishes categories.
1658  * @param uri		(%s) Self URI. Ex.: sip:alice7@boston.local
1659  * @param publications	(%s) XML publications
1660  */
1661 #define SIPE_SEND_PRESENCE \
1662 	"<publish xmlns=\"http://schemas.microsoft.com/2006/09/sip/rich-presence\">"\
1663 		"<publications uri=\"%s\">"\
1664 			"%s"\
1665 		"</publications>"\
1666 	"</publish>"
1667 
send_presence_publish(struct sipe_core_private * sipe_private,const char * publications)1668 static void send_presence_publish(struct sipe_core_private *sipe_private,
1669 				  const char *publications)
1670 {
1671 	gchar *uri;
1672 	gchar *doc;
1673 	gchar *tmp;
1674 	gchar *hdr;
1675 
1676 	uri = sip_uri_self(sipe_private);
1677 	doc = g_strdup_printf(SIPE_SEND_PRESENCE,
1678 		uri,
1679 		publications);
1680 
1681 	tmp = get_contact(sipe_private);
1682 	hdr = g_strdup_printf("Contact: %s\r\n"
1683 		"Content-Type: application/msrtc-category-publish+xml\r\n", tmp);
1684 
1685 	sip_transport_service(sipe_private,
1686 			      uri,
1687 			      hdr,
1688 			      doc,
1689 			      process_send_presence_category_publish_response);
1690 
1691 	g_free(tmp);
1692 	g_free(hdr);
1693 	g_free(uri);
1694 	g_free(doc);
1695 }
1696 
1697 /**
1698  * Publishes self status
1699  * based on own calendar information.
1700  */
sipe_ocs2007_presence_publish(struct sipe_core_private * sipe_private,SIPE_UNUSED_PARAMETER void * unused)1701 void sipe_ocs2007_presence_publish(struct sipe_core_private *sipe_private,
1702 				   SIPE_UNUSED_PARAMETER void *unused)
1703 {
1704 	struct sipe_calendar* cal = sipe_private->calendar;
1705 	struct sipe_cal_event* event = NULL;
1706 	gchar *pub_cal_working_hours = NULL;
1707 	gchar *pub_cal_free_busy = NULL;
1708 	gchar *pub_calendar = NULL;
1709 	gchar *pub_calendar2 = NULL;
1710 	gchar *pub_oof_note = NULL;
1711 	const gchar *oof_note;
1712 	time_t oof_start = 0;
1713 	time_t oof_end = 0;
1714 
1715 	if (!cal) {
1716 		SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self() no calendar data.");
1717 		return;
1718 	}
1719 
1720 	SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self() started.");
1721 	if (cal->cal_events) {
1722 		event = sipe_cal_get_event(cal->cal_events, time(NULL));
1723 	}
1724 
1725 	if (event) {
1726 		sipe_cal_event_debug(event, "publish_calendar_status_self: current event is:\n");
1727 	} else {
1728 		SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self: current event is NULL");
1729 	}
1730 
1731 	/* Logic
1732 	if OOF
1733 		OOF publish, Busy clean
1734 	ilse if Busy
1735 		OOF clean, Busy publish
1736 	else
1737 		OOF clean, Busy clean
1738 	*/
1739 	if (event && event->cal_status == SIPE_CAL_OOF) {
1740 		pub_calendar  = sipe_publish_get_category_state_calendar(sipe_private, event, cal->email, SIPE_CAL_OOF);
1741 		pub_calendar2 = sipe_publish_get_category_state_calendar(sipe_private, NULL,  cal->email, SIPE_CAL_BUSY);
1742 	} else if (event && event->cal_status == SIPE_CAL_BUSY) {
1743 		pub_calendar  = sipe_publish_get_category_state_calendar(sipe_private, NULL,  cal->email, SIPE_CAL_OOF);
1744 		pub_calendar2 = sipe_publish_get_category_state_calendar(sipe_private, event, cal->email, SIPE_CAL_BUSY);
1745 	} else {
1746 		pub_calendar  = sipe_publish_get_category_state_calendar(sipe_private, NULL,  cal->email, SIPE_CAL_OOF);
1747 		pub_calendar2 = sipe_publish_get_category_state_calendar(sipe_private, NULL,  cal->email, SIPE_CAL_BUSY);
1748 	}
1749 
1750 	oof_note = sipe_ews_get_oof_note(cal);
1751 	if (sipe_strequal("Scheduled", cal->oof_state)) {
1752 		oof_start = cal->oof_start;
1753 		oof_end = cal->oof_end;
1754 	}
1755 	pub_oof_note = sipe_publish_get_category_note(sipe_private, oof_note, "OOF", oof_start, oof_end, FALSE);
1756 
1757 	pub_cal_working_hours = sipe_publish_get_category_cal_working_hours(sipe_private);
1758 	pub_cal_free_busy = sipe_publish_get_category_cal_free_busy(sipe_private);
1759 
1760 	if (!pub_cal_working_hours && !pub_cal_free_busy && !pub_calendar && !pub_calendar2 && !pub_oof_note) {
1761 		SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self: nothing has changed.");
1762 	} else {
1763 		gchar *publications = g_strdup_printf("%s%s%s%s%s",
1764 				       pub_cal_working_hours ? pub_cal_working_hours : "",
1765 				       pub_cal_free_busy ? pub_cal_free_busy : "",
1766 				       pub_calendar ? pub_calendar : "",
1767 				       pub_calendar2 ? pub_calendar2 : "",
1768 				       pub_oof_note ? pub_oof_note : "");
1769 
1770 		send_presence_publish(sipe_private, publications);
1771 		g_free(publications);
1772 	}
1773 
1774 	g_free(pub_cal_working_hours);
1775 	g_free(pub_cal_free_busy);
1776 	g_free(pub_calendar);
1777 	g_free(pub_calendar2);
1778 	g_free(pub_oof_note);
1779 
1780 	/* repeat scheduling */
1781 	schedule_publish_update(sipe_private, time(NULL));
1782 }
1783 
sipe_ocs2007_category_publish(struct sipe_core_private * sipe_private,gboolean force_publish)1784 void sipe_ocs2007_category_publish(struct sipe_core_private *sipe_private,
1785 				   gboolean force_publish)
1786 {
1787 	GString *publications = g_string_new("");
1788 	gchar *tmp;
1789 
1790 	if (force_publish || sipe_private->status_set_by_user) {
1791 		tmp = sipe_publish_get_category_state_user(sipe_private,
1792 							   force_publish);
1793 		if (tmp) {
1794 			g_string_append(publications, tmp);
1795 			g_free(tmp);
1796 		}
1797 	}
1798 
1799 	tmp = sipe_publish_get_category_state_machine(sipe_private,
1800 						      force_publish);
1801 	if (tmp) {
1802 		g_string_append(publications, tmp);
1803 		g_free(tmp);
1804 	}
1805 
1806 	tmp = sipe_publish_get_category_note(sipe_private,
1807 					     sipe_private->note,
1808 					     SIPE_CORE_PRIVATE_FLAG_IS(OOF_NOTE) ? "OOF" : "personal",
1809 					     0,
1810 					     0,
1811 					     force_publish);
1812 	if (tmp) {
1813 		g_string_append(publications, tmp);
1814 		g_free(tmp);
1815 	}
1816 
1817 	if (publications->len)
1818 		send_presence_publish(sipe_private, publications->str);
1819 	else
1820 		SIPE_DEBUG_INFO_NOFORMAT("sipe_osc2007_category_publish: nothing has changed. Exiting.");
1821 
1822 	g_string_free(publications, TRUE);
1823 }
1824 
sipe_ocs2007_phone_state_publish(struct sipe_core_private * sipe_private)1825 void sipe_ocs2007_phone_state_publish(struct sipe_core_private *sipe_private)
1826 {
1827 	gchar *publications = NULL;
1828 	guint instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_PHONE_VOIP);
1829 
1830 	/* key is <category><instance><container> */
1831 	gchar *key_2 = g_strdup_printf("<%s><%u><%u>", "state", instance, 2);
1832 	gchar *key_3 = g_strdup_printf("<%s><%u><%u>", "state", instance, 3);
1833 	gpointer state = g_hash_table_lookup(sipe_private->our_publications, "state");
1834 	struct sipe_publication *publication_2 = state ? g_hash_table_lookup(state, key_2) : NULL;
1835 	struct sipe_publication *publication_3 = state ? g_hash_table_lookup(state, key_3) : NULL;
1836 	g_free(key_2);
1837 	g_free(key_3);
1838 
1839 #ifdef HAVE_VV
1840 	if (g_hash_table_size(sipe_private->media_calls)) {
1841 		guint availability_min = 0;
1842 		guint availability_max = 8999;
1843 		const gchar *token = NULL;
1844 		GList *calls = g_hash_table_get_values(sipe_private->media_calls);
1845 		GList *i;
1846 
1847 		if (sipe_core_media_get_call(SIPE_CORE_PUBLIC)) {
1848 			availability_min = 6500;
1849 			token = sipe_status_activity_to_token(SIPE_ACTIVITY_ON_PHONE);
1850 		}
1851 
1852 		for (i = calls; i; i = i->next) {
1853 			if (sipe_media_is_conference_call(i->data)) {
1854 				availability_min = 7000;
1855 				token = sipe_status_activity_to_token(SIPE_ACTIVITY_IN_CONF);
1856 			}
1857 
1858 			if (sipe_appshare_get_role(i->data) == SIPE_APPSHARE_ROLE_PRESENTER) {
1859 				availability_min = 9000;
1860 				availability_max = 11999;
1861 				token = sipe_status_activity_to_token(SIPE_ACTIVITY_IN_PRES);
1862 			}
1863 		}
1864 
1865 		g_list_free(calls);
1866 
1867 		if (token) {
1868 			publications = g_strdup_printf(SIPE_PUB_XML_STATE_PHONE,
1869 					instance, publication_2 ? publication_2->version : 0,
1870 					availability_min, token, availability_min, availability_max,
1871 					instance, publication_3 ? publication_3->version : 0,
1872 					availability_min, token, availability_min, availability_max);
1873 		}
1874 	} else
1875 #endif
1876 	{
1877 		publications = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_PHONE_CLEAR,
1878 				instance, publication_2 ? publication_2->version : 0,
1879 				instance, publication_3 ? publication_3->version : 0);
1880 	}
1881 
1882 	if (publications) {
1883 		send_presence_publish(sipe_private, publications);
1884 		g_free(publications);
1885 	}
1886 }
1887 
sipe_publish_get_cat_state_user_to_clear(SIPE_UNUSED_PARAMETER const char * name,gpointer value,GString * str)1888 static void sipe_publish_get_cat_state_user_to_clear(SIPE_UNUSED_PARAMETER const char *name,
1889 						     gpointer value,
1890 						     GString* str)
1891 {
1892 	struct sipe_publication *publication = value;
1893 
1894 	g_string_append_printf( str,
1895 				SIPE_PUB_XML_PUBLICATION_CLEAR,
1896 				publication->category,
1897 				publication->instance,
1898 				publication->container,
1899 				publication->version,
1900 				"static");
1901 }
1902 
sipe_ocs2007_reset_status(struct sipe_core_private * sipe_private)1903 void sipe_ocs2007_reset_status(struct sipe_core_private *sipe_private)
1904 {
1905 	GString* str;
1906 	gchar *publications;
1907 
1908 	if (!sipe_private->user_state_publications || g_hash_table_size(sipe_private->user_state_publications) == 0) {
1909 		SIPE_DEBUG_INFO_NOFORMAT("sipe_reset_status: no userState publications, exiting.");
1910 		return;
1911 	}
1912 
1913 	str = g_string_new(NULL);
1914 	g_hash_table_foreach(sipe_private->user_state_publications, (GHFunc)sipe_publish_get_cat_state_user_to_clear, str);
1915 	publications = g_string_free(str, FALSE);
1916 
1917 	send_presence_publish(sipe_private, publications);
1918 	g_free(publications);
1919 }
1920 
1921 /* key is <category><instance><container> */
sipe_is_our_publication(struct sipe_core_private * sipe_private,const gchar * key)1922 static gboolean sipe_is_our_publication(struct sipe_core_private *sipe_private,
1923 					const gchar *key)
1924 {
1925 	GSList *entry;
1926 
1927 	/* filling keys for our publications if not yet cached */
1928 	if (!sipe_private->our_publication_keys) {
1929 		guint device_instance	  = sipe_get_pub_instance(sipe_private, SIPE_PUB_DEVICE);
1930 		guint machine_instance	  = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_MACHINE);
1931 		guint user_instance	  = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_USER);
1932 		guint calendar_instance	  = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_CALENDAR);
1933 		guint cal_oof_instance	  = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_CALENDAR_OOF);
1934 		guint phone_voip_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_PHONE_VOIP);
1935 		guint cal_data_instance	  = sipe_get_pub_instance(sipe_private, SIPE_PUB_CALENDAR_DATA);
1936 		guint note_oof_instance	  = sipe_get_pub_instance(sipe_private, SIPE_PUB_NOTE_OOF);
1937 
1938 		SIPE_DEBUG_INFO_NOFORMAT("* Our Publication Instances *");
1939 		SIPE_DEBUG_INFO("\tDevice               : %u\t0x%08X", device_instance, device_instance);
1940 		SIPE_DEBUG_INFO("\tMachine State        : %u\t0x%08X", machine_instance, machine_instance);
1941 		SIPE_DEBUG_INFO("\tUser Stare           : %u\t0x%08X", user_instance, user_instance);
1942 		SIPE_DEBUG_INFO("\tCalendar State       : %u\t0x%08X", calendar_instance, calendar_instance);
1943 		SIPE_DEBUG_INFO("\tCalendar OOF State   : %u\t0x%08X", cal_oof_instance, cal_oof_instance);
1944 		SIPE_DEBUG_INFO("\tVOIP Phone State     : %u\t0x%08X", phone_voip_instance, phone_voip_instance);
1945 		SIPE_DEBUG_INFO("\tCalendar FreeBusy    : %u\t0x%08X", cal_data_instance, cal_data_instance);
1946 		SIPE_DEBUG_INFO("\tOOF Note             : %u\t0x%08X", note_oof_instance, note_oof_instance);
1947 		SIPE_DEBUG_INFO("\tNote                 : %u", 0);
1948 		SIPE_DEBUG_INFO("\tCalendar WorkingHours: %u", 0);
1949 
1950 		/* device */
1951 		sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1952 			g_strdup_printf("<%s><%u><%u>", "device", device_instance, 2));
1953 
1954 		/* state:machineState */
1955 		sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1956 			g_strdup_printf("<%s><%u><%u>", "state", machine_instance, 2));
1957 		sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1958 			g_strdup_printf("<%s><%u><%u>", "state", machine_instance, 3));
1959 
1960 		/* state:userState */
1961 		sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1962 			g_strdup_printf("<%s><%u><%u>", "state", user_instance, 2));
1963 		sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1964 			g_strdup_printf("<%s><%u><%u>", "state", user_instance, 3));
1965 
1966 		/* state:calendarState */
1967 		sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1968 			g_strdup_printf("<%s><%u><%u>", "state", calendar_instance, 2));
1969 		sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1970 			g_strdup_printf("<%s><%u><%u>", "state", calendar_instance, 3));
1971 
1972 		/* state:calendarState OOF */
1973 		sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1974 			g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance, 2));
1975 		sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1976 			g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance, 3));
1977 
1978 		/* state:phoneState */
1979 		sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1980 			g_strdup_printf("<%s><%u><%u>", "state", phone_voip_instance, 2));
1981 		sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1982 			g_strdup_printf("<%s><%u><%u>", "state", phone_voip_instance, 3));
1983 
1984 		/* note */
1985 		sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1986 			g_strdup_printf("<%s><%u><%u>", "note", 0, 200));
1987 		sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1988 			g_strdup_printf("<%s><%u><%u>", "note", 0, 300));
1989 		sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1990 			g_strdup_printf("<%s><%u><%u>", "note", 0, 400));
1991 
1992 		/* note OOF */
1993 		sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1994 			g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance, 200));
1995 		sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1996 			g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance, 300));
1997 		sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
1998 			g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance, 400));
1999 
2000 		/* calendarData:WorkingHours */
2001 		sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2002 			g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1));
2003 		sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2004 			g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100));
2005 		sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2006 			g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200));
2007 		sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2008 			g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300));
2009 		sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2010 			g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400));
2011 		sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2012 			g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000));
2013 
2014 		/* calendarData:FreeBusy */
2015 		sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2016 			g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 1));
2017 		sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2018 			g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 100));
2019 		sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2020 			g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 200));
2021 		sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2022 			g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 300));
2023 		sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2024 			g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 400));
2025 		sipe_private->our_publication_keys = g_slist_append(sipe_private->our_publication_keys,
2026 			g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 32000));
2027 
2028 		//SIPE_DEBUG_INFO("sipe_is_our_publication: sipe_private->our_publication_keys length=%d",
2029 		//	  sipe_private->our_publication_keys ? (int) g_slist_length(sipe_private->our_publication_keys) : -1);
2030 	}
2031 
2032 	//SIPE_DEBUG_INFO("sipe_is_our_publication: key=%s", key);
2033 
2034 	entry = sipe_private->our_publication_keys;
2035 	while (entry) {
2036 		//SIPE_DEBUG_INFO("   sipe_is_our_publication: entry->data=%s", entry->data);
2037 		if (sipe_strequal(entry->data, key)) {
2038 			return TRUE;
2039 		}
2040 		entry = entry->next;
2041 	}
2042 	return FALSE;
2043 }
2044 
sipe_refresh_blocked_status_cb(char * buddy_name,SIPE_UNUSED_PARAMETER struct sipe_buddy * buddy,struct sipe_core_private * sipe_private)2045 static void sipe_refresh_blocked_status_cb(char *buddy_name,
2046 					   SIPE_UNUSED_PARAMETER struct sipe_buddy *buddy,
2047 					   struct sipe_core_private *sipe_private)
2048 {
2049 	int container_id = sipe_ocs2007_find_access_level(sipe_private, "user", buddy_name, NULL);
2050 	gboolean blocked = (container_id == 32000);
2051 	gboolean blocked_in_blist = sipe_backend_buddy_is_blocked(SIPE_CORE_PUBLIC, buddy_name);
2052 
2053 	/* SIPE_DEBUG_INFO("sipe_refresh_blocked_status_cb: buddy_name=%s, blocked=%s, blocked_in_blist=%s",
2054 		buddy_name, blocked ? "T" : "F", blocked_in_blist ? "T" : "F"); */
2055 
2056 	if (blocked != blocked_in_blist) {
2057 		sipe_backend_buddy_set_blocked_status(SIPE_CORE_PUBLIC, buddy_name, blocked);
2058 	}
2059 }
2060 
sipe_refresh_blocked_status(struct sipe_core_private * sipe_private)2061 static void sipe_refresh_blocked_status(struct sipe_core_private *sipe_private)
2062 {
2063 	sipe_buddy_foreach(sipe_private,
2064 			   (GHFunc) sipe_refresh_blocked_status_cb,
2065 			   sipe_private);
2066 }
2067 
2068 /**
2069   *   When we receive some self (BE) NOTIFY with a new subscriber
2070   *   we sends a setSubscribers request to him [SIP-PRES] 4.8
2071   *
2072   */
sipe_ocs2007_process_roaming_self(struct sipe_core_private * sipe_private,struct sipmsg * msg)2073 void sipe_ocs2007_process_roaming_self(struct sipe_core_private *sipe_private,
2074 				       struct sipmsg *msg)
2075 {
2076 	gchar *contact;
2077 	gchar *to;
2078 	sipe_xml *xml;
2079 	const sipe_xml *node;
2080 	const sipe_xml *node2;
2081         char *display_name = NULL;
2082         char *uri;
2083 	GSList *category_names = NULL;
2084 	int aggreg_avail = 0;
2085 	gchar *activity_token = NULL;
2086 	gboolean do_update_status = FALSE;
2087 	gboolean has_note_cleaned = FALSE;
2088 	GHashTable *devices;
2089 
2090 	SIPE_DEBUG_INFO_NOFORMAT("sipe_ocs2007_process_roaming_self");
2091 
2092 	xml = sipe_xml_parse(msg->body, msg->bodylen);
2093 	if (!xml) return;
2094 
2095 	contact = get_contact(sipe_private);
2096 	to = sip_uri_self(sipe_private);
2097 
2098 	/* categories */
2099 	/* set list of categories participating in this XML */
2100 	for (node = sipe_xml_child(xml, "categories/category"); node; node = sipe_xml_twin(node)) {
2101 		const gchar *name = sipe_xml_attribute(node, "name");
2102 		category_names = sipe_utils_slist_insert_unique_sorted(category_names,
2103 								       (gchar *)name,
2104 								       (GCompareFunc)strcmp,
2105 								       NULL);
2106 	}
2107 	SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: category_names length=%d",
2108 			category_names ? (int) g_slist_length(category_names) : -1);
2109 	/* drop category information */
2110 	if (category_names) {
2111 		GSList *entry = category_names;
2112 		while (entry) {
2113 			GHashTable *cat_publications;
2114 			const gchar *category = entry->data;
2115 			entry = entry->next;
2116 			SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: dropping category: %s", category);
2117 			cat_publications = g_hash_table_lookup(sipe_private->our_publications, category);
2118 			if (cat_publications) {
2119 				g_hash_table_remove(sipe_private->our_publications, category);
2120 				SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: dropped category: %s", category);
2121 			}
2122 		}
2123 	}
2124 	g_slist_free(category_names);
2125 
2126 	/* filling our categories reflected in roaming data */
2127 	devices = g_hash_table_new_full(g_str_hash, g_str_equal,
2128 					g_free, NULL);
2129 	for (node = sipe_xml_child(xml, "categories/category"); node; node = sipe_xml_twin(node)) {
2130 		const char *tmp;
2131 		const gchar *name = sipe_xml_attribute(node, "name");
2132 		guint container = sipe_xml_int_attribute(node, "container", -1);
2133 		guint instance  = sipe_xml_int_attribute(node, "instance", -1);
2134 		guint version   = sipe_xml_int_attribute(node, "version", 0);
2135 		time_t publish_time = (tmp = sipe_xml_attribute(node, "publishTime")) ?
2136 			sipe_utils_str_to_time(tmp) : 0;
2137 		gchar *key;
2138 		GHashTable *cat_publications = g_hash_table_lookup(sipe_private->our_publications, name);
2139 
2140 		/* Ex. clear note: <category name="note"/> */
2141 		if (container == (guint)-1) {
2142 			g_free(sipe_private->note);
2143 			sipe_private->note = NULL;
2144 			do_update_status = TRUE;
2145 			continue;
2146 		}
2147 
2148 		/* Ex. clear note: <category name="note" container="200"/> */
2149 		if (instance == (guint)-1) {
2150 			if (container == 200) {
2151 				g_free(sipe_private->note);
2152 				sipe_private->note = NULL;
2153 				do_update_status = TRUE;
2154 			}
2155 			SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: removing publications for: %s/%u", name, container);
2156 			sipe_remove_category_container_publications(
2157 				sipe_private->our_publications, name, container);
2158 			continue;
2159 		}
2160 
2161 		/* key is <category><instance><container> */
2162 		key = g_strdup_printf("<%s><%u><%u>", name, instance, container);
2163 		SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: key=%s version=%d", key, version);
2164 
2165 		/* capture all userState publication for later clean up if required */
2166 		if (sipe_strequal(name, "state") && (container == 2 || container == 3)) {
2167 			const sipe_xml *xn_state = sipe_xml_child(node, "state");
2168 
2169 			if (xn_state && sipe_strequal(sipe_xml_attribute(xn_state, "type"), "userState")) {
2170 				struct sipe_publication *publication = g_new0(struct sipe_publication, 1);
2171 				publication->category  = g_strdup(name);
2172 				publication->instance  = instance;
2173 				publication->container = container;
2174 				publication->version   = version;
2175 
2176 				if (!sipe_private->user_state_publications) {
2177 					sipe_private->user_state_publications = g_hash_table_new_full(
2178 						g_str_hash, g_str_equal,
2179 						g_free,	(GDestroyNotify)free_publication);
2180 				}
2181 				g_hash_table_insert(sipe_private->user_state_publications, g_strdup(key), publication);
2182 				SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added to user_state_publications key=%s version=%d",
2183 						key, version);
2184 			}
2185 		}
2186 
2187 		/* count each client instance only once */
2188 		if (sipe_strequal(name, "device"))
2189 			g_hash_table_replace(devices, g_strdup_printf("%u", instance), NULL);
2190 
2191 		if (sipe_is_our_publication(sipe_private, key)) {
2192 			struct sipe_publication *publication = g_new0(struct sipe_publication, 1);
2193 
2194 			publication->category = g_strdup(name);
2195 			publication->instance  = instance;
2196 			publication->container = container;
2197 			publication->version   = version;
2198 
2199 			/* filling publication->availability */
2200 			if (sipe_strequal(name, "state")) {
2201 				const sipe_xml *xn_state = sipe_xml_child(node, "state");
2202 				const sipe_xml *xn_avail = sipe_xml_child(xn_state, "availability");
2203 
2204 				if (xn_avail) {
2205 					gchar *avail_str = sipe_xml_data(xn_avail);
2206 					if (avail_str) {
2207 						publication->availability = atoi(avail_str);
2208 					}
2209 					g_free(avail_str);
2210 				}
2211 				/* for calendarState */
2212 				if (xn_state && sipe_strequal(sipe_xml_attribute(xn_state, "type"), "calendarState")) {
2213 					const sipe_xml *xn_activity = sipe_xml_child(xn_state, "activity");
2214 					struct sipe_cal_event *event = g_new0(struct sipe_cal_event, 1);
2215 
2216 					event->start_time = sipe_utils_str_to_time(sipe_xml_attribute(xn_state, "startTime"));
2217 					if (xn_activity) {
2218 						if (sipe_strequal(sipe_xml_attribute(xn_activity, "token"),
2219 								  sipe_status_activity_to_token(SIPE_ACTIVITY_IN_MEETING)))
2220 						{
2221 							event->is_meeting = TRUE;
2222 						}
2223 					}
2224 					event->subject = sipe_xml_data(sipe_xml_child(xn_state, "meetingSubject"));
2225 					event->location = sipe_xml_data(sipe_xml_child(xn_state, "meetingLocation"));
2226 
2227 					publication->cal_event_hash = sipe_cal_event_hash(event);
2228 					SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: hash=%s",
2229 							publication->cal_event_hash);
2230 					sipe_cal_event_free(event);
2231 				}
2232 			}
2233 			/* filling publication->note */
2234 			if (sipe_strequal(name, "note")) {
2235 				const sipe_xml *xn_body = sipe_xml_child(node, "note/body");
2236 
2237 				if (!has_note_cleaned) {
2238 					has_note_cleaned = TRUE;
2239 
2240 					g_free(sipe_private->note);
2241 					sipe_private->note = NULL;
2242 					sipe_private->note_since = publish_time;
2243 
2244 					do_update_status = TRUE;
2245 				}
2246 
2247 				g_free(publication->note);
2248 				publication->note = NULL;
2249 				if (xn_body) {
2250 					char *tmp;
2251 
2252 					publication->note = g_markup_escape_text((tmp = sipe_xml_data(xn_body)), -1);
2253 					g_free(tmp);
2254 					if (publish_time >= sipe_private->note_since) {
2255 						g_free(sipe_private->note);
2256 						sipe_private->note = g_strdup(publication->note);
2257 						sipe_private->note_since = publish_time;
2258 						if (sipe_strequal(sipe_xml_attribute(xn_body, "type"), "OOF"))
2259 							SIPE_CORE_PRIVATE_FLAG_SET(OOF_NOTE);
2260 						else
2261 							SIPE_CORE_PRIVATE_FLAG_UNSET(OOF_NOTE);
2262 
2263 						do_update_status = TRUE;
2264 					}
2265 				}
2266 			}
2267 
2268 			/* filling publication->fb_start_str, free_busy_base64, working_hours_xml_str */
2269 			if (sipe_strequal(name, "calendarData") && (publication->container == 300)) {
2270 				const sipe_xml *xn_free_busy = sipe_xml_child(node, "calendarData/freeBusy");
2271 				const sipe_xml *xn_working_hours = sipe_xml_child(node, "calendarData/WorkingHours");
2272 				if (xn_free_busy) {
2273 					publication->fb_start_str = g_strdup(sipe_xml_attribute(xn_free_busy, "startTime"));
2274 					publication->free_busy_base64 = sipe_xml_data(xn_free_busy);
2275 				}
2276 				if (xn_working_hours) {
2277 					publication->working_hours_xml_str = sipe_xml_stringify(xn_working_hours);
2278 				}
2279 			}
2280 
2281 			if (!cat_publications) {
2282 				cat_publications = g_hash_table_new_full(
2283 							g_str_hash, g_str_equal,
2284 							g_free,	(GDestroyNotify)free_publication);
2285 				g_hash_table_insert(sipe_private->our_publications, g_strdup(name), cat_publications);
2286 				SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added GHashTable cat=%s", name);
2287 			}
2288 			g_hash_table_insert(cat_publications, g_strdup(key), publication);
2289 			SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added key=%s version=%d", key, version);
2290 		}
2291 		g_free(key);
2292 
2293 		/* aggregateState (not an our publication) from 2-nd container */
2294 		if (sipe_strequal(name, "state") && container == 2) {
2295 			const sipe_xml *xn_state = sipe_xml_child(node, "state");
2296 			const sipe_xml *xn_activity = sipe_xml_child(xn_state, "activity");
2297 
2298 			if (xn_state && sipe_strequal(sipe_xml_attribute(xn_state, "type"), "aggregateState")) {
2299 				const sipe_xml *xn_avail = sipe_xml_child(xn_state, "availability");
2300 
2301 				if (xn_avail) {
2302 					gchar *avail_str = sipe_xml_data(xn_avail);
2303 					if (avail_str) {
2304 						aggreg_avail = atoi(avail_str);
2305 					}
2306 					g_free(avail_str);
2307 				}
2308 
2309 				do_update_status = TRUE;
2310 			}
2311 
2312 			if (xn_activity) {
2313 				activity_token = g_strdup(sipe_xml_attribute(xn_activity, "token"));
2314 			}
2315 		}
2316 
2317 		/* userProperties published by server from AD */
2318 		if (!sipe_private->csta &&
2319 		    sipe_strequal(name, "userProperties")) {
2320 			const sipe_xml *line;
2321 			/* line, for Remote Call Control (RCC) or external Lync/Communicator call */
2322 			for (line = sipe_xml_child(node, "userProperties/lines/line"); line; line = sipe_xml_twin(line)) {
2323 				const gchar *line_type = sipe_xml_attribute(line, "lineType");
2324 				gchar *line_uri = sipe_xml_data(line);
2325 				if (!line_uri) {
2326 					continue;
2327 				}
2328 
2329 				if (sipe_strequal(line_type, "Rcc") || sipe_strequal(line_type, "Dual")) {
2330 					const gchar *line_server = sipe_xml_attribute(line, "lineServer");
2331 					if (line_server) {
2332 						gchar *tmp = g_strstrip(line_uri);
2333 						SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: line_uri=%s server=%s",
2334 								tmp, line_server);
2335 						sip_csta_open(sipe_private, tmp, line_server);
2336 					}
2337 				}
2338 #ifdef HAVE_VV
2339 				else if (sipe_strequal(line_type, "Uc")) {
2340 
2341 					if (!sipe_private->uc_line_uri) {
2342 						sipe_private->uc_line_uri = g_strdup(g_strstrip(line_uri));
2343 					} else {
2344 						SIPE_DEBUG_INFO_NOFORMAT("sipe_ocs2007_process_roaming_self: "
2345 								"sipe_private->uc_line_uri is already set.");
2346 					}
2347 				}
2348 #endif
2349 
2350 				g_free(line_uri);
2351 
2352 				break;
2353 			}
2354 		}
2355 	}
2356 	SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: sipe_private->our_publications size=%d",
2357 			sipe_private->our_publications ? (int) g_hash_table_size(sipe_private->our_publications) : -1);
2358 
2359 	/* active clients for user account */
2360 	if (g_hash_table_size(devices) == 0) {
2361 		/* updated roaming information without device information - no need to update MPOP flag */
2362 	} else if (g_hash_table_size(devices) > 1) {
2363 		SIPE_CORE_PRIVATE_FLAG_SET(MPOP);
2364 		SIPE_LOG_INFO("sipe_ocs2007_process_roaming_self: multiple clients detected (%d)",
2365 			      g_hash_table_size(devices));
2366 	} else {
2367 		SIPE_CORE_PRIVATE_FLAG_UNSET(MPOP);
2368 		SIPE_LOG_INFO_NOFORMAT("sipe_ocs2007_process_roaming_self: single client detected");
2369 	}
2370 	g_hash_table_destroy(devices);
2371 
2372 	/* containers */
2373 	for (node = sipe_xml_child(xml, "containers/container"); node; node = sipe_xml_twin(node)) {
2374 		guint id = sipe_xml_int_attribute(node, "id", 0);
2375 		struct sipe_container *container = sipe_find_container(sipe_private, id);
2376 
2377 		if (container) {
2378 			sipe_private->containers = g_slist_remove(sipe_private->containers, container);
2379 			SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: removed existing container id=%d v%d", container->id, container->version);
2380 			sipe_ocs2007_free_container(container);
2381 		}
2382 		container = g_new0(struct sipe_container, 1);
2383 		container->id = id;
2384 		container->version = sipe_xml_int_attribute(node, "version", 0);
2385 		sipe_private->containers = g_slist_append(sipe_private->containers, container);
2386 		SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added container id=%d v%d", container->id, container->version);
2387 
2388 		for (node2 = sipe_xml_child(node, "member"); node2; node2 = sipe_xml_twin(node2)) {
2389 			struct sipe_container_member *member = g_new0(struct sipe_container_member, 1);
2390 			member->type = g_strdup(sipe_xml_attribute(node2, "type"));
2391 			member->value = g_strdup(sipe_xml_attribute(node2, "value"));
2392 			container->members = g_slist_append(container->members, member);
2393 			SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: added container member type=%s value=%s",
2394 					member->type, member->value ? member->value : "");
2395 		}
2396 	}
2397 
2398 	SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: access_level_set=%s",
2399 			SIPE_CORE_PRIVATE_FLAG_IS(ACCESS_LEVEL_SET) ? "TRUE" : "FALSE");
2400 	if (!SIPE_CORE_PRIVATE_FLAG_IS(ACCESS_LEVEL_SET) && sipe_xml_child(xml, "containers")) {
2401 		char *container_xmls = NULL;
2402 		int sameEnterpriseAL = sipe_ocs2007_find_access_level(sipe_private, "sameEnterprise", NULL, NULL);
2403 		int federatedAL      = sipe_ocs2007_find_access_level(sipe_private, "federated", NULL, NULL);
2404 
2405 		SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: sameEnterpriseAL=%d", sameEnterpriseAL);
2406 		SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: federatedAL=%d", federatedAL);
2407 		/* initial set-up to let counterparties see your status */
2408 		if (sameEnterpriseAL < 0) {
2409 			struct sipe_container *container = sipe_find_container(sipe_private, 200);
2410 			guint version = container ? container->version : 0;
2411 			sipe_send_container_members_prepare(200, version, "add", "sameEnterprise", NULL, &container_xmls);
2412 		}
2413 		if (federatedAL < 0) {
2414 			struct sipe_container *container = sipe_find_container(sipe_private, 100);
2415 			guint version = container ? container->version : 0;
2416 			sipe_send_container_members_prepare(100, version, "add", "federated", NULL, &container_xmls);
2417 		}
2418 		SIPE_CORE_PRIVATE_FLAG_SET(ACCESS_LEVEL_SET);
2419 
2420 		if (container_xmls) {
2421 			sipe_send_set_container_members(sipe_private, container_xmls);
2422 		}
2423 		g_free(container_xmls);
2424 	}
2425 
2426 	/* Refresh contacts' blocked status */
2427 	sipe_refresh_blocked_status(sipe_private);
2428 
2429 	/* subscribers */
2430 	for (node = sipe_xml_child(xml, "subscribers/subscriber"); node; node = sipe_xml_twin(node)) {
2431 		const char *user;
2432 		const char *acknowledged;
2433 		gchar *hdr;
2434 		gchar *body;
2435 
2436 		user = sipe_xml_attribute(node, "user"); /* without 'sip:' prefix */
2437 		if (!user) continue;
2438 		SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: user %s", user);
2439 		display_name = g_strdup(sipe_xml_attribute(node, "displayName"));
2440 		uri = sip_uri_from_name(user);
2441 
2442 		sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, display_name);
2443 		sipe_backend_buddy_refresh_properties(SIPE_CORE_PUBLIC, uri);
2444 
2445 	        acknowledged= sipe_xml_attribute(node, "acknowledged");
2446 		if(sipe_strcase_equal(acknowledged,"false")){
2447                         SIPE_DEBUG_INFO("sipe_ocs2007_process_roaming_self: user added you %s", user);
2448 			if (!sipe_backend_buddy_find(SIPE_CORE_PUBLIC, uri, NULL)) {
2449 				sipe_backend_buddy_request_add(SIPE_CORE_PUBLIC, uri, display_name);
2450 			}
2451 
2452 		        hdr = g_strdup_printf(
2453 				      "Contact: %s\r\n"
2454 				      "Content-Type: application/msrtc-presence-setsubscriber+xml\r\n", contact);
2455 
2456 		        body = g_strdup_printf(
2457 				       "<setSubscribers xmlns=\"http://schemas.microsoft.com/2006/09/sip/presence-subscribers\">"
2458 				       "<subscriber user=\"%s\" acknowledged=\"true\"/>"
2459 				       "</setSubscribers>", user);
2460 
2461 		        sip_transport_service(sipe_private,
2462 					      to,
2463 					      hdr,
2464 					      body,
2465 					      NULL);
2466 		        g_free(body);
2467 		        g_free(hdr);
2468                 }
2469 		g_free(display_name);
2470 		g_free(uri);
2471 	}
2472 
2473 	g_free(contact);
2474 	sipe_xml_free(xml);
2475 
2476 	/* Publish initial state if not yet.
2477 	 * Assuming this happens on initial responce to subscription to roaming-self
2478 	 * so we've already updated our roaming data in full.
2479 	 * Only for 2007+
2480 	 */
2481 	if (!SIPE_CORE_PRIVATE_FLAG_IS(INITIAL_PUBLISH)) {
2482 		send_publish_category_initial(sipe_private);
2483 		SIPE_CORE_PRIVATE_FLAG_SET(INITIAL_PUBLISH);
2484 		/* dalayed run */
2485 		sipe_cal_delayed_calendar_update(sipe_private);
2486 		do_update_status = FALSE;
2487 	} else if (aggreg_avail) {
2488 
2489 		if (aggreg_avail &&
2490 		    (aggreg_avail < SIPE_OCS2007_LEGACY_AVAILIBILITY_OFFLINE)) {
2491 			/* not offline */
2492 			sipe_status_set_token(sipe_private,
2493 					      sipe_ocs2007_status_from_legacy_availability(aggreg_avail, activity_token));
2494 		} else {
2495 			/* do not let offline status switch us off */
2496 			sipe_status_set_activity(sipe_private,
2497 						 SIPE_ACTIVITY_INVISIBLE);
2498 		}
2499 	}
2500 
2501 	if (do_update_status) {
2502 		sipe_status_and_note(sipe_private, NULL);
2503 	}
2504 
2505 	g_free(to);
2506 	g_free(activity_token);
2507 }
2508 
2509 /**
2510  * for Access levels menu
2511  */
2512 #define INDENT_FMT			"  %s"
2513 
2514 /**
2515  * Member is indirectly belong to access level container.
2516  * For example 'sameEnterprise' is in the container and user
2517  * belongs to that same enterprise.
2518  */
2519 #define INDENT_MARKED_INHERITED_FMT	"= %s"
2520 
access_levels_menu(struct sipe_core_private * sipe_private,struct sipe_backend_buddy_menu * menu,const gchar * member_type,const gchar * member_value,const gboolean extra_menu)2521 static struct sipe_backend_buddy_menu *access_levels_menu(struct sipe_core_private *sipe_private,
2522 							  struct sipe_backend_buddy_menu *menu,
2523 							  const gchar *member_type,
2524 							  const gchar *member_value,
2525 							  const gboolean extra_menu)
2526 {
2527 	unsigned int i;
2528 	gboolean is_group_access = FALSE;
2529 	int container_id;
2530 
2531 	if (!menu)
2532 		menu = sipe_backend_buddy_menu_start(SIPE_CORE_PUBLIC);
2533 
2534 	container_id = sipe_ocs2007_find_access_level(sipe_private,
2535 						      member_type,
2536 						      member_value,
2537 						      &is_group_access);
2538 
2539 	for (i = 1; i <= CONTAINERS_LEN; i++) {
2540 		/*
2541 		 * Blocked should remain in the first place
2542 		 * in the containers[] array.
2543 		 */
2544 		unsigned int j  = (i == CONTAINERS_LEN) ? 0 : i;
2545 		int container_j = containers[j];
2546 		const gchar *acc_level_name = sipe_ocs2007_access_level_name(container_j);
2547 		struct sipe_container *container = create_container(j,
2548 								    member_type,
2549 								    member_value,
2550 								    FALSE);
2551 		gchar *label;
2552 
2553 		/* libpurple memory leak workaround */
2554 		blist_menu_remember_container(sipe_private, container);
2555 
2556 		/* current container/access level */
2557 		if (container_j == container_id) {
2558 			label = is_group_access ?
2559 				g_strdup_printf(INDENT_MARKED_INHERITED_FMT, acc_level_name) :
2560 				g_strdup_printf(SIPE_OCS2007_INDENT_MARKED_FMT, acc_level_name);
2561 		} else {
2562 			label = g_strdup_printf(INDENT_FMT, acc_level_name);
2563 		}
2564 
2565 		menu = sipe_backend_buddy_menu_add(SIPE_CORE_PUBLIC,
2566 						   menu,
2567 						   label,
2568 						   SIPE_BUDDY_MENU_CHANGE_ACCESS_LEVEL,
2569 						   container);
2570 		g_free(label);
2571 	}
2572 
2573 	if (extra_menu && (container_id >= 0) && !is_group_access) {
2574 		struct sipe_container *container = create_container(0,
2575 									    member_type,
2576 									    member_value,
2577 									    TRUE);
2578 		gchar *label;
2579 
2580 		/* separator */
2581 		menu = sipe_backend_buddy_menu_separator(SIPE_CORE_PUBLIC,
2582 							 menu,
2583 							 "  --------------");
2584 
2585 
2586 		/* libpurple memory leak workaround */
2587 		blist_menu_remember_container(sipe_private, container);
2588 
2589 		/* Translators: remove (clear) previously assigned access level */
2590 		label = g_strdup_printf(INDENT_FMT, _("Unspecify"));
2591 		menu = sipe_backend_buddy_menu_add(SIPE_CORE_PUBLIC,
2592 						   menu,
2593 						   label,
2594 						   SIPE_BUDDY_MENU_CHANGE_ACCESS_LEVEL,
2595 						   container);
2596 		g_free(label);
2597 	}
2598 
2599 	return(menu);
2600 }
2601 
access_groups_menu(struct sipe_core_private * sipe_private)2602 static struct sipe_backend_buddy_menu *access_groups_menu(struct sipe_core_private *sipe_private)
2603 {
2604 	struct sipe_backend_buddy_menu *menu = sipe_backend_buddy_menu_start(SIPE_CORE_PUBLIC);
2605 	GSList *access_domains, *entry;
2606 
2607 	menu = sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC,
2608 					       menu,
2609 					       _("People in my company"),
2610 					       access_levels_menu(sipe_private,
2611 								  NULL,
2612 								  "sameEnterprise",
2613 								  NULL,
2614 								  FALSE));
2615 
2616 	/* this is original name, don't edit */
2617 	menu = sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC,
2618 					       menu,
2619 					       _("People in domains connected with my company"),
2620 					       access_levels_menu(sipe_private,
2621 								  NULL,
2622 								  "federated",
2623 								  NULL,
2624 								  FALSE));
2625 
2626 	menu = sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC,
2627 					       menu,
2628 					       _("People in public domains"),
2629 					       access_levels_menu(sipe_private,
2630 								  NULL,
2631 								  "publicCloud",
2632 								  NULL,
2633 								  TRUE));
2634 
2635 	entry = access_domains = get_access_domains(sipe_private);
2636 	while (entry) {
2637 		gchar *domain    = entry->data;
2638 		gchar *menu_name = g_strdup_printf(_("People at %s"), domain);
2639 
2640 		/* takes over ownership of entry->data (= domain) */
2641 		menu = sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC,
2642 						       menu,
2643 						       menu_name,
2644 						       access_levels_menu(sipe_private,
2645 									  NULL,
2646 									  "domain",
2647 									  domain,
2648 									  TRUE));
2649 		g_free(menu_name);
2650 
2651 		entry = entry->next;
2652 	}
2653 	g_slist_free(access_domains);
2654 
2655 	/* separator */
2656 	/*			                  People in domains connected with my company */
2657 	menu = sipe_backend_buddy_menu_separator(SIPE_CORE_PUBLIC,
2658 						 menu,
2659 						 "-------------------------------------------");
2660 
2661 	menu = sipe_backend_buddy_menu_add(SIPE_CORE_PUBLIC,
2662 					   menu,
2663 					   _("Add new domain..."),
2664 					   SIPE_BUDDY_MENU_ADD_NEW_DOMAIN,
2665 					   NULL);
2666 
2667 	return(menu);
2668 }
2669 
sipe_ocs2007_access_control_menu(struct sipe_core_private * sipe_private,const gchar * buddy_name)2670 struct sipe_backend_buddy_menu *sipe_ocs2007_access_control_menu(struct sipe_core_private *sipe_private,
2671 								 const gchar *buddy_name)
2672 {
2673 	struct sipe_backend_buddy_menu *menu = sipe_backend_buddy_menu_start(SIPE_CORE_PUBLIC);
2674 	gchar *label;
2675 
2676 	/*
2677 	 * Workaround for missing libpurple API to release resources allocated
2678 	 * during blist_node_menu() callback. See also:
2679 	 *
2680 	 *   <http://developer.pidgin.im/ticket/12597>
2681 	 *
2682 	 * We remember all memory blocks in a list and deallocate them when
2683 	 *
2684 	 *   - the next time we enter the callback, or
2685 	 *   - the account is disconnected
2686 	 *
2687 	 * That means that after the buddy menu has been closed we have unused
2688 	 * resources but at least we don't leak them anymore...
2689 	 */
2690 	sipe_core_buddy_menu_free(SIPE_CORE_PUBLIC);
2691 
2692 	label = g_strdup_printf(INDENT_FMT, _("Online help..."));
2693 	menu = sipe_backend_buddy_menu_add(SIPE_CORE_PUBLIC,
2694 					   menu,
2695 					   label,
2696 					   SIPE_BUDDY_MENU_ACCESS_LEVEL_HELP,
2697 					   NULL);
2698 	g_free(label);
2699 
2700 	label = g_strdup_printf(INDENT_FMT, _("Access groups"));
2701 	menu = sipe_backend_buddy_sub_menu_add(SIPE_CORE_PUBLIC,
2702 					       menu,
2703 					       label,
2704 					       access_groups_menu(sipe_private));
2705 	g_free(label);
2706 
2707 	menu = access_levels_menu(sipe_private,
2708 				  menu,
2709 				  "user",
2710 				  sipe_get_no_sip_uri(buddy_name),
2711 				  TRUE);
2712 
2713 	return(menu);
2714 }
2715 
2716 /*
2717   Local Variables:
2718   mode: c
2719   c-file-style: "bsd"
2720   indent-tabs-mode: t
2721   tab-width: 8
2722   End:
2723 */
2724