friendlist.c 44.4 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 639 640
	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);
	}
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
#ifdef SQLITE_STORAGE_ENABLED
652 653 654
	if (lf && lf->lc && lf->lc->friends_db) {
		linphone_core_remove_friend_from_db(lf->lc, lf);
	}
655
#endif
656
	if (remove_from_server) {
657
		LinphoneVcard *lvc = linphone_friend_get_vcard(lf);
658 659
		if (lvc && linphone_vcard_get_uid(lvc)) {
			LinphoneCardDavContext *cdc = linphone_carddav_context_new(list);
660 661 662 663 664 665
			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);
666 667
			}
		}
668
	}
669 670 671
	if (!list->lc->friends_db_file) {
		linphone_core_write_friends_config(list->lc);
	}
672
	list->friends = bctbx_list_erase_link(list->friends, elem);
673 674
	if(lf->refkey) {
		bctbx_iterator_t * it = bctbx_map_cchar_find_key(list->friends_map, lf->refkey);
675 676
		bctbx_iterator_t * end = bctbx_map_cchar_end(list->friends_map);
		if (!bctbx_iterator_cchar_equals(it, end)){
677 678 679
			linphone_friend_unref((LinphoneFriend*)bctbx_pair_cchar_get_second(bctbx_iterator_cchar_get_pair(it)));
			bctbx_map_cchar_erase(list->friends_map, it);
		}
680 681
		if (it) bctbx_iterator_cchar_delete(it);
		if (end) bctbx_iterator_cchar_delete(end);
682
	}
683

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

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

720 721 722
		iterator = bctbx_list_next(iterator);
	}

723 724
	lf->friend_list = NULL;
	linphone_friend_unref(lf);
725
	return LinphoneFriendListOK;
726
}
727

728 729 730 731
LinphoneFriendListStatus linphone_friend_list_remove_friend(LinphoneFriendList *list, LinphoneFriend *lf) {
	return _linphone_friend_list_remove_friend(list, lf, TRUE);
}

732
const bctbx_list_t * linphone_friend_list_get_friends(const LinphoneFriendList *list) {
733 734 735
	return list->friends;
}

736
void linphone_friend_list_update_dirty_friends(LinphoneFriendList *list) {
737
	bctbx_list_t *dirty_friends = list->dirty_friends_to_update;
738

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

static void carddav_created(LinphoneCardDavContext *cdc, LinphoneFriend *lf) {
757
	if (cdc) {
758
		LinphoneFriendList *lfl = cdc->friend_list;
759
		linphone_friend_list_import_friend(lfl, lf, FALSE);
760
		if (cdc->friend_list->cbs->contact_created_cb) {
761
			cdc->friend_list->cbs->contact_created_cb(lfl, lf);
762
		}
763 764 765 766
	}
}

static void carddav_removed(LinphoneCardDavContext *cdc, LinphoneFriend *lf) {
767
	if (cdc) {
768
		LinphoneFriendList *lfl = cdc->friend_list;
769
		_linphone_friend_list_remove_friend(lfl, lf, FALSE);
770
		if (cdc->friend_list->cbs->contact_deleted_cb) {
771
			cdc->friend_list->cbs->contact_deleted_cb(lfl, lf);
772
		}
773 774 775
	}
}

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

785 786 787
		if (cdc->friend_list->cbs->contact_updated_cb) {
			cdc->friend_list->cbs->contact_updated_cb(lfl, lf_new, lf_old);
		}
788
		linphone_friend_unref(lf_old);
789
	}
790 791
}

792
void linphone_friend_list_synchronize_friends_from_server(LinphoneFriendList *list) {
793
	LinphoneCardDavContext *cdc = NULL;
794

795 796 797 798
	if (!list || !list->uri || !list->lc) {
		ms_error("FATAL");
		return;
	}
799

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

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

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

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

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

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

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

888
static void _linphone_friend_list_send_list_subscription_with_body(LinphoneFriendList *list, const LinphoneAddress *address) {
889
	char *xml_content = create_resource_list_xml(list);
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 922 923 924 925
	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");
926
		}
927 928 929 930 931 932 933
		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);
934
	}
935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961
	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);
962
	linphone_event_set_user_data(list->event, list);
963 964 965 966 967 968 969 970 971 972 973 974 975 976
}

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

979
void linphone_friend_list_update_subscriptions(LinphoneFriendList *list) {
980
	LinphoneProxyConfig *cfg = NULL;
981
	const LinphoneAddress *address = _linphone_friend_list_get_rls_address(list);
982 983
	bool_t only_when_registered = FALSE;
	bool_t should_send_list_subscribe = FALSE;
984

985 986 987 988 989 990
	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);
	}
991

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

void linphone_friend_list_invalidate_subscriptions(LinphoneFriendList *list) {
1019
	const bctbx_list_t *elem;
1020 1021 1022 1023 1024 1025 1026 1027

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

1028 1029
	for (elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) {
		LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(elem);
1030
		linphone_friend_invalidate_subscription(lf);
1031 1032 1033 1034
	}
}

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

void linphone_friend_list_notify_presence_received(LinphoneFriendList *list, LinphoneEvent *lev, const LinphoneContent *body) {
1043 1044
	if (!linphone_content_is_multipart(body))
		return;
1045

1046 1047 1048
	LinphoneContent *first_part;
	const char *type = linphone_content_get_type(body);
	const char *subtype = linphone_content_get_subtype(body);
1049

1050 1051 1052 1053
	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;
	}
1054

1055 1056 1057 1058 1059 1060 1061 1062 1063 1064
	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
1065
		linphone_content_unref(first_part);
1066
		return;
1067
	}
1068 1069 1070

	linphone_friend_list_parse_multipart_related_body(list, body, linphone_content_get_string_buffer(first_part));
	linphone_content_unref(first_part);
1071
}
1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083

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);
1084
		linphone_core_store_friends_list_in_db(list->lc, list);
1085 1086 1087
	}
}

1088 1089 1090 1091 1092 1093 1094 1095
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;
}

1096 1097
void linphone_friend_list_update_revision(LinphoneFriendList *list, int rev) {
	list->revision = rev;
1098
	linphone_core_store_friends_list_in_db(list->lc, list);
1099 1100
}

1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111
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);
1112

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

1120
LinphoneCore* linphone_friend_list_get_core(const LinphoneFriendList *list) {
1121 1122
	return list->lc;
}
1123

1124
static LinphoneStatus linphone_friend_list_import_friends_from_vcard4(LinphoneFriendList *list, bctbx_list_t *vcards)  {
1125
	bctbx_list_t *vcards_iterator = NULL;
1126
	int count = 0;
1127

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

1137
	vcards_iterator = vcards;
1138

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

1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174
}
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);
1175 1176
}

1177
LinphoneStatus linphone_friend_list_import_friends_from_vcard4_buffer(LinphoneFriendList *list, const char *vcard_buffer) {
1178
	bctbx_list_t *vcards = NULL;
1179

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

1189
	vcards = linphone_vcard_context_get_vcard_list_from_buffer(list->lc->vcard_context, vcard_buffer);
1190 1191 1192 1193
	if (!vcards) {
		ms_error("Failed to parse the buffer");
		return -1;
	}
1194

1195
	return linphone_friend_list_import_friends_from_vcard4(list,vcards);}
1196 1197 1198

void linphone_friend_list_export_friends_as_vcard4_file(LinphoneFriendList *list, const char *vcard_file) {
	FILE *file = NULL;
1199 1200 1201 1202 1203 1204
	const bctbx_list_t *friends;

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

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

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