friendlist.c 44.3 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 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
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;
}

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

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

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

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

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

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

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

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

96 97 98 99 100 101 102 103
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;
}

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

150 151 152 153 154 155
static char * create_resource_list_xml(const LinphoneFriendList *list) {
	char *xml_content = NULL;
	xmlBufferPtr buf;
	xmlTextWriterPtr writer;
	int err;

156
	if (list->friends == NULL) return NULL;
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181

	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
182 183 184

	{
		bctbx_list_t* entries = uri_list(list);
185 186
		bctbx_list_t* it;
		for(it = entries; it != NULL; it = it->next){
187
			err = add_uri_entry(writer, err, reinterpret_cast<const char *>(it->data));
188
		}
189
		bctbx_list_free_with_data(entries, ms_free);
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
	}
	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;
}

213 214 215 216
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
217
	if (xml_ctx->doc) {
218
		LinphoneFriend *lf;
219 220
		LinphoneContent *presence_part;
		xmlXPathObjectPtr resource_object;
Simon Morlat's avatar
Simon Morlat committed
221 222 223
		char *version_str = NULL;
		char *full_state_str = NULL;
		char *uri = NULL;
224 225
		bool_t full_state = FALSE;
		int version;
226
		int i;
227 228
		bctbx_list_t *list_friends_presence_received = NULL;
		LinphoneFriendListCbs *list_cbs = linphone_friend_list_get_callbacks(list);
229 230 231

		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");
232 233

		version_str = linphone_get_xml_attribute_text_content(xml_ctx, "/rlmi:list", "version");
Benjamin REIS's avatar
Benjamin REIS committed
234
		if (!version_str) {
235 236 237 238
			ms_warning("rlmi+xml: No version attribute in list");
			goto end;
		}
		version = atoi(version_str);
Ghislain MARY's avatar
Ghislain MARY committed
239
		linphone_free_xml_text_content(version_str);
240 241
		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);
242 243 244
		}

		full_state_str = linphone_get_xml_attribute_text_content(xml_ctx, "/rlmi:list", "fullState");
Benjamin REIS's avatar
Benjamin REIS committed
245
		if (!full_state_str) {
246 247 248 249
			ms_warning("rlmi+xml: No fullState attribute in list");
			goto end;
		}
		if ((strcmp(full_state_str, "true") == 0) || (strcmp(full_state_str, "1") == 0)) {
250
			bctbx_list_t *l = list->friends;
251 252
			for (; l != NULL; l = bctbx_list_next(l)) {
				lf = (LinphoneFriend *)bctbx_list_get_data(l);
253
				linphone_friend_clear_presence_models(lf);
254 255 256 257
			}
			full_state = TRUE;
		}
		linphone_free_xml_text_content(full_state_str);
Benjamin REIS's avatar
Benjamin REIS committed
258
		if ((list->expected_notification_version == 0) && !full_state) {
259 260 261 262 263
			ms_warning("rlmi+xml: Notification with version 0 is not full state, this is not valid");
			goto end;
		}
		list->expected_notification_version = version + 1;

264
		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
265
		if (resource_object && resource_object->nodesetval) {
266
			for (i = 1; i <= resource_object->nodesetval->nodeNr; i++) {
Simon Morlat's avatar
Simon Morlat committed
267
				char *cid = NULL;
268 269
				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
270
				if (cid) {
271
					presence_part = linphone_content_find_part_by_header(body, "Content-Id", cid);
Benjamin REIS's avatar
Benjamin REIS committed
272
					if (!presence_part) {
273 274 275 276
						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
277
						if (presence) {
278 279
							// 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;
280
							uri = linphone_get_xml_text_content(xml_ctx, "./@uri");
Benjamin REIS's avatar
Benjamin REIS committed
281 282
							if (!uri)
								continue;
283
							addr = linphone_address_new(uri);
Benjamin REIS's avatar
Benjamin REIS committed
284 285
							if (!addr)
								continue;
286 287
							lf = linphone_friend_list_find_friend_by_address(list, addr);
							linphone_address_unref(addr);
288 289 290 291

							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
292
								linphone_friend_unref(lf);
293
							}
294 295 296
							if (lf) {
								const char *phone_number = linphone_friend_sip_uri_to_phone_number(lf, uri);
								lf->presence_received = TRUE;
297 298 299
								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));
300 301
									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))){
302
										linphone_friend_unref((LinphoneFriend*)bctbx_pair_cchar_get_second(bctbx_iterator_cchar_get_pair(it)));
303
										bctbx_map_cchar_erase(list->friends_map_uri, it);
304
									}
305
									bctbx_map_cchar_insert_and_delete(list->friends_map_uri, pair);
306 307 308 309
									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
310
								if (!full_state) {
311 312 313 314
									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);
315
									// Deprecated
316
									linphone_core_notify_notify_presence_received(list->lc, lf);
317
									list_friends_presence_received = bctbx_list_prepend(list_friends_presence_received, lf);
318
								}
319
								linphone_free_xml_text_content(uri);
320
							}
321
							linphone_content_unref(presence_part);
322 323
						}
					}
324
					linphone_free_xml_text_content(cid);
325 326
				}
			}
327 328 329 330 331
			// Notify list with all friends for which we received presence information
			if (bctbx_list_size(list_friends_presence_received) > 0 && 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);
			}
			bctbx_list_free(list_friends_presence_received);
332
		}
Benjamin REIS's avatar
Benjamin REIS committed
333 334
		if (resource_object)
			xmlXPathFreeObject(resource_object);
335

Benjamin REIS's avatar
Benjamin REIS committed
336
		if (full_state) {
337
			const bctbx_list_t *addresses;
338 339
			bctbx_list_t *numbers;
			bctbx_list_t *iterator;
340
			bctbx_list_t *l = list->friends;
341 342
			for (; l != NULL; l = bctbx_list_next(l)) {
				lf = (LinphoneFriend *)bctbx_list_get_data(l);
343 344
				addresses = linphone_friend_get_addresses(lf);
				numbers = linphone_friend_get_phone_numbers(lf);
345
				iterator = (bctbx_list_t *)addresses;
346 347 348 349
				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);
350
					if (presence) linphone_core_notify_notify_presence_received_for_uri_or_tel(list->lc, lf, uri, presence);
351 352 353 354 355 356 357
					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);
358 359 360
					if (presence) {
						linphone_core_notify_notify_presence_received_for_uri_or_tel(list->lc, lf, number, presence);
					}
361 362
					iterator = bctbx_list_next(iterator);
				}
Ghislain MARY's avatar
Ghislain MARY committed
363
				if (numbers) bctbx_list_free(numbers);
Benjamin REIS's avatar
Benjamin REIS committed
364
				if (linphone_friend_is_presence_received(lf)) {
365
					// Deprecated
366
					linphone_core_notify_notify_presence_received(list->lc, lf);
367
					list_friends_presence_received = bctbx_list_prepend(list_friends_presence_received, lf);
368 369
				}
			}
370 371 372 373 374
			// Notify list with all friends for which we received presence information
			if (bctbx_list_size(list_friends_presence_received) > 0 && 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);
			}
			bctbx_list_free(list_friends_presence_received);
375
		}
376 377 378 379 380 381 382 383
	} else {
		ms_warning("Wrongly formatted rlmi+xml body: %s", xml_ctx->errorBuffer);
	}

end:
	linphone_xmlparsing_context_destroy(xml_ctx);
}

384
static bool_t linphone_friend_list_has_subscribe_inactive(const LinphoneFriendList *list) {
385 386 387
	if (list->bodyless_subscription)
		return TRUE;

388
	bctbx_list_t *l = list->friends;
389
	bool_t has_subscribe_inactive = FALSE;
390 391
	for (; l != NULL; l = bctbx_list_next(l)) {
		LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(l);
392
		if (lf->subscribe_active != TRUE) {
393 394 395 396 397 398 399
			has_subscribe_inactive = TRUE;
			break;
		}
	}
	return has_subscribe_inactive;
}

400 401
static LinphoneFriendList * linphone_friend_list_new(void) {
	LinphoneFriendList *list = belle_sip_object_new(LinphoneFriendList);
402
	list->cbs = linphone_friend_list_cbs_new();
403
	list->enable_subscriptions = TRUE;
404
	list->friends_map = bctbx_mmap_cchar_new();
405
	list->friends_map_uri = bctbx_mmap_cchar_new();
406
	list->bodyless_subscription = FALSE;
407 408 409
	return list;
}

410 411
static void linphone_friend_list_destroy(LinphoneFriendList *list) {
	if (list->display_name != NULL) ms_free(list->display_name);
412
	if (list->rls_addr) linphone_address_unref(list->rls_addr);
413
	if (list->rls_uri != NULL) ms_free(list->rls_uri);
414
	if (list->content_digest != NULL) ms_free(list->content_digest);
415 416 417
	if (list->event != NULL) {
		linphone_event_terminate(list->event);
		linphone_event_unref(list->event);
418
		list->event = NULL;
419
	}
420 421
	if (list->uri != NULL) ms_free(list->uri);
	if (list->cbs) linphone_friend_list_cbs_unref(list->cbs);
422 423
	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
424
	if (list->friends_map) bctbx_mmap_cchar_delete_with_data(list->friends_map, (void (*)(void *))linphone_friend_unref);
425
	if (list->friends_map_uri) bctbx_mmap_cchar_delete_with_data(list->friends_map_uri, (void (*)(void *))linphone_friend_unref);
426 427 428 429 430 431 432 433
}

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
434
	FALSE
435 436 437
);


438 439 440
LinphoneFriendList * linphone_core_create_friend_list(LinphoneCore *lc) {
	LinphoneFriendList *list = linphone_friend_list_new();
	list->lc = lc;
441 442 443 444 445 446 447 448
	return list;
}

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

449 450 451
void _linphone_friend_list_release(LinphoneFriendList *list){
	/*drops all references to core and unref*/
	list->lc = NULL;
Simon Morlat's avatar
Simon Morlat committed
452 453 454 455
	if (list->event != NULL) {
		linphone_event_unref(list->event);
		list->event = NULL;
	}
456 457 458 459
	if (list->cbs) {
		linphone_friend_list_cbs_unref(list->cbs);
		list->cbs = NULL;
	}
460
	if (list->dirty_friends_to_update) {
461
		list->dirty_friends_to_update = bctbx_list_free_with_data(list->dirty_friends_to_update, (void (*)(void *))linphone_friend_unref);
462 463
	}
	if (list->friends) {
464
		list->friends = bctbx_list_free_with_data(list->friends, (void (*)(void *))_linphone_friend_release);
465
	}
466
	linphone_friend_list_unref(list);
467 468
}

469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491
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);
492
		linphone_core_store_friends_list_in_db(list->lc, list);
493 494 495
	}
}

496 497
const LinphoneAddress * linphone_friend_list_get_rls_address(const LinphoneFriendList *list){
	return list->rls_addr;
498
}
499 500 501
const LinphoneAddress * _linphone_friend_list_get_rls_address(const LinphoneFriendList *list) {
	if (list->rls_addr)
		return list->rls_addr;
502 503 504 505
	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);
506

507
		list->lc->default_rls_addr=NULL;
508

509 510 511 512
		if (rls_uri) {
			/*to make sure changes in config are used if any*/
			list->lc->default_rls_addr = linphone_address_new(rls_uri);
		}
513

514
		return list->lc->default_rls_addr;
515
	}
516 517 518
	else
		return NULL;
}
519 520
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;
521

522 523 524 525 526
	if (list->rls_addr){
		linphone_address_unref(list->rls_addr);
	}
	list->rls_addr = new_rls_addr;
	if (list->rls_uri != NULL){
527 528 529
		ms_free(list->rls_uri);
		list->rls_uri = NULL;
	}
530 531
	if (list->rls_addr){
		list->rls_uri = linphone_address_as_string(list->rls_addr);
532
		linphone_core_store_friends_list_in_db(list->lc, list);
533 534 535
	}
}

536 537 538 539 540 541 542
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
543
	if (addr) linphone_address_unref(addr);
544 545
}

546
static LinphoneFriendListStatus _linphone_friend_list_add_friend(LinphoneFriendList *list, LinphoneFriend *lf, bool_t synchronize) {
547
	LinphoneFriendListStatus status = LinphoneFriendListInvalidFriend;
548
	const LinphoneAddress *addr;
549

550
	if (!list || lf->friend_list) {
Sylvain Berfini's avatar
Sylvain Berfini committed
551 552
		if (!list)
			ms_error("linphone_friend_list_add_friend(): invalid list, null");
553
		if (lf->friend_list)
554
			ms_error("linphone_friend_list_add_friend(): invalid friend, already in list");
555
		return status;
Simon Morlat's avatar
Simon Morlat committed
556
	}
557
	addr = linphone_friend_get_address(lf);
Benjamin REIS's avatar
Benjamin REIS committed
558
	bool_t present = FALSE;
Benjamin REIS's avatar
Benjamin REIS committed
559
	if (lf->refkey) {
Benjamin REIS's avatar
Benjamin REIS committed
560
		present = linphone_friend_list_find_friend_by_ref_key(list, lf->refkey) != NULL;
Benjamin REIS's avatar
Benjamin REIS committed
561
	} else {
Benjamin REIS's avatar
Benjamin REIS committed
562
		present = bctbx_list_find(list->friends, lf) != NULL;
Benjamin REIS's avatar
Benjamin REIS committed
563
	}
Benjamin REIS's avatar
Benjamin REIS committed
564
	if (present) {
565 566 567
		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
568
		if (tmp) ms_free(tmp);
569
	} else {
570
		status = linphone_friend_list_import_friend(list, lf, synchronize);
571
		linphone_friend_save(lf, lf->lc);
572
	}
573

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

577
	return status;
578 579
}

580 581 582 583 584 585 586 587
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);
}

588
LinphoneFriendListStatus linphone_friend_list_import_friend(LinphoneFriendList *list, LinphoneFriend *lf, bool_t synchronize) {
589 590 591
	bctbx_list_t *iterator;
	bctbx_list_t *phone_numbers;
	const bctbx_list_t *addresses;
592
	if (lf->friend_list) {
593 594
		if (lf->friend_list)
			ms_error("linphone_friend_list_add_friend(): invalid friend, already in list");
Sylvain Berfini's avatar
Sylvain Berfini committed
595 596
		return LinphoneFriendListInvalidFriend;
	}
597
	lf->friend_list = list;
598
	lf->lc = list->lc;
Benjamin REIS's avatar
Benjamin REIS committed
599
	list->friends = bctbx_list_prepend(list->friends, linphone_friend_ref(lf));
600 601 602 603
	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);
	}
604 605 606 607 608 609 610 611 612 613 614 615

	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);
	}
616
	bctbx_list_free(phone_numbers);
617 618 619 620 621 622 623 624 625 626 627 628 629 630

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

631
	if (synchronize) {
Benjamin REIS's avatar
Benjamin REIS committed
632
		list->dirty_friends_to_update = bctbx_list_prepend(list->dirty_friends_to_update, linphone_friend_ref(lf));
633
	}
634 635 636
	return LinphoneFriendListOK;
}

637
static void carddav_done(LinphoneCardDavContext *cdc, bool_t success, const char *msg) {
638
	if (cdc && cdc->friend_list->cbs && cdc->friend_list->cbs->sync_state_changed_cb) {
639 640
		cdc->friend_list->cbs->sync_state_changed_cb(cdc->friend_list, success ? LinphoneFriendListSyncSuccessful : LinphoneFriendListSyncFailure, msg);
	}
641 642 643
	linphone_carddav_context_destroy(cdc);
}

644
static LinphoneFriendListStatus _linphone_friend_list_remove_friend(LinphoneFriendList *list, LinphoneFriend *lf, bool_t remove_from_server) {
645 646 647
	bctbx_list_t *iterator;
	bctbx_list_t *phone_numbers;
	const bctbx_list_t *addresses;
648
	bctbx_list_t *elem = bctbx_list_find(list->friends, lf);
649
	if (elem == NULL) return LinphoneFriendListNonExistentFriend;
650

651 652 653
	if (lf && lf->lc && lf->lc->friends_db) {
		linphone_core_remove_friend_from_db(lf->lc, lf);
	}
654

655
	if (remove_from_server) {
656
		LinphoneVcard *lvc = linphone_friend_get_vcard(lf);
657 658
		if (lvc && linphone_vcard_get_uid(lvc)) {
			LinphoneCardDavContext *cdc = linphone_carddav_context_new(list);
659 660 661 662 663 664
			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);
665 666
			}
		}
667
	}
668
	list->friends = bctbx_list_erase_link(list->friends, elem);
669 670
	if(lf->refkey) {
		bctbx_iterator_t * it = bctbx_map_cchar_find_key(list->friends_map, lf->refkey);
671 672
		bctbx_iterator_t * end = bctbx_map_cchar_end(list->friends_map);
		if (!bctbx_iterator_cchar_equals(it, end)){
673 674 675
			linphone_friend_unref((LinphoneFriend*)bctbx_pair_cchar_get_second(bctbx_iterator_cchar_get_pair(it)));
			bctbx_map_cchar_erase(list->friends_map, it);
		}
676 677
		if (it) bctbx_iterator_cchar_delete(it);
		if (end) bctbx_iterator_cchar_delete(end);
678
	}
679

680 681 682 683 684 685 686
	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);
687 688
			bctbx_iterator_t * end = bctbx_map_cchar_end(list->friends_map_uri);
			if (!bctbx_iterator_cchar_equals(it, end)){
689 690 691
				linphone_friend_unref((LinphoneFriend*)bctbx_pair_cchar_get_second(bctbx_iterator_cchar_get_pair(it)));
				bctbx_map_cchar_erase(list->friends_map_uri, it);
			}
692 693
			if (it) bctbx_iterator_cchar_delete(it);
			if (end) bctbx_iterator_cchar_delete(end);
694 695 696
		}
		iterator = bctbx_list_next(iterator);
	}
697
	if (phone_numbers) bctbx_list_free(phone_numbers);
698 699 700 701 702 703 704 705

	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);
706 707
			bctbx_iterator_t * end = bctbx_map_cchar_end(list->friends_map_uri);
			if (!bctbx_iterator_cchar_equals(it, end)){
708 709 710
				linphone_friend_unref((LinphoneFriend*)bctbx_pair_cchar_get_second(bctbx_iterator_cchar_get_pair(it)));
				bctbx_map_cchar_erase(list->friends_map_uri, it);
			}
711 712
			if (it) bctbx_iterator_cchar_delete(it);
			if (end) bctbx_iterator_cchar_delete(end);
713 714
			ms_free(uri);
		}
715

716 717 718
		iterator = bctbx_list_next(iterator);
	}

719 720
	lf->friend_list = NULL;
	linphone_friend_unref(lf);
721
	return LinphoneFriendListOK;
722
}
723

724 725 726 727
LinphoneFriendListStatus linphone_friend_list_remove_friend(LinphoneFriendList *list, LinphoneFriend *lf) {
	return _linphone_friend_list_remove_friend(list, lf, TRUE);
}

728
const bctbx_list_t * linphone_friend_list_get_friends(const LinphoneFriendList *list) {
729 730 731
	return list->friends;
}

732
void linphone_friend_list_update_dirty_friends(LinphoneFriendList *list) {
733
	bctbx_list_t *dirty_friends = list->dirty_friends_to_update;
734

735 736 737
	while (dirty_friends) {
		LinphoneCardDavContext *cdc = linphone_carddav_context_new(list);
		if (cdc) {
738
			LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(dirty_friends);
739
			cdc->sync_done_cb = carddav_done;
740
			if (lf) {
741 742 743
				if (cdc->friend_list->cbs->sync_state_changed_cb) {
					cdc->friend_list->cbs->sync_state_changed_cb(cdc->friend_list, LinphoneFriendListSyncStarted, NULL);
				}
744 745 746
				linphone_carddav_put_vcard(cdc, lf);
			}
		}
747
		dirty_friends = bctbx_list_next(dirty_friends);
748
	}
749
	list->dirty_friends_to_update = bctbx_list_free_with_data(list->dirty_friends_to_update, (void (*)(void *))linphone_friend_unref);
750 751 752
}

static void carddav_created(LinphoneCardDavContext *cdc, LinphoneFriend *lf) {
753
	if (cdc) {
754
		LinphoneFriendList *lfl = cdc->friend_list;
755
		linphone_friend_list_import_friend(lfl, lf, FALSE);
756
		if (cdc->friend_list->cbs->contact_created_cb) {
757
			cdc->friend_list->cbs->contact_created_cb(lfl, lf);
758
		}
759 760 761 762
	}
}

static void carddav_removed(LinphoneCardDavContext *cdc, LinphoneFriend *lf) {
763
	if (cdc) {
764
		LinphoneFriendList *lfl = cdc->friend_list;
765
		_linphone_friend_list_remove_friend(lfl, lf, FALSE);
766
		if (cdc->friend_list->cbs->contact_deleted_cb) {
767
			cdc->friend_list->cbs->contact_deleted_cb(lfl, lf);
768
		}
769 770 771
	}
}

772
static void carddav_updated(LinphoneCardDavContext *cdc, LinphoneFriend *lf_new, LinphoneFriend *lf_old) {
773 774
	if (cdc) {
		LinphoneFriendList *lfl = cdc->friend_list;
775
		bctbx_list_t *elem = bctbx_list_find(lfl->friends, lf_old);
776
		if (elem) {
777
			elem->data = linphone_friend_ref(lf_new);
778
		}
779
		linphone_core_store_friend_in_db(lf_new->lc, lf_new);
780

781 782 783
		if (cdc->friend_list->cbs->contact_updated_cb) {
			cdc->friend_list->cbs->contact_updated_cb(lfl, lf_new, lf_old);
		}
784
		linphone_friend_unref(lf_old);
785
	}
786 787
}

788
void linphone_friend_list_synchronize_friends_from_server(LinphoneFriendList *list) {
789
	LinphoneCardDavContext *cdc = NULL;
790

791 792 793 794
	if (!list || !list->uri || !list->lc) {
		ms_error("FATAL");
		return;
	}
795

796
	cdc = linphone_carddav_context_new(list);
797 798 799
	if (cdc) {
		cdc->contact_created_cb = carddav_created;
		cdc->contact_removed_cb = carddav_removed;
800
		cdc->contact_updated_cb = carddav_updated;
801
		cdc->sync_done_cb = carddav_done;
802 803 804
		if (cdc && cdc->friend_list->cbs->sync_state_changed_cb) {
			cdc->friend_list->cbs->sync_state_changed_cb(cdc->friend_list, LinphoneFriendListSyncStarted, NULL);
		}
805 806 807 808
		linphone_carddav_synchronize(cdc);
	}
}

809
LinphoneFriend * linphone_friend_list_find_friend_by_address(const LinphoneFriendList *list, const LinphoneAddress *address) {
810
	LinphoneAddress *clean_addr = linphone_address_clone(address);
811
	LinphoneFriend *lf;
812
	if (linphone_address_has_uri_param(clean_addr, "gr")) {
813 814
		linphone_address_remove_uri_param(clean_addr, "gr");
	}
815 816 817
	char *uri = linphone_address_as_string_uri_only(clean_addr);
	lf = linphone_friend_list_find_friend_by_uri(list, uri);
	bctbx_free(uri);
818 819
	linphone_address_unref(clean_addr);
	return lf;
820 821 822
}

LinphoneFriend * linphone_friend_list_find_friend_by_uri(const LinphoneFriendList *list, const char *uri) {
823
	LinphoneFriend *result = NULL;
824 825 826
	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)) {
827 828
		bctbx_pair_t *pair = bctbx_iterator_cchar_get_pair(it);
		result = (LinphoneFriend *)bctbx_pair_cchar_get_second(pair);
829
	}
830
	bctbx_iterator_cchar_delete(end);
831 832
	bctbx_iterator_cchar_delete(it);
	return result;
833 834
}

835 836 837 838 839 840
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)) {
841
			bctbx_pair_t *pair = bctbx_iterator_cchar_get_pair(it);
842
			result = (LinphoneFriend *)bctbx_pair_cchar_get_second(pair);
843
		}
844 845
		bctbx_iterator_cchar_delete(end);
		bctbx_iterator_cchar_delete(it);
846
	}
847
	return result;
848 849
}

850 851 852 853
LinphoneFriend * linphone_friend_list_find_friend_by_inc_subscribe (
	const LinphoneFriendList *list,
	LinphonePrivate::SalOp *op
) {
854
	const bctbx_list_t *elem;
855 856
	for (elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) {
		LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(elem);
857
		if (bctbx_list_find(lf->insubs, op)) return lf;
858 859 860 861
	}
	return NULL;
}

862 863 864 865
LinphoneFriend * linphone_friend_list_find_friend_by_out_subscribe (
	const LinphoneFriendList *list,
	LinphonePrivate::SalOp *op
) {
866
	const bctbx_list_t *elem;
867 868
	for (elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) {
		LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(elem);
869
		if (lf->outsub && ((lf->outsub == op) || lf->outsub->isForkedOf(op))) return lf;
870 871 872 873
	}
	return NULL;
}

874 875
static void linphone_friend_list_close_subscriptions(LinphoneFriendList *list) {
	/* FIXME we should wait until subscription to complete. */
jehan's avatar
jehan committed
876 877
	if (list->event) {
		linphone_event_terminate(list->event);
878 879
		linphone_event_unref(list->event);
		list->event = NULL;
880
	}
881
	bctbx_list_for_each(list->friends, (void (*)(void *))linphone_friend_close_subscriptions);
882 883
}

884
static void _linphone_friend_list_send_list_subscription_with_body(LinphoneFriendList *list, const LinphoneAddress *address) {
885
	char *xml_content = create_resource_list_xml(list);
886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921
	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");
922
		}
923 924 925 926 927 928 929
		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);
930
	}
931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957
	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);
958
	linphone_event_set_user_data(list->event, list);
959 960 961 962 963 964 965 966 967 968 969 970 971 972
}

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

975
void linphone_friend_list_update_subscriptions(LinphoneFriendList *list) {
976
	LinphoneProxyConfig *cfg = NULL;
977
	const LinphoneAddress *address = _linphone_friend_list_get_rls_address(list);
978 979
	bool_t only_when_registered = FALSE;
	bool_t should_send_list_subscribe = FALSE;
980

981 982 983 984 985 986
	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);
	}
987

988
	if (address != NULL) {
989
		if (list->enable_subscriptions) {
990 991 992 993 994 995 996 997 998 999
			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);
1000
				}
1001
			}
1002 1003
		} else {
			ms_message("Friends list [%p] subscription update skipped since subscriptions not enabled yet", list);
1004
		}
1005
	} else if (list->enable_subscriptions) {
1006
		const bctbx_list_t *elem;
1007 1008
		for (elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) {
			LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(elem);
1009
			linphone_friend_update_subscribes(lf, only_when_registered);
1010
		}
1011 1012 1013 1014
	}
}

void linphone_friend_list_invalidate_subscriptions(LinphoneFriendList *list) {
1015
	const bctbx_list_t *elem;
1016 1017 1018 1019 1020 1021 1022 1023

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

1024 1025
	for (elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) {
		LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(elem);
1026
		linphone_friend_invalidate_subscription(lf);
1027 1028 1029 1030
	}
}

void linphone_friend_list_notify_presence(LinphoneFriendList *list, LinphonePresenceModel *presence) {
1031
	const bctbx_list_t *elem;
1032 1033
	for(elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) {
		LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(elem);
1034
		linphone_friend_notify(lf, presence);
1035 1036
	}
}
1037 1038

void linphone_friend_list_notify_presence_received(LinphoneFriendList *list, LinphoneEvent *lev, const LinphoneContent *body) {
1039 1040
	if (!linphone_content_is_multipart(body))
		return;
1041

1042 1043 1044
	LinphoneContent *first_part;
	const char *type = linphone_content_get_type(body);
	const char *subtype = linphone_content_get_subtype(body);
1045

1046 1047 1048 1049
	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;
	}
1050

1051 1052 1053 1054 1055 1056 1057 1058 1059 1060
	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
1061
		linphone_content_unref(first_part);
1062
		return;
1063
	}
1064 1065 1066

	linphone_friend_list_parse_multipart_related_body(list, body, linphone_content_get_string_buffer(first_part));
	linphone_content_unref(first_part);
1067
}
1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079

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);
1080
		linphone_core_store_friends_list_in_db(list->lc, list);
1081 1082 1083
	}
}

1084 1085 1086 1087 1088 1089 1090 1091
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;
}

1092 1093
void linphone_friend_list_update_revision(LinphoneFriendList *list, int rev) {
	list->revision = rev;
1094
	linphone_core_store_friends_list_in_db(list->lc, list);
1095 1096
}

1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107
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);
1108

1109
		if (state == LinphoneSubscriptionOutgoingProgress && linphone_event_get_reason(lev) == LinphoneReasonNoMatch) {
1110
			ms_message("Reseting version count for friend list [%p]",list);
1111 1112 1113 1114
			list->expected_notification_version = 0;
		}
	}
}
1115

1116
LinphoneCore* linphone_friend_list_get_core(const LinphoneFriendList *list) {
1117 1118
	return list->lc;
}
1119

1120
static LinphoneStatus linphone_friend_list_import_friends_from_vcard4(LinphoneFriendList *list, bctbx_list_t *vcards)  {
1121
	bctbx_list_t *vcards_iterator = NULL;
1122
	int count = 0;
1123

1124 1125 1126 1127
	if (!linphone_core_vcard_supported()) {
		ms_error("vCard support wasn't enabled at compilation time");
		return -1;
	}
1128 1129 1130 1131
	if (!list) {
		ms_error("Can't import into a NULL list");
		return -1;
	}
1132

1133
	vcards_iterator = vcards;
1134

1135 1136
	while (vcards_iterator != NULL && bctbx_list_get_data(vcards_iterator) != NULL) {
		LinphoneVcard *vcard = (LinphoneVcard *)bctbx_list_get_data(vcards_iterator);
1137
		LinphoneFriend *lf = linphone_friend_new_from_vcard(vcard);
1138
		linphone_vcard_unref(vcard);
1139 1140
		if (lf) {
			if (LinphoneFriendListOK == linphone_friend_list_import_friend(list, lf, TRUE)) {
1141
				linphone_friend_save(lf, lf->lc);
1142 1143 1144 1145
				count++;
			}
			linphone_friend_unref(lf);
		}
1146
		vcards_iterator = bctbx_list_next(vcards_iterator);
1147
	}
1148
	bctbx_list_free(vcards);
1149
	linphone_core_store_friends_list_in_db(list->lc, list);
1150
	return count;
1151

1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170
}
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);
1171 1172
}

1173
LinphoneStatus linphone_friend_list_import_friends_from_vcard4_buffer(LinphoneFriendList *list, const char *vcard_buffer) {
1174
	bctbx_list_t *vcards = NULL;
1175

1176 1177 1178 1179
	if (!linphone_core_vcard_supported()) {
		ms_error("vCard support wasn't enabled at compilation time");
		return -1;
	}
1180 1181 1182 1183
	if (!list) {
		ms_error("Can't import into a NULL list");
		return -1;
	}
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
1184

1185
	vcards = linphone_vcard_context_get_vcard_list_from_buffer(list->lc->vcard_context, vcard_buffer);
1186 1187 1188 1189
	if (!vcards) {
		ms_error("Failed to parse the buffer");
		return -1;
	}
1190

1191
	return linphone_friend_list_import_friends_from_vcard4(list,vcards);}
1192 1193 1194

void linphone_friend_list_export_friends_as_vcard4_file(LinphoneFriendList *list, const char *vcard_file) {
	FILE *file = NULL;
1195 1196 1197 1198 1199 1200
	const bctbx_list_t *friends;

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

1202
	file = fopen(vcard_file, "wb");
1203 1204 1205 1206
	if (file == NULL) {
		ms_warning("Could not write %s ! Maybe it is read-only. Contacts will not be saved.", vcard_file);
		return;
	}
1207

1208 1209 1210
	friends = linphone_friend_list_get_friends(list);
	while (friends != NULL && bctbx_list_get_data(friends) != NULL) {
		LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(friends);
1211
		LinphoneVcard *vcard = linphone_friend_get_vcard(lf);
1212 1213 1214 1215
		if (vcard) {
			const char *vcard_text = linphone_vcard_as_vcard4_string(vcard);
			fprintf(file, "%s", vcard_text);
		}
1216
		friends = bctbx_list_next(friends);
1217
	}
1218

1219 1220
	fclose(file);
}
1221 1222 1223