friendlist.c 47.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/*
linphone
Copyright (C) 2010-2015 Belledonne Communications SARL

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
17
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 19
*/

20 21
#include <bctoolbox/crypto.h>

22
#include "linphone/api/c-content.h"
23
#include "linphone/core.h"
24

25
#include "c-wrapper/c-wrapper.h"
26

27 28 29
// TODO: From coreapi. Remove me later.
#include "private.h"

30 31 32 33 34 35 36 37 38
BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneFriendListCbs);

BELLE_SIP_INSTANCIATE_VPTR(LinphoneFriendListCbs, belle_sip_object_t,
	NULL, // destroy
	NULL, // clone
	NULL, // Marshall
	FALSE
);

39
LinphoneFriendListCbs * linphone_friend_list_cbs_new(void) {
40 41 42
	return belle_sip_object_new(LinphoneFriendListCbs);
}

43 44
LinphoneFriendListCbs * linphone_friend_list_get_callbacks(const LinphoneFriendList *friend_list) {
	return friend_list->cbs;
45 46
}

47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
void linphone_friend_list_add_callbacks(LinphoneFriendList *friend_list, LinphoneFriendListCbs *cbs) {
	friend_list->callbacks = bctbx_list_append(friend_list->callbacks, linphone_friend_list_cbs_ref(cbs));
}

void linphone_friend_list_remove_callbacks(LinphoneFriendList *friend_list, LinphoneFriendListCbs *cbs) {
	friend_list->callbacks = bctbx_list_remove(friend_list->callbacks, cbs);
	linphone_friend_list_cbs_unref(cbs);
}

LinphoneFriendListCbs *linphone_friend_list_get_current_callbacks(const LinphoneFriendList *friend_list) {
	return friend_list->currentCbs;
}

void linphone_friend_list_set_current_callbacks(LinphoneFriendList *friend_list, LinphoneFriendListCbs *cbs) {
	friend_list->currentCbs = cbs;
}

const bctbx_list_t *linphone_friend_list_get_callbacks_list(const LinphoneFriendList *friend_list) {
	return friend_list->callbacks;
}

#define NOTIFY_IF_EXIST(cbName, functionName, ...) \
	bctbx_list_t *callbacksCopy = bctbx_list_copy(linphone_friend_list_get_callbacks_list(list)); \
	for (bctbx_list_t *it = callbacksCopy; it; it = bctbx_list_next(it)) { \
		linphone_friend_list_set_current_callbacks(list, reinterpret_cast<LinphoneFriendListCbs *>(bctbx_list_get_data(it))); \
		LinphoneFriendListCbs ## cbName ## Cb cb = linphone_friend_list_cbs_get_ ## functionName (linphone_friend_list_get_current_callbacks(list)); \
		if (cb) \
			cb(__VA_ARGS__); \
	} \
	linphone_friend_list_set_current_callbacks(list, nullptr); \
	bctbx_list_free(callbacksCopy);

79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
LinphoneFriendListCbs * linphone_friend_list_cbs_ref(LinphoneFriendListCbs *cbs) {
	belle_sip_object_ref(cbs);
	return cbs;
}

void linphone_friend_list_cbs_unref(LinphoneFriendListCbs *cbs) {
	belle_sip_object_unref(cbs);
}

void *linphone_friend_list_cbs_get_user_data(const LinphoneFriendListCbs *cbs) {
	return cbs->user_data;
}

void linphone_friend_list_cbs_set_user_data(LinphoneFriendListCbs *cbs, void *ud) {
	cbs->user_data = ud;
}

96
LinphoneFriendListCbsContactCreatedCb linphone_friend_list_cbs_get_contact_created(const LinphoneFriendListCbs *cbs) {
97 98 99
	return cbs->contact_created_cb;
}

100
void linphone_friend_list_cbs_set_contact_created(LinphoneFriendListCbs *cbs, LinphoneFriendListCbsContactCreatedCb cb) {
101 102 103
	cbs->contact_created_cb = cb;
}

104
LinphoneFriendListCbsContactDeletedCb linphone_friend_list_cbs_get_contact_deleted(const LinphoneFriendListCbs *cbs) {
105 106 107
	return cbs->contact_deleted_cb;
}

108
void linphone_friend_list_cbs_set_contact_deleted(LinphoneFriendListCbs *cbs, LinphoneFriendListCbsContactDeletedCb cb) {
109 110
	cbs->contact_deleted_cb = cb;
}
111

112
LinphoneFriendListCbsContactUpdatedCb linphone_friend_list_cbs_get_contact_updated(const LinphoneFriendListCbs *cbs) {
113 114 115
	return cbs->contact_updated_cb;
}

116
void linphone_friend_list_cbs_set_contact_updated(LinphoneFriendListCbs *cbs, LinphoneFriendListCbsContactUpdatedCb cb) {
117 118 119
	cbs->contact_updated_cb = cb;
}

120
LinphoneFriendListCbsSyncStateChangedCb linphone_friend_list_cbs_get_sync_status_changed(const LinphoneFriendListCbs *cbs) {
121 122 123
	return cbs->sync_state_changed_cb;
}

124
void linphone_friend_list_cbs_set_sync_status_changed(LinphoneFriendListCbs *cbs, LinphoneFriendListCbsSyncStateChangedCb cb) {
125 126 127
	cbs->sync_state_changed_cb = cb;
}

128 129 130 131 132 133 134 135
LinphoneFriendListCbsPresenceReceivedCb linphone_friend_list_cbs_get_presence_received(const LinphoneFriendListCbs *cbs) {
	return cbs->presence_received_cb;
}

void linphone_friend_list_cbs_set_presence_received(LinphoneFriendListCbs *cbs, LinphoneFriendListCbsPresenceReceivedCb cb) {
	cbs->presence_received_cb = cb;
}

136 137 138 139 140 141 142 143 144 145 146 147 148 149
static int add_uri_entry(xmlTextWriterPtr writer, int err, const char *uri) {
	if (err >= 0) {
		err = xmlTextWriterStartElement(writer, (const xmlChar *)"entry");
	}
	if (err >= 0) {
		err = xmlTextWriterWriteAttribute(writer, (const xmlChar *)"uri", (const xmlChar *)uri);
	}
	if (err >= 0) {
		/* Close the "entry" element. */
		err = xmlTextWriterEndElement(writer);
	}
	return err;
}

Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
150 151 152 153 154 155
static bctbx_list_t * uri_list(const LinphoneFriendList *list) {
	bctbx_list_t * uri_list = NULL;
	bctbx_list_t * elem = NULL;
	for (elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) {
		LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(elem);
		bctbx_list_t *iterator;
156
		const bctbx_list_t *addresses = linphone_friend_get_addresses(lf);
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
157
		bctbx_list_t *numbers = linphone_friend_get_phone_numbers(lf);
158
		iterator = (bctbx_list_t *)addresses;
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
159 160 161 162 163
		while (iterator) {
			LinphoneAddress *addr = (LinphoneAddress *)bctbx_list_get_data(iterator);
			if (addr) {
				char *uri = linphone_address_as_string_uri_only(addr);
				if (uri) {
164
					uri_list = bctbx_list_prepend(uri_list, uri);
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
165 166 167 168 169 170 171 172 173
				}
			}
			iterator = bctbx_list_next(iterator);
		}
		iterator = numbers;
		while (iterator) {
			const char *number = (const char *)bctbx_list_get_data(iterator);
			const char *uri = linphone_friend_phone_number_to_sip_uri(lf, number);
			if (uri) {
174
				uri_list = bctbx_list_prepend(uri_list, ms_strdup(uri));
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
175 176 177 178 179 180 181
			}
			iterator = bctbx_list_next(iterator);
		}
	}
	return uri_list;
}

182 183 184 185 186 187
static char * create_resource_list_xml(const LinphoneFriendList *list) {
	char *xml_content = NULL;
	xmlBufferPtr buf;
	xmlTextWriterPtr writer;
	int err;

188
	if (list->friends == NULL) return NULL;
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213

	buf = xmlBufferCreate();
	if (buf == NULL) {
		ms_error("%s: Error creating the XML buffer", __FUNCTION__);
		return NULL;
	}
	writer = xmlNewTextWriterMemory(buf, 0);
	if (writer == NULL) {
		ms_error("%s: Error creating the XML writer", __FUNCTION__);
		return NULL;
	}

	xmlTextWriterSetIndent(writer,1);
	err = xmlTextWriterStartDocument(writer, "1.0", "UTF-8", NULL);
	if (err >= 0) {
		err = xmlTextWriterStartElementNS(writer, NULL, (const xmlChar *)"resource-lists", (const xmlChar *)"urn:ietf:params:xml:ns:resource-lists");
	}
	if (err >= 0) {
		err = xmlTextWriterWriteAttributeNS(writer, (const xmlChar *)"xmlns", (const xmlChar *)"xsi",
						    NULL, (const xmlChar *)"http://www.w3.org/2001/XMLSchema-instance");
	}

	if (err>= 0) {
		err = xmlTextWriterStartElement(writer, (const xmlChar *)"list");
	}
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
214 215 216

	{
		bctbx_list_t* entries = uri_list(list);
217 218
		bctbx_list_t* it;
		for(it = entries; it != NULL; it = it->next){
219
			err = add_uri_entry(writer, err, reinterpret_cast<const char *>(it->data));
220
		}
221
		bctbx_list_free_with_data(entries, ms_free);
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
	}
	if (err >= 0) {
		/* Close the "list" element. */
		err = xmlTextWriterEndElement(writer);
	}

	if (err >= 0) {
		/* Close the "resource-lists" element. */
		err = xmlTextWriterEndElement(writer);
	}
	if (err >= 0) {
		err = xmlTextWriterEndDocument(writer);
	}
	if (err > 0) {
		/* xmlTextWriterEndDocument returns the size of the content. */
		xml_content = ms_strdup((char *)buf->content);
	}
	xmlFreeTextWriter(writer);
	xmlBufferFree(buf);

	return xml_content;
}

245 246 247 248
static void linphone_friend_list_parse_multipart_related_body(LinphoneFriendList *list, const LinphoneContent *body, const char *first_part_body) {
	xmlparsing_context_t *xml_ctx = linphone_xmlparsing_context_new();
	xmlSetGenericErrorFunc(xml_ctx, linphone_xmlparsing_genericxml_error);
	xml_ctx->doc = xmlReadDoc((const unsigned char*)first_part_body, 0, NULL, 0);
Benjamin REIS's avatar
Benjamin REIS committed
249
	if (xml_ctx->doc) {
250
		LinphoneFriend *lf;
251 252
		LinphoneContent *presence_part;
		xmlXPathObjectPtr resource_object;
253
		xmlXPathObjectPtr name_object;
Simon Morlat's avatar
Simon Morlat committed
254 255 256
		char *version_str = NULL;
		char *full_state_str = NULL;
		char *uri = NULL;
257 258
		bool_t full_state = FALSE;
		int version;
259
		int i;
260 261
		bctbx_list_t *list_friends_presence_received = NULL;
		LinphoneFriendListCbs *list_cbs = linphone_friend_list_get_callbacks(list);
262 263 264

		if (linphone_create_xml_xpath_context(xml_ctx) < 0) goto end;
		xmlXPathRegisterNs(xml_ctx->xpath_ctx, (const xmlChar *)"rlmi", (const xmlChar *)"urn:ietf:params:xml:ns:rlmi");
265 266

		version_str = linphone_get_xml_attribute_text_content(xml_ctx, "/rlmi:list", "version");
Benjamin REIS's avatar
Benjamin REIS committed
267
		if (!version_str) {
268 269 270 271
			ms_warning("rlmi+xml: No version attribute in list");
			goto end;
		}
		version = atoi(version_str);
Ghislain MARY's avatar
Ghislain MARY committed
272
		linphone_free_xml_text_content(version_str);
273 274
		if (version < list->expected_notification_version) { /*no longuer an error as dialog may be silently restarting by the refresher*/
			ms_warning("rlmi+xml: Received notification with version %d expected was %d, dialog may have been reseted", version, list->expected_notification_version);
275 276 277
		}

		full_state_str = linphone_get_xml_attribute_text_content(xml_ctx, "/rlmi:list", "fullState");
Benjamin REIS's avatar
Benjamin REIS committed
278
		if (!full_state_str) {
279 280 281 282
			ms_warning("rlmi+xml: No fullState attribute in list");
			goto end;
		}
		if ((strcmp(full_state_str, "true") == 0) || (strcmp(full_state_str, "1") == 0)) {
283
			bctbx_list_t *l = list->friends;
284 285
			for (; l != NULL; l = bctbx_list_next(l)) {
				lf = (LinphoneFriend *)bctbx_list_get_data(l);
286
				linphone_friend_clear_presence_models(lf);
287 288 289 290
			}
			full_state = TRUE;
		}
		linphone_free_xml_text_content(full_state_str);
Benjamin REIS's avatar
Benjamin REIS committed
291
		if ((list->expected_notification_version == 0) && !full_state) {
292 293 294 295 296
			ms_warning("rlmi+xml: Notification with version 0 is not full state, this is not valid");
			goto end;
		}
		list->expected_notification_version = version + 1;

297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
		name_object = linphone_get_xml_xpath_object_for_node_list(xml_ctx, "/rlmi:list/rlmi:resource/rlmi:name/..");
		if (name_object && name_object->nodesetval) {
			for (i = 1; i <= name_object->nodesetval->nodeNr; i++) {
				char *name = NULL;
				LinphoneAddress* addr;
				linphone_xml_xpath_context_set_node(xml_ctx, xmlXPathNodeSetItem(name_object->nodesetval, i-1));
				name = linphone_get_xml_text_content(xml_ctx, "./rlmi:name");
				uri = linphone_get_xml_text_content(xml_ctx, "./@uri");
				if (!uri)
					continue;
				addr = linphone_address_new(uri);
				if (!addr)
					continue;
				lf = linphone_friend_list_find_friend_by_address(list, addr);
				linphone_address_unref(addr);
				if (!lf && list->bodyless_subscription) {
					lf = linphone_core_create_friend_with_address(list->lc, uri);
					linphone_friend_list_add_friend(list, lf);
					linphone_friend_unref(lf);
				}
				if (name) {
					linphone_friend_set_name(lf, name);
					linphone_free_xml_text_content(name);
				}
			}
		}
		if (name_object)
			xmlXPathFreeObject(name_object);

326
		resource_object = linphone_get_xml_xpath_object_for_node_list(xml_ctx, "/rlmi:list/rlmi:resource/rlmi:instance[@state=\"active\"]/..");
Benjamin REIS's avatar
Benjamin REIS committed
327
		if (resource_object && resource_object->nodesetval) {
328
			for (i = 1; i <= resource_object->nodesetval->nodeNr; i++) {
Simon Morlat's avatar
Simon Morlat committed
329
				char *cid = NULL;
330 331
				linphone_xml_xpath_context_set_node(xml_ctx, xmlXPathNodeSetItem(resource_object->nodesetval, i-1));
				cid = linphone_get_xml_text_content(xml_ctx, "./rlmi:instance/@cid");
Benjamin REIS's avatar
Benjamin REIS committed
332
				if (cid) {
333
					presence_part = linphone_content_find_part_by_header(body, "Content-Id", cid);
Benjamin REIS's avatar
Benjamin REIS committed
334
					if (!presence_part) {
335 336 337 338
						ms_warning("rlmi+xml: Cannot find part with Content-Id: %s", cid);
					} else {
						SalPresenceModel *presence = NULL;
						linphone_notify_parse_presence(linphone_content_get_type(presence_part), linphone_content_get_subtype(presence_part), linphone_content_get_string_buffer(presence_part), &presence);
Benjamin REIS's avatar
Benjamin REIS committed
339
						if (presence) {
340 341
							// Try to reduce CPU cost of linphone_address_new and find_friend_by_address by only doing it when we know for sure we have a presence to notify
							LinphoneAddress* addr;
342
							uri = linphone_get_xml_text_content(xml_ctx, "./@uri");
Benjamin REIS's avatar
Benjamin REIS committed
343 344
							if (!uri)
								continue;
345
							addr = linphone_address_new(uri);
Benjamin REIS's avatar
Benjamin REIS committed
346 347
							if (!addr)
								continue;
348 349
							lf = linphone_friend_list_find_friend_by_address(list, addr);
							linphone_address_unref(addr);
350 351 352 353

							if (!lf && list->bodyless_subscription) {
								lf = linphone_core_create_friend_with_address(list->lc, uri);
								linphone_friend_list_add_friend(list, lf);
Benjamin REIS's avatar
Benjamin REIS committed
354
								linphone_friend_unref(lf);
355
							}
356 357
							if (lf) {
								lf->presence_received = TRUE;
358
								const char *phone_number = linphone_friend_sip_uri_to_phone_number(lf, uri);
359 360 361
								if (phone_number) {
									char *presence_address = linphone_presence_model_get_contact((LinphonePresenceModel *)presence);
									bctbx_pair_t *pair = (bctbx_pair_t*) bctbx_pair_cchar_new(presence_address, linphone_friend_ref(lf));
362 363
									bctbx_iterator_t * it = bctbx_map_cchar_find_key(list->friends_map_uri, presence_address);
									if (!bctbx_iterator_cchar_equals(it, bctbx_map_cchar_end(list->friends_map_uri))){
364
										linphone_friend_unref((LinphoneFriend*)bctbx_pair_cchar_get_second(bctbx_iterator_cchar_get_pair(it)));
365
										bctbx_map_cchar_erase(list->friends_map_uri, it);
366
									}
367
									bctbx_map_cchar_insert_and_delete(list->friends_map_uri, pair);
368 369 370 371
									linphone_friend_set_presence_model_for_uri_or_tel(lf, phone_number, (LinphonePresenceModel *)presence);
								} else {
									linphone_friend_set_presence_model_for_uri_or_tel(lf, uri, (LinphonePresenceModel *)presence);
								}
Benjamin REIS's avatar
Benjamin REIS committed
372
								if (!full_state) {
373 374 375 376
									if (phone_number)
										linphone_core_notify_notify_presence_received_for_uri_or_tel(list->lc, lf, phone_number, (LinphonePresenceModel *)presence);
									else
										linphone_core_notify_notify_presence_received_for_uri_or_tel(list->lc, lf, uri, (LinphonePresenceModel *)presence);
377
									// Deprecated
378
									linphone_core_notify_notify_presence_received(list->lc, lf);
379
									list_friends_presence_received = bctbx_list_prepend(list_friends_presence_received, lf);
380
								}
381
								linphone_free_xml_text_content(uri);
382
							}
383
							linphone_content_unref(presence_part);
384 385
						}
					}
386
					linphone_free_xml_text_content(cid);
387 388
				}
			}
389
			// Notify list with all friends for which we received presence information
390 391 392 393 394 395
			if (bctbx_list_size(list_friends_presence_received) > 0) {
				if (list_cbs && linphone_friend_list_cbs_get_presence_received(list_cbs)) {
					linphone_friend_list_cbs_get_presence_received(list_cbs)(list, list_friends_presence_received);
				}

				NOTIFY_IF_EXIST(PresenceReceived, presence_received, list, list_friends_presence_received)
396 397
			}
			bctbx_list_free(list_friends_presence_received);
398
		}
Benjamin REIS's avatar
Benjamin REIS committed
399 400
		if (resource_object)
			xmlXPathFreeObject(resource_object);
401

Benjamin REIS's avatar
Benjamin REIS committed
402
		if (full_state) {
403
			const bctbx_list_t *addresses;
404 405
			bctbx_list_t *numbers;
			bctbx_list_t *iterator;
406
			bctbx_list_t *l = list->friends;
407 408
			for (; l != NULL; l = bctbx_list_next(l)) {
				lf = (LinphoneFriend *)bctbx_list_get_data(l);
409 410
				addresses = linphone_friend_get_addresses(lf);
				numbers = linphone_friend_get_phone_numbers(lf);
411
				iterator = (bctbx_list_t *)addresses;
412 413 414 415
				while (iterator) {
					LinphoneAddress *addr = (LinphoneAddress *)bctbx_list_get_data(iterator);
					char *uri = linphone_address_as_string_uri_only(addr);
					const LinphonePresenceModel *presence = linphone_friend_get_presence_model_for_uri_or_tel(lf, uri);
416
					if (presence) linphone_core_notify_notify_presence_received_for_uri_or_tel(list->lc, lf, uri, presence);
417 418 419 420 421 422 423
					ms_free(uri);
					iterator = bctbx_list_next(iterator);
				}
				iterator = numbers;
				while (iterator) {
					const char *number = (const char *)bctbx_list_get_data(iterator);
					const LinphonePresenceModel *presence = linphone_friend_get_presence_model_for_uri_or_tel(lf, number);
424 425 426
					if (presence) {
						linphone_core_notify_notify_presence_received_for_uri_or_tel(list->lc, lf, number, presence);
					}
427 428
					iterator = bctbx_list_next(iterator);
				}
Ghislain MARY's avatar
Ghislain MARY committed
429
				if (numbers) bctbx_list_free(numbers);
Benjamin REIS's avatar
Benjamin REIS committed
430
				if (linphone_friend_is_presence_received(lf)) {
431
					// Deprecated
432
					linphone_core_notify_notify_presence_received(list->lc, lf);
433
					list_friends_presence_received = bctbx_list_prepend(list_friends_presence_received, lf);
434 435
				}
			}
436
			// Notify list with all friends for which we received presence information
437 438 439 440 441 442
			if (bctbx_list_size(list_friends_presence_received) > 0) {
				if (list_cbs && linphone_friend_list_cbs_get_presence_received(list_cbs)) {
					linphone_friend_list_cbs_get_presence_received(list_cbs)(list, list_friends_presence_received);
				}
				
				NOTIFY_IF_EXIST(PresenceReceived, presence_received, list, list_friends_presence_received)
443 444
			}
			bctbx_list_free(list_friends_presence_received);
445
		}
446 447 448 449 450 451 452 453
	} else {
		ms_warning("Wrongly formatted rlmi+xml body: %s", xml_ctx->errorBuffer);
	}

end:
	linphone_xmlparsing_context_destroy(xml_ctx);
}

454
static bool_t linphone_friend_list_has_subscribe_inactive(const LinphoneFriendList *list) {
455 456 457
	if (list->bodyless_subscription)
		return TRUE;

458
	bctbx_list_t *l = list->friends;
459
	bool_t has_subscribe_inactive = FALSE;
460 461
	for (; l != NULL; l = bctbx_list_next(l)) {
		LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(l);
462
		if (lf->subscribe_active != TRUE) {
463 464 465 466 467 468 469
			has_subscribe_inactive = TRUE;
			break;
		}
	}
	return has_subscribe_inactive;
}

470 471
static LinphoneFriendList * linphone_friend_list_new(void) {
	LinphoneFriendList *list = belle_sip_object_new(LinphoneFriendList);
472
	list->cbs = linphone_friend_list_cbs_new();
473
	list->enable_subscriptions = TRUE;
474
	list->friends_map = bctbx_mmap_cchar_new();
475
	list->friends_map_uri = bctbx_mmap_cchar_new();
476
	list->bodyless_subscription = FALSE;
477 478 479
	return list;
}

480 481
static void linphone_friend_list_destroy(LinphoneFriendList *list) {
	if (list->display_name != NULL) ms_free(list->display_name);
482
	if (list->rls_addr) linphone_address_unref(list->rls_addr);
483
	if (list->rls_uri != NULL) ms_free(list->rls_uri);
484
	if (list->content_digest != NULL) ms_free(list->content_digest);
485 486 487
	if (list->event != NULL) {
		linphone_event_terminate(list->event);
		linphone_event_unref(list->event);
488
		list->event = NULL;
489
	}
490 491
	if (list->uri != NULL) ms_free(list->uri);
	if (list->cbs) linphone_friend_list_cbs_unref(list->cbs);
492 493
	bctbx_list_free_with_data(list->callbacks, (bctbx_list_free_func)linphone_friend_list_cbs_unref);
	list->callbacks = nullptr;
494 495
	if (list->dirty_friends_to_update) list->dirty_friends_to_update = bctbx_list_free_with_data(list->dirty_friends_to_update, (void (*)(void *))linphone_friend_unref);
	if (list->friends) list->friends = bctbx_list_free_with_data(list->friends, (void (*)(void *))_linphone_friend_release);
Benjamin REIS's avatar
Benjamin REIS committed
496
	if (list->friends_map) bctbx_mmap_cchar_delete_with_data(list->friends_map, (void (*)(void *))linphone_friend_unref);
497
	if (list->friends_map_uri) bctbx_mmap_cchar_delete_with_data(list->friends_map_uri, (void (*)(void *))linphone_friend_unref);
498 499 500 501 502 503 504 505
}

BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneFriendList);

BELLE_SIP_INSTANCIATE_VPTR(LinphoneFriendList, belle_sip_object_t,
	(belle_sip_object_destroy_t)linphone_friend_list_destroy,
	NULL, // clone
	NULL, // marshal
506
	FALSE
507 508 509
);


510 511 512
LinphoneFriendList * linphone_core_create_friend_list(LinphoneCore *lc) {
	LinphoneFriendList *list = linphone_friend_list_new();
	list->lc = lc;
513 514 515 516 517 518 519 520
	return list;
}

LinphoneFriendList * linphone_friend_list_ref(LinphoneFriendList *list) {
	belle_sip_object_ref(list);
	return list;
}

521 522 523
void _linphone_friend_list_release(LinphoneFriendList *list){
	/*drops all references to core and unref*/
	list->lc = NULL;
Simon Morlat's avatar
Simon Morlat committed
524 525 526 527
	if (list->event != NULL) {
		linphone_event_unref(list->event);
		list->event = NULL;
	}
528 529 530 531
	if (list->cbs) {
		linphone_friend_list_cbs_unref(list->cbs);
		list->cbs = NULL;
	}
532
	if (list->dirty_friends_to_update) {
533
		list->dirty_friends_to_update = bctbx_list_free_with_data(list->dirty_friends_to_update, (void (*)(void *))linphone_friend_unref);
534 535
	}
	if (list->friends) {
536
		list->friends = bctbx_list_free_with_data(list->friends, (void (*)(void *))_linphone_friend_release);
537
	}
538
	linphone_friend_list_unref(list);
539 540
}

541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563
void linphone_friend_list_unref(LinphoneFriendList *list) {
	belle_sip_object_unref(list);
}

void * linphone_friend_list_get_user_data(const LinphoneFriendList *list) {
	return list->user_data;
}

void linphone_friend_list_set_user_data(LinphoneFriendList *list, void *ud) {
	list->user_data = ud;
}

const char * linphone_friend_list_get_display_name(const LinphoneFriendList *list) {
	return list->display_name;
}

void linphone_friend_list_set_display_name(LinphoneFriendList *list, const char *display_name) {
	if (list->display_name != NULL) {
		ms_free(list->display_name);
		list->display_name = NULL;
	}
	if (display_name != NULL) {
		list->display_name = ms_strdup(display_name);
564
		linphone_core_store_friends_list_in_db(list->lc, list);
565 566 567
	}
}

568 569
const LinphoneAddress * linphone_friend_list_get_rls_address(const LinphoneFriendList *list){
	return list->rls_addr;
570
}
571 572 573
const LinphoneAddress * _linphone_friend_list_get_rls_address(const LinphoneFriendList *list) {
	if (list->rls_addr)
		return list->rls_addr;
574 575 576 577
	else if (list->lc) {
		const char* rls_uri = lp_config_get_string(list->lc->config, "sip", "rls_uri", NULL);
		if (list->lc->default_rls_addr)
			linphone_address_unref(list->lc->default_rls_addr);
578

579
		list->lc->default_rls_addr=NULL;
580

581 582 583 584
		if (rls_uri) {
			/*to make sure changes in config are used if any*/
			list->lc->default_rls_addr = linphone_address_new(rls_uri);
		}
585

586
		return list->lc->default_rls_addr;
587
	}
588 589 590
	else
		return NULL;
}
591 592
void linphone_friend_list_set_rls_address(LinphoneFriendList *list, const LinphoneAddress *rls_addr){
	LinphoneAddress *new_rls_addr = rls_addr ? linphone_address_clone(rls_addr) : NULL;
593

594 595 596 597 598
	if (list->rls_addr){
		linphone_address_unref(list->rls_addr);
	}
	list->rls_addr = new_rls_addr;
	if (list->rls_uri != NULL){
599 600 601
		ms_free(list->rls_uri);
		list->rls_uri = NULL;
	}
602 603
	if (list->rls_addr){
		list->rls_uri = linphone_address_as_string(list->rls_addr);
604
		linphone_core_store_friends_list_in_db(list->lc, list);
605 606 607
	}
}

608 609 610 611 612 613 614
const char * linphone_friend_list_get_rls_uri(const LinphoneFriendList *list) {
	return list->rls_uri;
}

void linphone_friend_list_set_rls_uri(LinphoneFriendList *list, const char *rls_uri) {
	LinphoneAddress *addr = rls_uri ? linphone_core_create_address(list->lc, rls_uri) : NULL;
	linphone_friend_list_set_rls_address(list, addr);
Simon Morlat's avatar
Simon Morlat committed
615
	if (addr) linphone_address_unref(addr);
616 617
}

618
static LinphoneFriendListStatus _linphone_friend_list_add_friend(LinphoneFriendList *list, LinphoneFriend *lf, bool_t synchronize) {
619
	LinphoneFriendListStatus status = LinphoneFriendListInvalidFriend;
620
	const LinphoneAddress *addr;
621

622
	if (!list || lf->friend_list) {
Sylvain Berfini's avatar
Sylvain Berfini committed
623 624
		if (!list)
			ms_error("linphone_friend_list_add_friend(): invalid list, null");
625
		if (lf->friend_list)
626
			ms_error("linphone_friend_list_add_friend(): invalid friend, already in list");
627
		return status;
Simon Morlat's avatar
Simon Morlat committed
628
	}
629
	addr = linphone_friend_get_address(lf);
Benjamin REIS's avatar
Benjamin REIS committed
630
	bool_t present = FALSE;
Benjamin REIS's avatar
Benjamin REIS committed
631
	if (lf->refkey) {
Benjamin REIS's avatar
Benjamin REIS committed
632
		present = linphone_friend_list_find_friend_by_ref_key(list, lf->refkey) != NULL;
Benjamin REIS's avatar
Benjamin REIS committed
633
	} else {
Benjamin REIS's avatar
Benjamin REIS committed
634
		present = bctbx_list_find(list->friends, lf) != NULL;
Benjamin REIS's avatar
Benjamin REIS committed
635
	}
Benjamin REIS's avatar
Benjamin REIS committed
636
	if (present) {
637 638 639
		char *tmp = NULL;
		if (addr) tmp = linphone_address_as_string(addr);
		ms_warning("Friend %s already in list [%s], ignored.", tmp ? tmp : "unknown", list->display_name);
Ghislain MARY's avatar
Ghislain MARY committed
640
		if (tmp) ms_free(tmp);
641
	} else {
642
		status = linphone_friend_list_import_friend(list, lf, synchronize);
643
		linphone_friend_save(lf, lf->lc);
644
	}
645

Benjamin REIS's avatar
Benjamin REIS committed
646
	if (!list->rls_uri) // Mimic the behaviour of linphone_core_add_friend() when a resource list server is not in use
647
		linphone_friend_apply(lf, lf->lc);
Benjamin REIS's avatar
Benjamin REIS committed
648

649
	return status;
650 651
}

652 653 654 655 656 657 658 659
LinphoneFriendListStatus linphone_friend_list_add_friend(LinphoneFriendList *list, LinphoneFriend *lf) {
	return _linphone_friend_list_add_friend(list, lf, TRUE);
}

LinphoneFriendListStatus linphone_friend_list_add_local_friend(LinphoneFriendList *list, LinphoneFriend *lf) {
	return _linphone_friend_list_add_friend(list, lf, FALSE);
}

660
LinphoneFriendListStatus linphone_friend_list_import_friend(LinphoneFriendList *list, LinphoneFriend *lf, bool_t synchronize) {
661 662 663
	bctbx_list_t *iterator;
	bctbx_list_t *phone_numbers;
	const bctbx_list_t *addresses;
664
	if (lf->friend_list) {
665 666
		if (lf->friend_list)
			ms_error("linphone_friend_list_add_friend(): invalid friend, already in list");
Sylvain Berfini's avatar
Sylvain Berfini committed
667 668
		return LinphoneFriendListInvalidFriend;
	}
669
	lf->friend_list = list;
670
	lf->lc = list->lc;
Benjamin REIS's avatar
Benjamin REIS committed
671
	list->friends = bctbx_list_prepend(list->friends, linphone_friend_ref(lf));
672 673 674 675
	if (lf->refkey) {
		bctbx_pair_t *pair = (bctbx_pair_t*) bctbx_pair_cchar_new(lf->refkey, linphone_friend_ref(lf));
		bctbx_map_cchar_insert_and_delete(list->friends_map, pair);
	}
676 677 678 679 680 681 682 683 684 685 686 687

	phone_numbers = linphone_friend_get_phone_numbers(lf);
	iterator = phone_numbers;
	while (iterator) {
		const char *number = (const char *)bctbx_list_get_data(iterator);
		const char *uri = linphone_friend_phone_number_to_sip_uri(lf, number);
		if(uri) {
			bctbx_pair_t *pair = (bctbx_pair_t*) bctbx_pair_cchar_new(uri, linphone_friend_ref(lf));
			bctbx_map_cchar_insert_and_delete(list->friends_map_uri, pair);
		}
		iterator = bctbx_list_next(iterator);
	}
688
	bctbx_list_free(phone_numbers);
689 690 691 692 693 694 695 696 697 698 699 700 701 702

	addresses = linphone_friend_get_addresses(lf);
	iterator = (bctbx_list_t *)addresses;
	while (iterator) {
		LinphoneAddress *lfaddr = (LinphoneAddress *)bctbx_list_get_data(iterator);
		char *uri = linphone_address_as_string_uri_only(lfaddr);
		if(uri) {
			bctbx_pair_t *pair = (bctbx_pair_t*) bctbx_pair_cchar_new(uri, linphone_friend_ref(lf));
			bctbx_map_cchar_insert_and_delete(list->friends_map_uri, pair);
			ms_free(uri);
		}
		iterator = bctbx_list_next(iterator);
	}

703
	if (synchronize) {
Benjamin REIS's avatar
Benjamin REIS committed
704
		list->dirty_friends_to_update = bctbx_list_prepend(list->dirty_friends_to_update, linphone_friend_ref(lf));
705
	}
706 707 708
	return LinphoneFriendListOK;
}

709
static void carddav_done(LinphoneCardDavContext *cdc, bool_t success, const char *msg) {
710
	LinphoneFriendList *list = cdc->friend_list;
711
	if (cdc && cdc->friend_list->cbs && cdc->friend_list->cbs->sync_state_changed_cb) {
712 713
		cdc->friend_list->cbs->sync_state_changed_cb(cdc->friend_list, success ? LinphoneFriendListSyncSuccessful : LinphoneFriendListSyncFailure, msg);
	}
714
	NOTIFY_IF_EXIST(SyncStateChanged, sync_status_changed, list, success ? LinphoneFriendListSyncSuccessful : LinphoneFriendListSyncFailure, msg)
715 716 717
	linphone_carddav_context_destroy(cdc);
}

718
static LinphoneFriendListStatus _linphone_friend_list_remove_friend(LinphoneFriendList *list, LinphoneFriend *lf, bool_t remove_from_server) {
719 720 721
	bctbx_list_t *iterator;
	bctbx_list_t *phone_numbers;
	const bctbx_list_t *addresses;
722
	bctbx_list_t *elem = bctbx_list_find(list->friends, lf);
723
	if (elem == NULL) return LinphoneFriendListNonExistentFriend;
724

725 726 727
	if (lf && lf->lc && lf->lc->friends_db) {
		linphone_core_remove_friend_from_db(lf->lc, lf);
	}
728

729
	if (remove_from_server) {
730
		LinphoneVcard *lvc = linphone_friend_get_vcard(lf);
731 732
		if (lvc && linphone_vcard_get_uid(lvc)) {
			LinphoneCardDavContext *cdc = linphone_carddav_context_new(list);
733 734 735 736 737
			if (cdc) {
				cdc->sync_done_cb = carddav_done;
				if (cdc->friend_list->cbs->sync_state_changed_cb) {
					cdc->friend_list->cbs->sync_state_changed_cb(cdc->friend_list, LinphoneFriendListSyncStarted, NULL);
				}
738
				NOTIFY_IF_EXIST(SyncStateChanged, sync_status_changed, list, LinphoneFriendListSyncStarted, NULL)
739
				linphone_carddav_delete_vcard(cdc, lf);
740 741
			}
		}
742
	}
743
	list->friends = bctbx_list_erase_link(list->friends, elem);
744 745
	if(lf->refkey) {
		bctbx_iterator_t * it = bctbx_map_cchar_find_key(list->friends_map, lf->refkey);
746 747
		bctbx_iterator_t * end = bctbx_map_cchar_end(list->friends_map);
		if (!bctbx_iterator_cchar_equals(it, end)){
748 749 750
			linphone_friend_unref((LinphoneFriend*)bctbx_pair_cchar_get_second(bctbx_iterator_cchar_get_pair(it)));
			bctbx_map_cchar_erase(list->friends_map, it);
		}
751 752
		if (it) bctbx_iterator_cchar_delete(it);
		if (end) bctbx_iterator_cchar_delete(end);
753
	}
754

755 756 757 758 759 760 761
	phone_numbers = linphone_friend_get_phone_numbers(lf);
	iterator = phone_numbers;
	while (iterator) {
		const char *number = (const char *)bctbx_list_get_data(iterator);
		const char *uri = linphone_friend_phone_number_to_sip_uri(lf, number);
		if(uri) {
			bctbx_iterator_t * it = bctbx_map_cchar_find_key(list->friends_map_uri, uri);
762 763
			bctbx_iterator_t * end = bctbx_map_cchar_end(list->friends_map_uri);
			if (!bctbx_iterator_cchar_equals(it, end)){
764 765 766
				linphone_friend_unref((LinphoneFriend*)bctbx_pair_cchar_get_second(bctbx_iterator_cchar_get_pair(it)));
				bctbx_map_cchar_erase(list->friends_map_uri, it);
			}
767 768
			if (it) bctbx_iterator_cchar_delete(it);
			if (end) bctbx_iterator_cchar_delete(end);
769 770 771
		}
		iterator = bctbx_list_next(iterator);
	}
772
	if (phone_numbers) bctbx_list_free(phone_numbers);
773 774 775 776 777 778 779 780

	addresses = linphone_friend_get_addresses(lf);
	iterator = (bctbx_list_t *)addresses;
	while (iterator) {
		LinphoneAddress *lfaddr = (LinphoneAddress *)bctbx_list_get_data(iterator);
		char *uri = linphone_address_as_string_uri_only(lfaddr);
		if(uri) {
			bctbx_iterator_t * it = bctbx_map_cchar_find_key(list->friends_map_uri, uri);
781 782
			bctbx_iterator_t * end = bctbx_map_cchar_end(list->friends_map_uri);
			if (!bctbx_iterator_cchar_equals(it, end)){
783 784 785
				linphone_friend_unref((LinphoneFriend*)bctbx_pair_cchar_get_second(bctbx_iterator_cchar_get_pair(it)));
				bctbx_map_cchar_erase(list->friends_map_uri, it);
			}
786 787
			if (it) bctbx_iterator_cchar_delete(it);
			if (end) bctbx_iterator_cchar_delete(end);
788 789
			ms_free(uri);
		}
790

791 792 793
		iterator = bctbx_list_next(iterator);
	}

794 795
	lf->friend_list = NULL;
	linphone_friend_unref(lf);
796
	return LinphoneFriendListOK;
797
}
798

799 800 801 802
LinphoneFriendListStatus linphone_friend_list_remove_friend(LinphoneFriendList *list, LinphoneFriend *lf) {
	return _linphone_friend_list_remove_friend(list, lf, TRUE);
}

803
const bctbx_list_t * linphone_friend_list_get_friends(const LinphoneFriendList *list) {
804 805 806
	return list->friends;
}

807
void linphone_friend_list_update_dirty_friends(LinphoneFriendList *list) {
808
	bctbx_list_t *dirty_friends = list->dirty_friends_to_update;
809

810 811 812
	while (dirty_friends) {
		LinphoneCardDavContext *cdc = linphone_carddav_context_new(list);
		if (cdc) {
813
			LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(dirty_friends);
814
			cdc->sync_done_cb = carddav_done;
815
			if (lf) {
816 817 818
				if (cdc->friend_list->cbs->sync_state_changed_cb) {
					cdc->friend_list->cbs->sync_state_changed_cb(cdc->friend_list, LinphoneFriendListSyncStarted, NULL);
				}
819
				NOTIFY_IF_EXIST(SyncStateChanged, sync_status_changed, list, LinphoneFriendListSyncStarted, NULL)
820 821 822
				linphone_carddav_put_vcard(cdc, lf);
			}
		}
823
		dirty_friends = bctbx_list_next(dirty_friends);
824
	}
825
	list->dirty_friends_to_update = bctbx_list_free_with_data(list->dirty_friends_to_update, (void (*)(void *))linphone_friend_unref);
826 827 828
}

static void carddav_created(LinphoneCardDavContext *cdc, LinphoneFriend *lf) {
829
	if (cdc) {
830 831
		LinphoneFriendList *list = cdc->friend_list;
		linphone_friend_list_import_friend(list, lf, FALSE);
832
		if (cdc->friend_list->cbs->contact_created_cb) {
833
			cdc->friend_list->cbs->contact_created_cb(list, lf);
834
		}
835
		NOTIFY_IF_EXIST(ContactCreated, contact_created, list, lf)
836 837 838 839
	}
}

static void carddav_removed(LinphoneCardDavContext *cdc, LinphoneFriend *lf) {
840
	if (cdc) {
841 842
		LinphoneFriendList *list = cdc->friend_list;
		_linphone_friend_list_remove_friend(list, lf, FALSE);
843
		if (cdc->friend_list->cbs->contact_deleted_cb) {
844
			cdc->friend_list->cbs->contact_deleted_cb(list, lf);
845
		}
846
		NOTIFY_IF_EXIST(ContactDeleted, contact_deleted, list, lf)
847 848 849
	}
}

850
static void carddav_updated(LinphoneCardDavContext *cdc, LinphoneFriend *lf_new, LinphoneFriend *lf_old) {
851
	if (cdc) {
852 853
		LinphoneFriendList *list = cdc->friend_list;
		bctbx_list_t *elem = bctbx_list_find(list->friends, lf_old);
854
		if (elem) {
855
			elem->data = linphone_friend_ref(lf_new);
856
		}
857
		linphone_core_store_friend_in_db(lf_new->lc, lf_new);
858

859
		if (cdc->friend_list->cbs->contact_updated_cb) {
860
			cdc->friend_list->cbs->contact_updated_cb(list, lf_new, lf_old);
861
		}
862
		NOTIFY_IF_EXIST(ContactUpdated, contact_updated, list, lf_new, lf_old)
863
		linphone_friend_unref(lf_old);
864
	}
865 866
}

867
void linphone_friend_list_synchronize_friends_from_server(LinphoneFriendList *list) {
868
	LinphoneCardDavContext *cdc = NULL;
869

870 871 872 873
	if (!list || !list->uri || !list->lc) {
		ms_error("FATAL");
		return;
	}
874

875
	cdc = linphone_carddav_context_new(list);
876 877 878
	if (cdc) {
		cdc->contact_created_cb = carddav_created;
		cdc->contact_removed_cb = carddav_removed;
879
		cdc->contact_updated_cb = carddav_updated;
880
		cdc->sync_done_cb = carddav_done;
881 882 883
		if (cdc && cdc->friend_list->cbs->sync_state_changed_cb) {
			cdc->friend_list->cbs->sync_state_changed_cb(cdc->friend_list, LinphoneFriendListSyncStarted, NULL);
		}
884
		NOTIFY_IF_EXIST(SyncStateChanged, sync_status_changed, list, LinphoneFriendListSyncStarted, NULL)
885 886 887 888
		linphone_carddav_synchronize(cdc);
	}
}

889
LinphoneFriend * linphone_friend_list_find_friend_by_address(const LinphoneFriendList *list, const LinphoneAddress *address) {
890
	LinphoneAddress *clean_addr = linphone_address_clone(address);
891
	LinphoneFriend *lf;
892
	if (linphone_address_has_uri_param(clean_addr, "gr")) {
893 894
		linphone_address_remove_uri_param(clean_addr, "gr");
	}
895 896 897
	char *uri = linphone_address_as_string_uri_only(clean_addr);
	lf = linphone_friend_list_find_friend_by_uri(list, uri);
	bctbx_free(uri);
898 899
	linphone_address_unref(clean_addr);
	return lf;
900 901 902
}

LinphoneFriend * linphone_friend_list_find_friend_by_uri(const LinphoneFriendList *list, const char *uri) {
903
	LinphoneFriend *result = NULL;
904 905 906
	bctbx_iterator_t *it = bctbx_map_cchar_find_key(list->friends_map_uri, uri);
	bctbx_iterator_t *end = bctbx_map_cchar_end(list->friends_map_uri);
	if (!bctbx_iterator_cchar_equals(it, end)) {
907 908
		bctbx_pair_t *pair = bctbx_iterator_cchar_get_pair(it);
		result = (LinphoneFriend *)bctbx_pair_cchar_get_second(pair);
909
	}
910
	bctbx_iterator_cchar_delete(end);
911 912
	bctbx_iterator_cchar_delete(it);
	return result;
913 914
}

915 916 917 918 919 920
LinphoneFriend *linphone_friend_list_find_friend_by_ref_key (const LinphoneFriendList *list, const char *ref_key) {
	LinphoneFriend *result = NULL;
	if (list) {
		bctbx_iterator_t *it = bctbx_map_cchar_find_key(list->friends_map, ref_key);
		bctbx_iterator_t *end = bctbx_map_cchar_end(list->friends_map);
		if (!bctbx_iterator_cchar_equals(it, end)) {
921
			bctbx_pair_t *pair = bctbx_iterator_cchar_get_pair(it);
922
			result = (LinphoneFriend *)bctbx_pair_cchar_get_second(pair);
923
		}
924 925
		bctbx_iterator_cchar_delete(end);
		bctbx_iterator_cchar_delete(it);
926
	}
927
	return result;
928 929
}

930 931 932 933
LinphoneFriend * linphone_friend_list_find_friend_by_inc_subscribe (
	const LinphoneFriendList *list,
	LinphonePrivate::SalOp *op
) {
934
	const bctbx_list_t *elem;
935 936
	for (elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) {
		LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(elem);
937
		if (bctbx_list_find(lf->insubs, op)) return lf;
938 939 940 941
	}
	return NULL;
}

942 943 944 945
LinphoneFriend * linphone_friend_list_find_friend_by_out_subscribe (
	const LinphoneFriendList *list,
	LinphonePrivate::SalOp *op
) {
946
	const bctbx_list_t *elem;
947 948
	for (elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) {
		LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(elem);
949
		if (lf->outsub && ((lf->outsub == op) || lf->outsub->isForkedOf(op))) return lf;
950 951 952 953
	}
	return NULL;
}

954 955
static void linphone_friend_list_close_subscriptions(LinphoneFriendList *list) {
	/* FIXME we should wait until subscription to complete. */
jehan's avatar
jehan committed
956 957
	if (list->event) {
		linphone_event_terminate(list->event);
958 959
		linphone_event_unref(list->event);
		list->event = NULL;
960
	}
961
	bctbx_list_for_each(list->friends, (void (*)(void *))linphone_friend_close_subscriptions);
962 963
}

964
static void _linphone_friend_list_send_list_subscription_with_body(LinphoneFriendList *list, const LinphoneAddress *address) {
965
	char *xml_content = create_resource_list_xml(list);
966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001
	if (!xml_content)
		return;

	unsigned char digest[16];
	bctbx_md5((unsigned char *)xml_content, strlen(xml_content), digest);
	if (list->event && list->content_digest && (memcmp(list->content_digest, digest, sizeof(digest)) == 0)) {
		/* The content has not changed, only refresh the event. */
		linphone_event_refresh_subscribe(list->event);
	} else {
		LinphoneContent *content;
		bctbx_list_t * elem = NULL;
		int expires = lp_config_get_int(list->lc->config, "sip", "rls_presence_expires", 3600);
		list->expected_notification_version = 0;
		if (list->content_digest)
			ms_free(list->content_digest);

		list->content_digest = reinterpret_cast<unsigned char *>(ms_malloc(sizeof(digest)));
		memcpy(list->content_digest, digest, sizeof(digest));
		if (list->event) {
			linphone_event_terminate(list->event);
			linphone_event_unref(list->event);
		}
		list->event = linphone_core_create_subscribe(list->lc, address, "presence", expires);
		linphone_event_ref(list->event);
		linphone_event_set_internal(list->event, TRUE);
		linphone_event_add_custom_header(list->event, "Require", "recipient-list-subscribe");
		linphone_event_add_custom_header(list->event, "Supported", "eventlist");
		linphone_event_add_custom_header(list->event, "Accept", "multipart/related, application/pidf+xml, application/rlmi+xml");
		linphone_event_add_custom_header(list->event, "Content-Disposition", "recipient-list");
		content = linphone_core_create_content(list->lc);
		linphone_content_set_type(content, "application");
		linphone_content_set_subtype(content, "resource-lists+xml");
		linphone_content_set_string_buffer(content, xml_content);
		if (linphone_core_content_encoding_supported(list->lc, "deflate")) {
			linphone_content_set_encoding(content, "deflate");
			linphone_event_add_custom_header(list->event, "Accept-Encoding", "deflate");
1002
		}
1003 1004 1005 1006 1007 1008 1009
		for (elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) {
			LinphoneFriend *lf = (LinphoneFriend *)elem->data;
			lf->subscribe_active = TRUE;
		}
		linphone_event_send_subscribe(list->event, content);
		linphone_content_unref(content);
		linphone_event_set_user_data(list->event, list);
1010
	}
1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037
	ms_free(xml_content);
}

static void _linphone_friend_list_send_list_subscription_without_body(LinphoneFriendList *list, const LinphoneAddress *address) {
	bctbx_list_t *elem = NULL;
	int expires = lp_config_get_int(list->lc->config, "sip", "rls_presence_expires", 3600);
	list->expected_notification_version = 0;
	if (list->content_digest)
		ms_free(list->content_digest);

	if (list->event) {
		linphone_event_terminate(list->event);
		linphone_event_unref(list->event);
	}
	list->event = linphone_core_create_subscribe(list->lc, address, "presence", expires);
	linphone_event_ref(list->event);
	linphone_event_set_internal(list->event, TRUE);
	linphone_event_add_custom_header(list->event, "Supported", "eventlist");
	linphone_event_add_custom_header(list->event, "Accept", "multipart/related, application/pidf+xml, application/rlmi+xml");
	if (linphone_core_content_encoding_supported(list->lc, "deflate"))
		linphone_event_add_custom_header(list->event, "Accept-Encoding", "deflate");

	for (elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) {
		LinphoneFriend *lf = (LinphoneFriend *)elem->data;
		lf->subscribe_active = TRUE;
	}
	linphone_event_send_subscribe(list->event, NULL);
1038
	linphone_event_set_user_data(list->event, list);
1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052
}

static void linphone_friend_list_send_list_subscription(LinphoneFriendList *list) {
	const LinphoneAddress *address = _linphone_friend_list_get_rls_address(list);
	if (!address)
		return;

	if (!linphone_friend_list_has_subscribe_inactive(list))
		return;

	if (list->bodyless_subscription)
		_linphone_friend_list_send_list_subscription_without_body(list, address);
	else
		_linphone_friend_list_send_list_subscription_with_body(list, address);
1053 1054
}

1055
void linphone_friend_list_update_subscriptions(LinphoneFriendList *list) {
1056
	LinphoneProxyConfig *cfg = NULL;
1057
	const LinphoneAddress *address = _linphone_friend_list_get_rls_address(list);
1058 1059
	bool_t only_when_registered = FALSE;
	bool_t should_send_list_subscribe = FALSE;
1060

1061 1062 1063 1064 1065 1066
	if (list->lc){
		if (address)
			cfg = linphone_core_lookup_known_proxy(list->lc, address);
		only_when_registered = linphone_core_should_subscribe_friends_only_when_registered(list->lc);
		should_send_list_subscribe = (!only_when_registered || !cfg || cfg->state == LinphoneRegistrationOk);
	}
1067

1068
	if (address != NULL) {
1069
		if (list->enable_subscriptions) {
1070 1071 1072 1073 1074 1075 1076 1077 1078 1079
			if (should_send_list_subscribe){
				linphone_friend_list_send_list_subscription(list);
			}else{
				if (list->event){
					linphone_event_terminate(list->event);
					linphone_event_unref(list->event);
					list->event = NULL;
					ms_message("Friends list [%p] subscription terminated because proxy config lost connection", list);
				}else{
					ms_message("Friends list [%p] subscription update skipped since dependant proxy config is not yet registered", list);
1080
				}
1081
			}
1082 1083
		} else {
			ms_message("Friends list [%p] subscription update skipped since subscriptions not enabled yet", list);
1084
		}
1085
	} else if (list->enable_subscriptions) {
1086
		const bctbx_list_t *elem;
1087 1088
		for (elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) {
			LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(elem);
1089
			linphone_friend_update_subscribes(lf, only_when_registered);
1090
		}
1091 1092 1093 1094
	}
}

void linphone_friend_list_invalidate_subscriptions(LinphoneFriendList *list) {
1095
	const bctbx_list_t *elem;
1096 1097 1098 1099 1100 1101 1102 1103

	// Terminate subscription event
	if (list->event) {
		linphone_event_terminate(list->event);
		linphone_event_unref(list->event);
		list->event = NULL;
	}

1104 1105
	for (elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) {
		LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(elem);
1106
		linphone_friend_invalidate_subscription(lf);
1107 1108 1109 1110
	}
}

void linphone_friend_list_notify_presence(LinphoneFriendList *list, LinphonePresenceModel *presence) {
1111
	const bctbx_list_t *elem;
1112 1113
	for(elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) {
		LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(elem);
1114
		linphone_friend_notify(lf, presence);
1115 1116
	}
}
1117 1118

void linphone_friend_list_notify_presence_received(LinphoneFriendList *list, LinphoneEvent *lev, const LinphoneContent *body) {
1119 1120
	if (!linphone_content_is_multipart(body))
		return;
1121

1122 1123 1124
	LinphoneContent *first_part;
	const char *type = linphone_content_get_type(body);
	const char *subtype = linphone_content_get_subtype(body);
1125

1126 1127 1128 1129
	if ((strcmp(type, "multipart") != 0) || (strcmp(subtype, "related") != 0)) {
		ms_warning("multipart presence notified but it is not 'multipart/related', instead is '%s/%s'", type, subtype);
		return;
	}
1130

1131 1132 1133 1134 1135 1136 1137 1138 1139 1140
	first_part = linphone_content_get_part(body, 0);
	if (first_part == NULL) {
		ms_warning("'multipart/related' presence notified but it doesn't contain any part");
		return;
	}

	type = linphone_content_get_type(first_part);
	subtype = linphone_content_get_subtype(first_part);
	if ((strcmp(type, "application") != 0) || (strcmp(subtype, "rlmi+xml") != 0)) {
		ms_warning("multipart presence notified but first part is not 'application/rlmi+xml'");
Ghislain MARY's avatar
Ghislain MARY committed
1141
		linphone_content_unref(first_part);
1142
		return;
1143
	}
1144 1145 1146

	linphone_friend_list_parse_multipart_related_body(list, body, linphone_content_get_string_buffer(first_part));
	linphone_content_unref(first_part);
1147
}
1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159

const char * linphone_friend_list_get_uri(const LinphoneFriendList *list) {
	return list->uri;
}

void linphone_friend_list_set_uri(LinphoneFriendList *list, const char *uri) {
	if (list->uri != NULL) {
		ms_free(list->uri);
		list->uri = NULL;
	}
	if (uri != NULL) {
		list->uri = ms_strdup(uri);
1160
		linphone_core_store_friends_list_in_db(list->lc, list);
1161 1162 1163
	}
}

1164 1165 1166 1167 1168 1169 1170 1171
bool_t linphone_friend_list_is_subscription_bodyless(LinphoneFriendList *list) {
	return list->bodyless_subscription;
}

void linphone_friend_list_set_subscription_bodyless(LinphoneFriendList *list, bool_t bodyless) {
	list->bodyless_subscription = bodyless;
}

1172 1173
void linphone_friend_list_update_revision(LinphoneFriendList *list, int rev) {
	list->revision = rev;
1174
	linphone_core_store_friends_list_in_db(list->lc, list);
1175 1176
}

1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187
void linphone_friend_list_subscription_state_changed(LinphoneCore *lc, LinphoneEvent *lev, LinphoneSubscriptionState state) {
	LinphoneFriendList *list = (LinphoneFriendList *)linphone_event_get_user_data(lev);
	if (!list) {
		ms_warning("core [%p] Receiving unexpected state [%s] for event [%p], no associated friend list",lc
					, linphone_subscription_state_to_string(state)
				   , lev);
	} else {
		ms_message("Receiving new state [%s] for event [%p] for friend list [%p]"
				   , linphone_subscription_state_to_string(state)
				   , lev
				   , list);
1188

1189
		if (state == LinphoneSubscriptionOutgoingProgress && linphone_event_get_reason(lev) == LinphoneReasonNoMatch) {
1190
			ms_message("Reseting version count for friend list [%p]",list);
1191 1192 1193 1194
			list->expected_notification_version = 0;
		}
	}
}
1195

1196
LinphoneCore* linphone_friend_list_get_core(const LinphoneFriendList *list) {
1197 1198
	return list->lc;
}
1199

1200
static LinphoneStatus linphone_friend_list_import_friends_from_vcard4(LinphoneFriendList *list, bctbx_list_t *vcards)  {
1201
	bctbx_list_t *vcards_iterator = NULL;
1202
	int count = 0;
1203

1204 1205 1206 1207
	if (!linphone_core_vcard_supported()) {
		ms_error("vCard support wasn't enabled at compilation time");
		return -1;
	}
1208 1209 1210 1211
	if (!list) {
		ms_error("Can't import into a NULL list");
		return -1;
	}
1212

1213
	vcards_iterator = vcards;
1214

1215 1216
	while (vcards_iterator != NULL && bctbx_list_get_data(vcards_iterator) != NULL) {
		LinphoneVcard *vcard = (LinphoneVcard *)bctbx_list_get_data(vcards_iterator);
1217
		LinphoneFriend *lf = linphone_friend_new_from_vcard(vcard);
1218
		linphone_vcard_unref(vcard);
1219 1220
		if (lf) {
			if (LinphoneFriendListOK == linphone_friend_list_import_friend(list, lf, TRUE)) {
1221
				linphone_friend_save(lf, lf->lc);
1222 1223 1224 1225
				count++;
			}
			linphone_friend_unref(lf);
		}
1226
		vcards_iterator = bctbx_list_next(vcards_iterator);
1227
	}
1228
	bctbx_list_free(vcards);
1229
	linphone_core_store_friends_list_in_db(list->lc, list);