friendlist.c 40.9 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/core.h"
23

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

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

29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneFriendListCbs);

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

static LinphoneFriendListCbs * linphone_friend_list_cbs_new(void) {
	return belle_sip_object_new(LinphoneFriendListCbs);
}

LinphoneFriendListCbs * linphone_friend_list_get_callbacks(const LinphoneFriendList *list) {
	return list->cbs;
}

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;
}

63
LinphoneFriendListCbsContactCreatedCb linphone_friend_list_cbs_get_contact_created(const LinphoneFriendListCbs *cbs) {
64 65 66
	return cbs->contact_created_cb;
}

67
void linphone_friend_list_cbs_set_contact_created(LinphoneFriendListCbs *cbs, LinphoneFriendListCbsContactCreatedCb cb) {
68 69 70
	cbs->contact_created_cb = cb;
}

71
LinphoneFriendListCbsContactDeletedCb linphone_friend_list_cbs_get_contact_deleted(const LinphoneFriendListCbs *cbs) {
72 73 74
	return cbs->contact_deleted_cb;
}

75
void linphone_friend_list_cbs_set_contact_deleted(LinphoneFriendListCbs *cbs, LinphoneFriendListCbsContactDeletedCb cb) {
76 77
	cbs->contact_deleted_cb = cb;
}
78

79
LinphoneFriendListCbsContactUpdatedCb linphone_friend_list_cbs_get_contact_updated(const LinphoneFriendListCbs *cbs) {
80 81 82
	return cbs->contact_updated_cb;
}

83
void linphone_friend_list_cbs_set_contact_updated(LinphoneFriendListCbs *cbs, LinphoneFriendListCbsContactUpdatedCb cb) {
84 85 86
	cbs->contact_updated_cb = cb;
}

87
LinphoneFriendListCbsSyncStateChangedCb linphone_friend_list_cbs_get_sync_status_changed(const LinphoneFriendListCbs *cbs) {
88 89 90
	return cbs->sync_state_changed_cb;
}

91
void linphone_friend_list_cbs_set_sync_status_changed(LinphoneFriendListCbs *cbs, LinphoneFriendListCbsSyncStateChangedCb cb) {
92 93 94
	cbs->sync_state_changed_cb = cb;
}

95 96 97 98 99 100 101 102 103 104 105 106 107 108
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
109 110 111 112 113 114
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;
115
		const bctbx_list_t *addresses = linphone_friend_get_addresses(lf);
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
116
		bctbx_list_t *numbers = linphone_friend_get_phone_numbers(lf);
117
		iterator = (bctbx_list_t *)addresses;
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
118 119 120 121 122
		while (iterator) {
			LinphoneAddress *addr = (LinphoneAddress *)bctbx_list_get_data(iterator);
			if (addr) {
				char *uri = linphone_address_as_string_uri_only(addr);
				if (uri) {
123
					uri_list = bctbx_list_prepend(uri_list, uri);
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
124 125 126 127 128 129 130 131 132
				}
			}
			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) {
133
				uri_list = bctbx_list_prepend(uri_list, ms_strdup(uri));
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
134 135 136 137 138 139 140
			}
			iterator = bctbx_list_next(iterator);
		}
	}
	return uri_list;
}

141 142 143 144 145 146
static char * create_resource_list_xml(const LinphoneFriendList *list) {
	char *xml_content = NULL;
	xmlBufferPtr buf;
	xmlTextWriterPtr writer;
	int err;

147
	if (list->friends == NULL) return NULL;
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172

	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
173 174 175

	{
		bctbx_list_t* entries = uri_list(list);
176 177
		bctbx_list_t* it;
		for(it = entries; it != NULL; it = it->next){
178
			err = add_uri_entry(writer, err, reinterpret_cast<const char *>(it->data));
179
		}
180
		bctbx_list_free_with_data(entries, ms_free);
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
	}
	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;
}

204 205 206 207 208
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);
	if (xml_ctx->doc != NULL) {
209
		LinphoneFriend *lf;
210 211
		LinphoneContent *presence_part;
		xmlXPathObjectPtr resource_object;
Simon Morlat's avatar
Simon Morlat committed
212 213 214
		char *version_str = NULL;
		char *full_state_str = NULL;
		char *uri = NULL;
215 216
		bool_t full_state = FALSE;
		int version;
217 218 219 220
		int i;

		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");
221 222 223 224 225 226 227

		version_str = linphone_get_xml_attribute_text_content(xml_ctx, "/rlmi:list", "version");
		if (version_str == NULL) {
			ms_warning("rlmi+xml: No version attribute in list");
			goto end;
		}
		version = atoi(version_str);
Ghislain MARY's avatar
Ghislain MARY committed
228
		linphone_free_xml_text_content(version_str);
229 230
		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);
231 232 233 234 235 236 237 238
		}

		full_state_str = linphone_get_xml_attribute_text_content(xml_ctx, "/rlmi:list", "fullState");
		if (full_state_str == NULL) {
			ms_warning("rlmi+xml: No fullState attribute in list");
			goto end;
		}
		if ((strcmp(full_state_str, "true") == 0) || (strcmp(full_state_str, "1") == 0)) {
239
			bctbx_list_t *l = list->friends;
240 241
			for (; l != NULL; l = bctbx_list_next(l)) {
				lf = (LinphoneFriend *)bctbx_list_get_data(l);
242
				linphone_friend_clear_presence_models(lf);
243 244 245 246 247 248 249 250 251 252
			}
			full_state = TRUE;
		}
		linphone_free_xml_text_content(full_state_str);
		if ((list->expected_notification_version == 0) && (full_state == FALSE)) {
			ms_warning("rlmi+xml: Notification with version 0 is not full state, this is not valid");
			goto end;
		}
		list->expected_notification_version = version + 1;

253
		resource_object = linphone_get_xml_xpath_object_for_node_list(xml_ctx, "/rlmi:list/rlmi:resource/rlmi:instance[@state=\"active\"]/..");
254 255
		if ((resource_object != NULL) && (resource_object->nodesetval != NULL)) {
			for (i = 1; i <= resource_object->nodesetval->nodeNr; i++) {
Simon Morlat's avatar
Simon Morlat committed
256
				char *cid = NULL;
257 258
				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");
259 260 261 262 263 264 265 266 267 268
				if (cid != NULL) {
					presence_part = linphone_content_find_part_by_header(body, "Content-Id", cid);
					if (presence_part == NULL) {
						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);
						if (presence != NULL) {
							// 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;
269
							uri = linphone_get_xml_text_content(xml_ctx, "./@uri");
270 271 272 273 274 275 276 277
							if (uri == NULL) 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) {
								const char *phone_number = linphone_friend_sip_uri_to_phone_number(lf, uri);
								lf->presence_received = TRUE;
278 279 280
								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));
281 282
									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))){
283
										linphone_friend_unref((LinphoneFriend*)bctbx_pair_cchar_get_second(bctbx_iterator_cchar_get_pair(it)));
284
										bctbx_map_cchar_erase(list->friends_map_uri, it);
285
									}
286
									bctbx_map_cchar_insert_and_delete(list->friends_map_uri, pair);
287 288 289 290
									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);
								}
291 292 293 294 295 296
								if (full_state == FALSE) {
									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);
									linphone_core_notify_notify_presence_received(list->lc, lf);
297
								}
298
								linphone_free_xml_text_content(uri);
299
							}
300
							linphone_content_unref(presence_part);
301 302
						}
					}
303
					linphone_free_xml_text_content(cid);
304 305 306 307
				}
			}
		}
		if (resource_object != NULL) xmlXPathFreeObject(resource_object);
308 309

		if (full_state == TRUE) {
310
			const bctbx_list_t *addresses;
311 312
			bctbx_list_t *numbers;
			bctbx_list_t *iterator;
313
			bctbx_list_t *l = list->friends;
314 315
			for (; l != NULL; l = bctbx_list_next(l)) {
				lf = (LinphoneFriend *)bctbx_list_get_data(l);
316 317
				addresses = linphone_friend_get_addresses(lf);
				numbers = linphone_friend_get_phone_numbers(lf);
318
				iterator = (bctbx_list_t *)addresses;
319 320 321 322
				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);
323
					if (presence) linphone_core_notify_notify_presence_received_for_uri_or_tel(list->lc, lf, uri, presence);
324 325 326 327 328 329 330
					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);
331 332 333
					if (presence) {
						linphone_core_notify_notify_presence_received_for_uri_or_tel(list->lc, lf, number, presence);
					}
334 335
					iterator = bctbx_list_next(iterator);
				}
Ghislain MARY's avatar
Ghislain MARY committed
336
				if (numbers) bctbx_list_free(numbers);
337 338
				if (linphone_friend_is_presence_received(lf) == TRUE) {
					linphone_core_notify_notify_presence_received(list->lc, lf);
339 340 341
				}
			}
		}
342 343 344 345 346 347 348 349
	} else {
		ms_warning("Wrongly formatted rlmi+xml body: %s", xml_ctx->errorBuffer);
	}

end:
	linphone_xmlparsing_context_destroy(xml_ctx);
}

350
static bool_t linphone_friend_list_has_subscribe_inactive(const LinphoneFriendList *list) {
351
	bctbx_list_t *l = list->friends;
352
	bool_t has_subscribe_inactive = FALSE;
353 354
	for (; l != NULL; l = bctbx_list_next(l)) {
		LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(l);
355
		if (lf->subscribe_active != TRUE) {
356 357 358 359 360 361 362
			has_subscribe_inactive = TRUE;
			break;
		}
	}
	return has_subscribe_inactive;
}

363 364
static LinphoneFriendList * linphone_friend_list_new(void) {
	LinphoneFriendList *list = belle_sip_object_new(LinphoneFriendList);
365
	list->cbs = linphone_friend_list_cbs_new();
366
	list->enable_subscriptions = TRUE;
367
	list->friends_map = bctbx_mmap_cchar_new();
368
	list->friends_map_uri = bctbx_mmap_cchar_new();
369 370 371
	return list;
}

372 373
static void linphone_friend_list_destroy(LinphoneFriendList *list) {
	if (list->display_name != NULL) ms_free(list->display_name);
374
	if (list->rls_addr) linphone_address_unref(list->rls_addr);
375
	if (list->rls_uri != NULL) ms_free(list->rls_uri);
376
	if (list->content_digest != NULL) ms_free(list->content_digest);
377 378 379
	if (list->event != NULL) {
		linphone_event_terminate(list->event);
		linphone_event_unref(list->event);
380
		list->event = NULL;
381
	}
382 383
	if (list->uri != NULL) ms_free(list->uri);
	if (list->cbs) linphone_friend_list_cbs_unref(list->cbs);
384 385
	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
386
	if (list->friends_map) bctbx_mmap_cchar_delete_with_data(list->friends_map, (void (*)(void *))linphone_friend_unref);
387
	if (list->friends_map_uri) bctbx_mmap_cchar_delete_with_data(list->friends_map_uri, (void (*)(void *))linphone_friend_unref);
388 389 390 391 392 393 394 395
}

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
396
	FALSE
397 398 399
);


400 401 402
LinphoneFriendList * linphone_core_create_friend_list(LinphoneCore *lc) {
	LinphoneFriendList *list = linphone_friend_list_new();
	list->lc = lc;
403 404 405 406 407 408 409 410
	return list;
}

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

411 412 413
void _linphone_friend_list_release(LinphoneFriendList *list){
	/*drops all references to core and unref*/
	list->lc = NULL;
Simon Morlat's avatar
Simon Morlat committed
414 415 416 417
	if (list->event != NULL) {
		linphone_event_unref(list->event);
		list->event = NULL;
	}
418 419 420 421
	if (list->cbs) {
		linphone_friend_list_cbs_unref(list->cbs);
		list->cbs = NULL;
	}
422
	if (list->dirty_friends_to_update) {
423
		list->dirty_friends_to_update = bctbx_list_free_with_data(list->dirty_friends_to_update, (void (*)(void *))linphone_friend_unref);
424 425
	}
	if (list->friends) {
426
		list->friends = bctbx_list_free_with_data(list->friends, (void (*)(void *))_linphone_friend_release);
427
	}
428
	linphone_friend_list_unref(list);
429 430
}

431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453
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);
454
		linphone_core_store_friends_list_in_db(list->lc, list);
455 456 457
	}
}

458 459
const LinphoneAddress * linphone_friend_list_get_rls_address(const LinphoneFriendList *list){
	return list->rls_addr;
460
}
461 462 463
const LinphoneAddress * _linphone_friend_list_get_rls_address(const LinphoneFriendList *list) {
	if (list->rls_addr)
		return list->rls_addr;
464 465 466 467
	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);
468

469
		list->lc->default_rls_addr=NULL;
470

471 472 473 474
		if (rls_uri) {
			/*to make sure changes in config are used if any*/
			list->lc->default_rls_addr = linphone_address_new(rls_uri);
		}
475

476
		return list->lc->default_rls_addr;
477
	}
478 479 480
	else
		return NULL;
}
481 482
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;
483

484 485 486 487 488
	if (list->rls_addr){
		linphone_address_unref(list->rls_addr);
	}
	list->rls_addr = new_rls_addr;
	if (list->rls_uri != NULL){
489 490 491
		ms_free(list->rls_uri);
		list->rls_uri = NULL;
	}
492 493
	if (list->rls_addr){
		list->rls_uri = linphone_address_as_string(list->rls_addr);
494
		linphone_core_store_friends_list_in_db(list->lc, list);
495 496 497
	}
}

498 499 500 501 502 503 504
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
505
	if (addr) linphone_address_unref(addr);
506 507
}

508
static LinphoneFriendListStatus _linphone_friend_list_add_friend(LinphoneFriendList *list, LinphoneFriend *lf, bool_t synchronize) {
509
	LinphoneFriendListStatus status = LinphoneFriendListInvalidFriend;
510
	const LinphoneAddress *addr;
511

512
	if (!list || lf->friend_list) {
Sylvain Berfini's avatar
Sylvain Berfini committed
513 514
		if (!list)
			ms_error("linphone_friend_list_add_friend(): invalid list, null");
515
		if (lf->friend_list)
516
			ms_error("linphone_friend_list_add_friend(): invalid friend, already in list");
517
		return status;
Simon Morlat's avatar
Simon Morlat committed
518
	}
519
	addr = linphone_friend_get_address(lf);
Benjamin REIS's avatar
Benjamin REIS committed
520
	bool_t present = FALSE;
Benjamin REIS's avatar
Benjamin REIS committed
521
	if (lf->refkey) {
Benjamin REIS's avatar
Benjamin REIS committed
522
		present = linphone_friend_list_find_friend_by_ref_key(list, lf->refkey) != NULL;
Benjamin REIS's avatar
Benjamin REIS committed
523
	} else {
Benjamin REIS's avatar
Benjamin REIS committed
524
		present = bctbx_list_find(list->friends, lf) != NULL;
Benjamin REIS's avatar
Benjamin REIS committed
525
	}
Benjamin REIS's avatar
Benjamin REIS committed
526
	if (present) {
527 528 529
		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
530
		if (tmp) ms_free(tmp);
531
	} else {
532
		status = linphone_friend_list_import_friend(list, lf, synchronize);
533
		linphone_friend_save(lf, lf->lc);
534
	}
535

536 537 538 539 540
	if (list->rls_uri == NULL) {
		/* Mimic the behaviour of linphone_core_add_friend() when a resource list server is not in use */
		linphone_friend_apply(lf, lf->lc);
	}
	return status;
541 542
}

543 544 545 546 547 548 549 550
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);
}

551
LinphoneFriendListStatus linphone_friend_list_import_friend(LinphoneFriendList *list, LinphoneFriend *lf, bool_t synchronize) {
552 553 554
	bctbx_list_t *iterator;
	bctbx_list_t *phone_numbers;
	const bctbx_list_t *addresses;
555
	if (lf->friend_list) {
556 557
		if (lf->friend_list)
			ms_error("linphone_friend_list_add_friend(): invalid friend, already in list");
Sylvain Berfini's avatar
Sylvain Berfini committed
558 559
		return LinphoneFriendListInvalidFriend;
	}
560
	lf->friend_list = list;
561
	lf->lc = list->lc;
Benjamin REIS's avatar
Benjamin REIS committed
562
	list->friends = bctbx_list_prepend(list->friends, linphone_friend_ref(lf));
563 564 565 566
	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);
	}
567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592

	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);
	}

	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);
	}

593
	if (synchronize) {
Benjamin REIS's avatar
Benjamin REIS committed
594
		list->dirty_friends_to_update = bctbx_list_prepend(list->dirty_friends_to_update, linphone_friend_ref(lf));
595
	}
596 597 598
	return LinphoneFriendListOK;
}

599
static void carddav_done(LinphoneCardDavContext *cdc, bool_t success, const char *msg) {
600 601 602
	if (cdc && cdc->friend_list->cbs->sync_state_changed_cb) {
		cdc->friend_list->cbs->sync_state_changed_cb(cdc->friend_list, success ? LinphoneFriendListSyncSuccessful : LinphoneFriendListSyncFailure, msg);
	}
603 604 605
	linphone_carddav_context_destroy(cdc);
}

606
static LinphoneFriendListStatus _linphone_friend_list_remove_friend(LinphoneFriendList *list, LinphoneFriend *lf, bool_t remove_from_server) {
607 608 609
	bctbx_list_t *iterator;
	bctbx_list_t *phone_numbers;
	const bctbx_list_t *addresses;
610
	bctbx_list_t *elem = bctbx_list_find(list->friends, lf);
611
	if (elem == NULL) return LinphoneFriendListNonExistentFriend;
612

613
#ifdef SQLITE_STORAGE_ENABLED
614 615 616
	if (lf && lf->lc && lf->lc->friends_db) {
		linphone_core_remove_friend_from_db(lf->lc, lf);
	}
617
#endif
618
	if (remove_from_server) {
619
		LinphoneVcard *lvc = linphone_friend_get_vcard(lf);
620 621
		if (lvc && linphone_vcard_get_uid(lvc)) {
			LinphoneCardDavContext *cdc = linphone_carddav_context_new(list);
622 623 624 625 626 627
			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);
				}
				linphone_carddav_delete_vcard(cdc, lf);
628 629
			}
		}
630
	}
631 632 633
	if (!list->lc->friends_db_file) {
		linphone_core_write_friends_config(list->lc);
	}
634
	list->friends = bctbx_list_erase_link(list->friends, elem);
635 636
	if(lf->refkey) {
		bctbx_iterator_t * it = bctbx_map_cchar_find_key(list->friends_map, lf->refkey);
637 638
		bctbx_iterator_t * end = bctbx_map_cchar_end(list->friends_map);
		if (!bctbx_iterator_cchar_equals(it, end)){
639 640 641
			linphone_friend_unref((LinphoneFriend*)bctbx_pair_cchar_get_second(bctbx_iterator_cchar_get_pair(it)));
			bctbx_map_cchar_erase(list->friends_map, it);
		}
642 643
		if (it) bctbx_iterator_cchar_delete(it);
		if (end) bctbx_iterator_cchar_delete(end);
644
	}
645

646 647 648 649 650 651 652
	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);
653 654
			bctbx_iterator_t * end = bctbx_map_cchar_end(list->friends_map_uri);
			if (!bctbx_iterator_cchar_equals(it, end)){
655 656 657
				linphone_friend_unref((LinphoneFriend*)bctbx_pair_cchar_get_second(bctbx_iterator_cchar_get_pair(it)));
				bctbx_map_cchar_erase(list->friends_map_uri, it);
			}
658 659
			if (it) bctbx_iterator_cchar_delete(it);
			if (end) bctbx_iterator_cchar_delete(end);
660 661 662
		}
		iterator = bctbx_list_next(iterator);
	}
663
	if (phone_numbers) bctbx_list_free(phone_numbers);
664 665 666 667 668 669 670 671

	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);
672 673
			bctbx_iterator_t * end = bctbx_map_cchar_end(list->friends_map_uri);
			if (!bctbx_iterator_cchar_equals(it, end)){
674 675 676
				linphone_friend_unref((LinphoneFriend*)bctbx_pair_cchar_get_second(bctbx_iterator_cchar_get_pair(it)));
				bctbx_map_cchar_erase(list->friends_map_uri, it);
			}
677 678
			if (it) bctbx_iterator_cchar_delete(it);
			if (end) bctbx_iterator_cchar_delete(end);
679 680
			ms_free(uri);
		}
681

682 683 684
		iterator = bctbx_list_next(iterator);
	}

685 686
	lf->friend_list = NULL;
	linphone_friend_unref(lf);
687
	return LinphoneFriendListOK;
688
}
689

690 691 692 693
LinphoneFriendListStatus linphone_friend_list_remove_friend(LinphoneFriendList *list, LinphoneFriend *lf) {
	return _linphone_friend_list_remove_friend(list, lf, TRUE);
}

694
const bctbx_list_t * linphone_friend_list_get_friends(const LinphoneFriendList *list) {
695 696 697
	return list->friends;
}

698
void linphone_friend_list_update_dirty_friends(LinphoneFriendList *list) {
699
	bctbx_list_t *dirty_friends = list->dirty_friends_to_update;
700

701 702 703
	while (dirty_friends) {
		LinphoneCardDavContext *cdc = linphone_carddav_context_new(list);
		if (cdc) {
704
			LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(dirty_friends);
705
			cdc->sync_done_cb = carddav_done;
706
			if (lf) {
707 708 709
				if (cdc->friend_list->cbs->sync_state_changed_cb) {
					cdc->friend_list->cbs->sync_state_changed_cb(cdc->friend_list, LinphoneFriendListSyncStarted, NULL);
				}
710 711 712
				linphone_carddav_put_vcard(cdc, lf);
			}
		}
713
		dirty_friends = bctbx_list_next(dirty_friends);
714
	}
715
	list->dirty_friends_to_update = bctbx_list_free_with_data(list->dirty_friends_to_update, (void (*)(void *))linphone_friend_unref);
716 717 718
}

static void carddav_created(LinphoneCardDavContext *cdc, LinphoneFriend *lf) {
719
	if (cdc) {
720
		LinphoneFriendList *lfl = cdc->friend_list;
721
		linphone_friend_list_import_friend(lfl, lf, FALSE);
722
		if (cdc->friend_list->cbs->contact_created_cb) {
723
			cdc->friend_list->cbs->contact_created_cb(lfl, lf);
724
		}
725 726 727 728
	}
}

static void carddav_removed(LinphoneCardDavContext *cdc, LinphoneFriend *lf) {
729
	if (cdc) {
730
		LinphoneFriendList *lfl = cdc->friend_list;
731
		_linphone_friend_list_remove_friend(lfl, lf, FALSE);
732
		if (cdc->friend_list->cbs->contact_deleted_cb) {
733
			cdc->friend_list->cbs->contact_deleted_cb(lfl, lf);
734
		}
735 736 737
	}
}

738
static void carddav_updated(LinphoneCardDavContext *cdc, LinphoneFriend *lf_new, LinphoneFriend *lf_old) {
739 740
	if (cdc) {
		LinphoneFriendList *lfl = cdc->friend_list;
741
		bctbx_list_t *elem = bctbx_list_find(lfl->friends, lf_old);
742
		if (elem) {
743
			elem->data = linphone_friend_ref(lf_new);
744
		}
745
		linphone_core_store_friend_in_db(lf_new->lc, lf_new);
746

747 748 749
		if (cdc->friend_list->cbs->contact_updated_cb) {
			cdc->friend_list->cbs->contact_updated_cb(lfl, lf_new, lf_old);
		}
750
		linphone_friend_unref(lf_old);
751
	}
752 753
}

754
void linphone_friend_list_synchronize_friends_from_server(LinphoneFriendList *list) {
755
	LinphoneCardDavContext *cdc = NULL;
756

757 758 759 760
	if (!list || !list->uri || !list->lc) {
		ms_error("FATAL");
		return;
	}
761

762
	cdc = linphone_carddav_context_new(list);
763 764 765
	if (cdc) {
		cdc->contact_created_cb = carddav_created;
		cdc->contact_removed_cb = carddav_removed;
766
		cdc->contact_updated_cb = carddav_updated;
767
		cdc->sync_done_cb = carddav_done;
768 769 770
		if (cdc && cdc->friend_list->cbs->sync_state_changed_cb) {
			cdc->friend_list->cbs->sync_state_changed_cb(cdc->friend_list, LinphoneFriendListSyncStarted, NULL);
		}
771 772 773 774
		linphone_carddav_synchronize(cdc);
	}
}

775
LinphoneFriend * linphone_friend_list_find_friend_by_address(const LinphoneFriendList *list, const LinphoneAddress *address) {
776
	LinphoneAddress *clean_addr = linphone_address_clone(address);
777
	LinphoneFriend *lf;
778
	if (linphone_address_has_uri_param(clean_addr, "gr")) {
779 780
		linphone_address_remove_uri_param(clean_addr, "gr");
	}
781 782 783
	char *uri = linphone_address_as_string_uri_only(clean_addr);
	lf = linphone_friend_list_find_friend_by_uri(list, uri);
	bctbx_free(uri);
784 785
	linphone_address_unref(clean_addr);
	return lf;
786 787 788
}

LinphoneFriend * linphone_friend_list_find_friend_by_uri(const LinphoneFriendList *list, const char *uri) {
789
	LinphoneFriend *result = NULL;
790 791 792
	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)) {
793 794
		bctbx_pair_t *pair = bctbx_iterator_cchar_get_pair(it);
		result = (LinphoneFriend *)bctbx_pair_cchar_get_second(pair);
795
	}
796
	bctbx_iterator_cchar_delete(end);
797 798
	bctbx_iterator_cchar_delete(it);
	return result;
799 800 801
}

LinphoneFriend * linphone_friend_list_find_friend_by_ref_key(const LinphoneFriendList *list, const char *ref_key) {
802
	if(list) {
803
		bctbx_iterator_t* it = bctbx_map_cchar_find_key(list->friends_map, ref_key);
804 805 806 807
		if (!bctbx_iterator_cchar_equals(it, bctbx_map_cchar_end(list->friends_map))) {
			bctbx_pair_t *pair = bctbx_iterator_cchar_get_pair(it);
			return (LinphoneFriend *)bctbx_pair_cchar_get_second(pair);
		}
808
	}
809
	return NULL;
810 811
}

812 813 814 815
LinphoneFriend * linphone_friend_list_find_friend_by_inc_subscribe (
	const LinphoneFriendList *list,
	LinphonePrivate::SalOp *op
) {
816
	const bctbx_list_t *elem;
817 818
	for (elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) {
		LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(elem);
819
		if (bctbx_list_find(lf->insubs, op)) return lf;
820 821 822 823
	}
	return NULL;
}

824 825 826 827
LinphoneFriend * linphone_friend_list_find_friend_by_out_subscribe (
	const LinphoneFriendList *list,
	LinphonePrivate::SalOp *op
) {
828
	const bctbx_list_t *elem;
829 830
	for (elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) {
		LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(elem);
831
		if (lf->outsub && ((lf->outsub == op) || lf->outsub->is_forked_of(op))) return lf;
832 833 834 835
	}
	return NULL;
}

836 837
static void linphone_friend_list_close_subscriptions(LinphoneFriendList *list) {
	/* FIXME we should wait until subscription to complete. */
jehan's avatar
jehan committed
838 839
	if (list->event) {
		linphone_event_terminate(list->event);
840 841
		linphone_event_unref(list->event);
		list->event = NULL;
842
	}
843
	bctbx_list_for_each(list->friends, (void (*)(void *))linphone_friend_close_subscriptions);
844 845
}

846
static void linphone_friend_list_send_list_subscription(LinphoneFriendList *list){
847
	const LinphoneAddress *address = _linphone_friend_list_get_rls_address(list);
848 849 850 851 852 853 854 855 856
	char *xml_content = create_resource_list_xml(list);
	if ((address != NULL) && (xml_content != NULL) && (linphone_friend_list_has_subscribe_inactive(list) == TRUE)) {
		unsigned char digest[16];
		bctbx_md5((unsigned char *)xml_content, strlen(xml_content), digest);
		if ((list->event != NULL) && (list->content_digest != NULL) && (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;
857
			bctbx_list_t * elem = NULL;
858 859 860
			int expires = lp_config_get_int(list->lc->config, "sip", "rls_presence_expires", 3600);
			list->expected_notification_version = 0;
			if (list->content_digest != NULL) ms_free(list->content_digest);
861
			list->content_digest = reinterpret_cast<unsigned char *>(ms_malloc(sizeof(digest)));
862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881
			memcpy(list->content_digest, digest, sizeof(digest));
			if (list->event != NULL) {
				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");
			}
882 883 884 885
			for (elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) {
				LinphoneFriend *lf = (LinphoneFriend *)elem->data;
				lf->subscribe_active = TRUE;
			}
886 887 888 889 890 891 892 893 894 895
			linphone_event_send_subscribe(list->event, content);
			linphone_content_unref(content);
			linphone_event_set_user_data(list->event, list);
		}
	}
	if (xml_content != NULL) ms_free(xml_content);
}

void linphone_friend_list_update_subscriptions(LinphoneFriendList *list){
	LinphoneProxyConfig *cfg = NULL;
896
	const LinphoneAddress *address = _linphone_friend_list_get_rls_address(list);
897 898
	bool_t only_when_registered = FALSE;
	bool_t should_send_list_subscribe = FALSE;
899

900 901 902 903 904 905
	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);
	}
906

907
	if (address != NULL) {
908
		if (list->enable_subscriptions) {
909 910 911 912 913 914 915 916 917 918
			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);
919
				}
920
			}
921 922
		} else {
			ms_message("Friends list [%p] subscription update skipped since subscriptions not enabled yet", list);
923
		}
924
	} else if (list->enable_subscriptions) {
925
		const bctbx_list_t *elem;
926 927
		for (elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) {
			LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(elem);
928
			linphone_friend_update_subscribes(lf, only_when_registered);
929
		}
930 931 932 933
	}
}

void linphone_friend_list_invalidate_subscriptions(LinphoneFriendList *list) {
934
	const bctbx_list_t *elem;
935 936 937 938 939 940 941 942

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

943 944
	for (elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) {
		LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(elem);
945
		linphone_friend_invalidate_subscription(lf);
946 947 948 949
	}
}

void linphone_friend_list_notify_presence(LinphoneFriendList *list, LinphonePresenceModel *presence) {
950
	const bctbx_list_t *elem;
951 952
	for(elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) {
		LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(elem);
953
		linphone_friend_notify(lf, presence);
954 955
	}
}
956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977

void linphone_friend_list_notify_presence_received(LinphoneFriendList *list, LinphoneEvent *lev, const LinphoneContent *body) {
	if (linphone_content_is_multipart(body)) {
		LinphoneContent *first_part;
		const char *type = linphone_content_get_type(body);
		const char *subtype = linphone_content_get_subtype(body);

		if ((strcmp(type, "multipart") != 0) || (strcmp(subtype, "related") != 0)) {
			ms_warning("multipart presence notified but it is not 'multipart/related'");
			return;
		}

		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
978
			linphone_content_unref(first_part);
979 980 981 982
			return;
		}

		linphone_friend_list_parse_multipart_related_body(list, body, linphone_content_get_string_buffer(first_part));
Ghislain MARY's avatar
Ghislain MARY committed
983
		linphone_content_unref(first_part);
984 985
	}
}
986 987 988 989 990 991 992 993 994 995 996 997

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);
998
		linphone_core_store_friends_list_in_db(list->lc, list);
999 1000 1001 1002 1003
	}
}

void linphone_friend_list_update_revision(LinphoneFriendList *list, int rev) {
	list->revision = rev;
1004
	linphone_core_store_friends_list_in_db(list->lc, list);
1005 1006
}

1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017
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);
1018

1019
		if (state == LinphoneSubscriptionOutgoingProgress && linphone_event_get_reason(lev) == LinphoneReasonNoMatch) {
1020
			ms_message("Reseting version count for friend list [%p]",list);
1021 1022 1023 1024
			list->expected_notification_version = 0;
		}
	}
}
1025

1026
LinphoneCore* linphone_friend_list_get_core(const LinphoneFriendList *list) {
1027 1028
	return list->lc;
}
1029

1030
static LinphoneStatus linphone_friend_list_import_friends_from_vcard4(LinphoneFriendList *list, bctbx_list_t *vcards)  {
1031
	bctbx_list_t *vcards_iterator = NULL;
1032
	int count = 0;
1033

1034 1035 1036 1037
	if (!linphone_core_vcard_supported()) {
		ms_error("vCard support wasn't enabled at compilation time");
		return -1;
	}
1038 1039 1040 1041
	if (!list) {
		ms_error("Can't import into a NULL list");
		return -1;
	}
1042

1043
	vcards_iterator = vcards;
1044

1045 1046
	while (vcards_iterator != NULL && bctbx_list_get_data(vcards_iterator) != NULL) {
		LinphoneVcard *vcard = (LinphoneVcard *)bctbx_list_get_data(vcards_iterator);
1047
		LinphoneFriend *lf = linphone_friend_new_from_vcard(vcard);
1048
		linphone_vcard_unref(vcard);
1049 1050
		if (lf) {
			if (LinphoneFriendListOK == linphone_friend_list_import_friend(list, lf, TRUE)) {
1051
				linphone_friend_save(lf, lf->lc);
1052 1053 1054 1055
				count++;
			}
			linphone_friend_unref(lf);
		}
1056
		vcards_iterator = bctbx_list_next(vcards_iterator);
1057
	}
1058
	bctbx_list_free(vcards);
1059
	linphone_core_store_friends_list_in_db(list->lc, list);
1060
	return count;
1061

1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080
}
LinphoneStatus linphone_friend_list_import_friends_from_vcard4_file(LinphoneFriendList *list, const char *vcard_file) {
	bctbx_list_t *vcards = NULL;

	if (!linphone_core_vcard_supported()) {
		ms_error("vCard support wasn't enabled at compilation time");
		return -1;
	}
	if (!list) {
		ms_error("Can't import into a NULL list");
		return -1;
	}

	vcards = linphone_vcard_context_get_vcard_list_from_file(list->lc->vcard_context, vcard_file);
	if (!vcards) {
		ms_error("Failed to parse the file %s", vcard_file);
		return -1;
	}
	return linphone_friend_list_import_friends_from_vcard4(list,vcards);
1081 1082
}

1083
LinphoneStatus linphone_friend_list_import_friends_from_vcard4_buffer(LinphoneFriendList *list, const char *vcard_buffer) {
1084
	bctbx_list_t *vcards = NULL;
1085

1086 1087 1088 1089
	if (!linphone_core_vcard_supported()) {
		ms_error("vCard support wasn't enabled at compilation time");
		return -1;
	}
1090 1091 1092 1093
	if (!list) {
		ms_error("Can't import into a NULL list");
		return -1;
	}
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
1094

1095
	vcards = linphone_vcard_context_get_vcard_list_from_buffer(list->lc->vcard_context, vcard_buffer);
1096 1097 1098 1099
	if (!vcards) {
		ms_error("Failed to parse the buffer");
		return -1;
	}
1100

1101
	return linphone_friend_list_import_friends_from_vcard4(list,vcards);}
1102 1103 1104

void linphone_friend_list_export_friends_as_vcard4_file(LinphoneFriendList *list, const char *vcard_file) {
	FILE *file = NULL;
1105 1106 1107 1108 1109 1110
	const bctbx_list_t *friends;

	if (!linphone_core_vcard_supported()) {
		ms_error("vCard support wasn't enabled at compilation time");
		return;
	}
1111

1112
	file = fopen(vcard_file, "wb");
1113 1114 1115 1116
	if (file == NULL) {
		ms_warning("Could not write %s ! Maybe it is read-only. Contacts will not be saved.", vcard_file);
		return;
	}
1117

1118 1119 1120
	friends = linphone_friend_list_get_friends(list);
	while (friends != NULL && bctbx_list_get_data(friends) != NULL) {
		LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(friends);
1121
		LinphoneVcard *vcard = linphone_friend_get_vcard(lf);
1122 1123 1124 1125
		if (vcard) {
			const char *vcard_text = linphone_vcard_as_vcard4_string(vcard);
			fprintf(file, "%s", vcard_text);
		}
1126
		friends = bctbx_list_next(friends);
1127
	}
1128

1129 1130
	fclose(file);
}
1131 1132 1133

void linphone_friend_list_enable_subscriptions(LinphoneFriendList *list, bool_t enabled) {
	if (list->enable_subscriptions != enabled) {
1134
		list->enable_subscriptions = enabled;
1135
		if (enabled) {
1136
			linphone_friend_list_update_subscriptions(list);
1137 1138 1139
		} else {
			linphone_friend_list_close_subscriptions(list);
		}
1140

1141 1142
	}
}
1143 1144 1145 1146

bool_t linphone_friend_list_subscriptions_enabled(LinphoneFriendList *list) {
	return list->enable_subscriptions;
}