friendlist.c 40.5 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
#include "linphone/core.h"
21 22
#include "private.h"

johan's avatar
johan committed
23
#include <bctoolbox/crypto.h>
24

25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
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;
}

59
LinphoneFriendListCbsContactCreatedCb linphone_friend_list_cbs_get_contact_created(const LinphoneFriendListCbs *cbs) {
60 61 62
	return cbs->contact_created_cb;
}

63
void linphone_friend_list_cbs_set_contact_created(LinphoneFriendListCbs *cbs, LinphoneFriendListCbsContactCreatedCb cb) {
64 65 66
	cbs->contact_created_cb = cb;
}

67
LinphoneFriendListCbsContactDeletedCb linphone_friend_list_cbs_get_contact_deleted(const LinphoneFriendListCbs *cbs) {
68 69 70
	return cbs->contact_deleted_cb;
}

71
void linphone_friend_list_cbs_set_contact_deleted(LinphoneFriendListCbs *cbs, LinphoneFriendListCbsContactDeletedCb cb) {
72 73
	cbs->contact_deleted_cb = cb;
}
74

75
LinphoneFriendListCbsContactUpdatedCb linphone_friend_list_cbs_get_contact_updated(const LinphoneFriendListCbs *cbs) {
76 77 78
	return cbs->contact_updated_cb;
}

79
void linphone_friend_list_cbs_set_contact_updated(LinphoneFriendListCbs *cbs, LinphoneFriendListCbsContactUpdatedCb cb) {
80 81 82
	cbs->contact_updated_cb = cb;
}

83
LinphoneFriendListCbsSyncStateChangedCb linphone_friend_list_cbs_get_sync_status_changed(const LinphoneFriendListCbs *cbs) {
84 85 86
	return cbs->sync_state_changed_cb;
}

87
void linphone_friend_list_cbs_set_sync_status_changed(LinphoneFriendListCbs *cbs, LinphoneFriendListCbsSyncStateChangedCb cb) {
88 89 90
	cbs->sync_state_changed_cb = cb;
}

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

137 138 139 140 141 142
static char * create_resource_list_xml(const LinphoneFriendList *list) {
	char *xml_content = NULL;
	xmlBufferPtr buf;
	xmlTextWriterPtr writer;
	int err;

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

	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
169 170 171

	{
		bctbx_list_t* entries = uri_list(list);
172 173
		bctbx_list_t* it;
		for(it = entries; it != NULL; it = it->next){
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
174
			err = add_uri_entry(writer, err, it->data);
175
		}
176
		bctbx_list_free_with_data(entries, ms_free);
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
	}
	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;
}

200 201 202 203 204
static void linphone_friend_list_parse_multipart_related_body(LinphoneFriendList *list, const LinphoneContent *body, const char *first_part_body) {
	xmlparsing_context_t *xml_ctx = linphone_xmlparsing_context_new();
	xmlSetGenericErrorFunc(xml_ctx, linphone_xmlparsing_genericxml_error);
	xml_ctx->doc = xmlReadDoc((const unsigned char*)first_part_body, 0, NULL, 0);
	if (xml_ctx->doc != NULL) {
205
		LinphoneFriend *lf;
206 207
		LinphoneContent *presence_part;
		xmlXPathObjectPtr resource_object;
208 209
		const char *version_str = NULL;
		const char *full_state_str = NULL;
210
		const char *uri = NULL;
211 212
		bool_t full_state = FALSE;
		int version;
213 214 215 216
		int i;

		if (linphone_create_xml_xpath_context(xml_ctx) < 0) goto end;
		xmlXPathRegisterNs(xml_ctx->xpath_ctx, (const xmlChar *)"rlmi", (const xmlChar *)"urn:ietf:params:xml:ns:rlmi");
217 218 219 220 221 222 223

		version_str = linphone_get_xml_attribute_text_content(xml_ctx, "/rlmi:list", "version");
		if (version_str == NULL) {
			ms_warning("rlmi+xml: No version attribute in list");
			goto end;
		}
		version = atoi(version_str);
Ghislain MARY's avatar
Ghislain MARY committed
224
		linphone_free_xml_text_content(version_str);
225 226
		if (version < list->expected_notification_version) {
			ms_warning("rlmi+xml: Discarding received notification with version %d because %d was expected", version, list->expected_notification_version);
227
			linphone_friend_list_update_subscriptions(list); /* Refresh subscription to get new full state notify. */
228 229 230 231 232 233 234 235 236
			goto end;
		}

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

251
		resource_object = linphone_get_xml_xpath_object_for_node_list(xml_ctx, "/rlmi:list/rlmi:resource/rlmi:instance[@state=\"active\"]/..");
252 253
		if ((resource_object != NULL) && (resource_object->nodesetval != NULL)) {
			for (i = 1; i <= resource_object->nodesetval->nodeNr; i++) {
254
				const char *cid = NULL;
255 256
				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");
257 258 259 260 261 262 263 264 265 266
				if (cid != NULL) {
					presence_part = linphone_content_find_part_by_header(body, "Content-Id", cid);
					if (presence_part == NULL) {
						ms_warning("rlmi+xml: Cannot find part with Content-Id: %s", cid);
					} else {
						SalPresenceModel *presence = NULL;
						linphone_notify_parse_presence(linphone_content_get_type(presence_part), linphone_content_get_subtype(presence_part), linphone_content_get_string_buffer(presence_part), &presence);
						if (presence != NULL) {
							// Try to reduce CPU cost of linphone_address_new and find_friend_by_address by only doing it when we know for sure we have a presence to notify
							LinphoneAddress* addr;
267
							uri = linphone_get_xml_text_content(xml_ctx, "./@uri");
268 269 270 271 272 273 274 275
							if (uri == NULL) continue;
							addr = linphone_address_new(uri);
							if (!addr) continue;
							lf = linphone_friend_list_find_friend_by_address(list, addr);
							linphone_address_unref(addr);
							if (lf) {
								const char *phone_number = linphone_friend_sip_uri_to_phone_number(lf, uri);
								lf->presence_received = TRUE;
276 277 278
								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));
Sylvain Berfini's avatar
Sylvain Berfini committed
279 280
									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))){
281
										linphone_friend_unref((LinphoneFriend*)bctbx_pair_cchar_get_second(bctbx_iterator_cchar_get_pair(it)));
Sylvain Berfini's avatar
Sylvain Berfini committed
282
										bctbx_map_cchar_erase(list->friends_map_uri, it);
283
									}
Sylvain Berfini's avatar
Sylvain Berfini committed
284
									bctbx_map_cchar_insert_and_delete(list->friends_map_uri, pair);
285 286 287 288
									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);
								}
289 290 291 292 293 294
								if (full_state == FALSE) {
									if (phone_number)
										linphone_core_notify_notify_presence_received_for_uri_or_tel(list->lc, lf, phone_number, (LinphonePresenceModel *)presence);
									else
										linphone_core_notify_notify_presence_received_for_uri_or_tel(list->lc, lf, uri, (LinphonePresenceModel *)presence);
									linphone_core_notify_notify_presence_received(list->lc, lf);
295
								}
296
								linphone_free_xml_text_content(uri);
297
							}
298
							linphone_content_unref(presence_part);
299 300
						}
					}
301
					linphone_free_xml_text_content(cid);
302 303 304 305
				}
			}
		}
		if (resource_object != NULL) xmlXPathFreeObject(resource_object);
306 307

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

end:
	linphone_xmlparsing_context_destroy(xml_ctx);
}

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

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

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

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
394
	FALSE
395 396 397
);


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

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

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

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

456 457
const LinphoneAddress * linphone_friend_list_get_rls_address(const LinphoneFriendList *list){
	return list->rls_addr;
458
}
jehan's avatar
jehan committed
459 460 461
const LinphoneAddress * _linphone_friend_list_get_rls_address(const LinphoneFriendList *list) {
	if (list->rls_addr)
		return list->rls_addr;
jehan's avatar
jehan committed
462 463 464 465 466 467 468 469 470 471 472 473
	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);
		
		list->lc->default_rls_addr=NULL;
		
		if (rls_uri) {
			/*to make sure changes in config are used if any*/
			list->lc->default_rls_addr = linphone_address_new(rls_uri);
		}
		
jehan's avatar
jehan committed
474
		return list->lc->default_rls_addr;
jehan's avatar
jehan committed
475
	}
jehan's avatar
jehan committed
476 477 478
	else
		return NULL;
}
479 480 481 482 483 484 485 486
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;
	
	if (list->rls_addr){
		linphone_address_unref(list->rls_addr);
	}
	list->rls_addr = new_rls_addr;
	if (list->rls_uri != NULL){
487 488 489
		ms_free(list->rls_uri);
		list->rls_uri = NULL;
	}
490 491
	if (list->rls_addr){
		list->rls_uri = linphone_address_as_string(list->rls_addr);
492
		linphone_core_store_friends_list_in_db(list->lc, list);
493 494 495
	}
}

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

506
static LinphoneFriendListStatus _linphone_friend_list_add_friend(LinphoneFriendList *list, LinphoneFriend *lf, bool_t synchronize) {
Ghislain MARY's avatar
Ghislain MARY committed
507
	LinphoneFriendListStatus status = LinphoneFriendListInvalidFriend;
508
	const LinphoneAddress *addr;
Ghislain MARY's avatar
Ghislain MARY committed
509

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

Ghislain MARY's avatar
Ghislain MARY committed
534 535 536 537 538
	if (list->rls_uri == NULL) {
		/* Mimic the behaviour of linphone_core_add_friend() when a resource list server is not in use */
		linphone_friend_apply(lf, lf->lc);
	}
	return status;
539 540
}

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

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

	phone_numbers = linphone_friend_get_phone_numbers(lf);
	iterator = phone_numbers;
	while (iterator) {
		const char *number = (const char *)bctbx_list_get_data(iterator);
		const char *uri = linphone_friend_phone_number_to_sip_uri(lf, number);
		if(uri) {
			bctbx_pair_t *pair = (bctbx_pair_t*) bctbx_pair_cchar_new(uri, linphone_friend_ref(lf));
			bctbx_map_cchar_insert_and_delete(list->friends_map_uri, pair);
		}
		iterator = bctbx_list_next(iterator);
	}

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

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

597
static void carddav_done(LinphoneCardDavContext *cdc, bool_t success, const char *msg) {
598 599 600
	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);
	}
601 602 603
	linphone_carddav_context_destroy(cdc);
}

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

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

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

	addresses = linphone_friend_get_addresses(lf);
	iterator = (bctbx_list_t *)addresses;
	while (iterator) {
		LinphoneAddress *lfaddr = (LinphoneAddress *)bctbx_list_get_data(iterator);
		char *uri = linphone_address_as_string_uri_only(lfaddr);
		if(uri) {
			bctbx_iterator_t * it = bctbx_map_cchar_find_key(list->friends_map_uri, uri);
			if (!bctbx_iterator_cchar_equals(it, bctbx_map_cchar_end(list->friends_map_uri))){
				linphone_friend_unref((LinphoneFriend*)bctbx_pair_cchar_get_second(bctbx_iterator_cchar_get_pair(it)));
				bctbx_map_cchar_erase(list->friends_map_uri, it);
			}
			bctbx_iterator_cchar_delete(it);
			ms_free(uri);
		}
		iterator = bctbx_list_next(iterator);
	}

674 675
	lf->friend_list = NULL;
	linphone_friend_unref(lf);
676
	return LinphoneFriendListOK;
677
}
678

679 680 681 682
LinphoneFriendListStatus linphone_friend_list_remove_friend(LinphoneFriendList *list, LinphoneFriend *lf) {
	return _linphone_friend_list_remove_friend(list, lf, TRUE);
}

683
const bctbx_list_t * linphone_friend_list_get_friends(const LinphoneFriendList *list) {
684 685 686
	return list->friends;
}

687
void linphone_friend_list_update_dirty_friends(LinphoneFriendList *list) {
688
	bctbx_list_t *dirty_friends = list->dirty_friends_to_update;
689

690 691 692
	while (dirty_friends) {
		LinphoneCardDavContext *cdc = linphone_carddav_context_new(list);
		if (cdc) {
693
			LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(dirty_friends);
694
			cdc->sync_done_cb = carddav_done;
695
			if (lf) {
696 697 698
				if (cdc->friend_list->cbs->sync_state_changed_cb) {
					cdc->friend_list->cbs->sync_state_changed_cb(cdc->friend_list, LinphoneFriendListSyncStarted, NULL);
				}
699 700 701
				linphone_carddav_put_vcard(cdc, lf);
			}
		}
702
		dirty_friends = bctbx_list_next(dirty_friends);
703
	}
704
	list->dirty_friends_to_update = bctbx_list_free_with_data(list->dirty_friends_to_update, (void (*)(void *))linphone_friend_unref);
705 706 707
}

static void carddav_created(LinphoneCardDavContext *cdc, LinphoneFriend *lf) {
708
	if (cdc) {
709
		LinphoneFriendList *lfl = cdc->friend_list;
710
		linphone_friend_list_import_friend(lfl, lf, FALSE);
711
		if (cdc->friend_list->cbs->contact_created_cb) {
712
			cdc->friend_list->cbs->contact_created_cb(lfl, lf);
713
		}
714 715 716 717
	}
}

static void carddav_removed(LinphoneCardDavContext *cdc, LinphoneFriend *lf) {
718
	if (cdc) {
719
		LinphoneFriendList *lfl = cdc->friend_list;
720
		_linphone_friend_list_remove_friend(lfl, lf, FALSE);
721
		if (cdc->friend_list->cbs->contact_deleted_cb) {
722
			cdc->friend_list->cbs->contact_deleted_cb(lfl, lf);
723
		}
724 725 726
	}
}

727
static void carddav_updated(LinphoneCardDavContext *cdc, LinphoneFriend *lf_new, LinphoneFriend *lf_old) {
728 729
	if (cdc) {
		LinphoneFriendList *lfl = cdc->friend_list;
730
		bctbx_list_t *elem = bctbx_list_find(lfl->friends, lf_old);
731
		if (elem) {
732
			elem->data = linphone_friend_ref(lf_new);
733
		}
734
		linphone_core_store_friend_in_db(lf_new->lc, lf_new);
735

736 737 738
		if (cdc->friend_list->cbs->contact_updated_cb) {
			cdc->friend_list->cbs->contact_updated_cb(lfl, lf_new, lf_old);
		}
739
		linphone_friend_unref(lf_old);
740
	}
741 742
}

743
void linphone_friend_list_synchronize_friends_from_server(LinphoneFriendList *list) {
744
	LinphoneCardDavContext *cdc = NULL;
745

746 747 748 749
	if (!list || !list->uri || !list->lc) {
		ms_error("FATAL");
		return;
	}
750

751
	cdc = linphone_carddav_context_new(list);
752 753 754
	if (cdc) {
		cdc->contact_created_cb = carddav_created;
		cdc->contact_removed_cb = carddav_removed;
755
		cdc->contact_updated_cb = carddav_updated;
756
		cdc->sync_done_cb = carddav_done;
757 758 759
		if (cdc && cdc->friend_list->cbs->sync_state_changed_cb) {
			cdc->friend_list->cbs->sync_state_changed_cb(cdc->friend_list, LinphoneFriendListSyncStarted, NULL);
		}
760 761 762 763
		linphone_carddav_synchronize(cdc);
	}
}

764
LinphoneFriend * linphone_friend_list_find_friend_by_address(const LinphoneFriendList *list, const LinphoneAddress *address) {
765
	LinphoneFriend *result = NULL;
766
	char *uri = linphone_address_as_string_uri_only(address);
767
	bctbx_iterator_t* it = bctbx_map_cchar_find_key(list->friends_map_uri, (void*)uri);
768 769 770
	if (!bctbx_iterator_cchar_equals(it, bctbx_map_cchar_end(list->friends_map_uri))) {
		bctbx_pair_t *pair = bctbx_iterator_cchar_get_pair(it);
		result = (LinphoneFriend *)bctbx_pair_cchar_get_second(pair);
771
	}
772
	bctbx_iterator_cchar_delete(it);
773
	ms_free(uri);
774
	return result;
775 776 777
}

LinphoneFriend * linphone_friend_list_find_friend_by_uri(const LinphoneFriendList *list, const char *uri) {
778
	LinphoneFriend *result = NULL;
779
	LinphoneAddress *address = linphone_address_new(uri);
780 781 782 783
	if(address) {
		result = linphone_friend_list_find_friend_by_address(list, address);
		linphone_address_unref(address);
	}
784
	return result;
785 786 787
}

LinphoneFriend * linphone_friend_list_find_friend_by_ref_key(const LinphoneFriendList *list, const char *ref_key) {
788 789 790 791 792 793
	if(list) {
		bctbx_iterator_t* it = bctbx_map_cchar_find_key(list->friends_map, (void*)ref_key);
		if (!bctbx_iterator_cchar_equals(it, bctbx_map_cchar_end(list->friends_map))) {
			bctbx_pair_t *pair = bctbx_iterator_cchar_get_pair(it);
			return (LinphoneFriend *)bctbx_pair_cchar_get_second(pair);
		}
794
	}
795
	return NULL;
796 797 798
}

LinphoneFriend * linphone_friend_list_find_friend_by_inc_subscribe(const LinphoneFriendList *list, SalOp *op) {
799
	const bctbx_list_t *elem;
800 801
	for (elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) {
		LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(elem);
802
		if (bctbx_list_find(lf->insubs, op)) return lf;
803 804 805 806 807
	}
	return NULL;
}

LinphoneFriend * linphone_friend_list_find_friend_by_out_subscribe(const LinphoneFriendList *list, SalOp *op) {
808
	const bctbx_list_t *elem;
809 810
	for (elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) {
		LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(elem);
811
		if (lf->outsub && ((lf->outsub == op) || sal_op_is_forked_of(lf->outsub, op))) return lf;
812 813 814 815
	}
	return NULL;
}

816 817
static void linphone_friend_list_close_subscriptions(LinphoneFriendList *list) {
	/* FIXME we should wait until subscription to complete. */
jehan's avatar
jehan committed
818 819
	if (list->event) {
		linphone_event_terminate(list->event);
820 821
		linphone_event_unref(list->event);
		list->event = NULL;
822
	}
823
	bctbx_list_for_each(list->friends, (void (*)(void *))linphone_friend_close_subscriptions);
824 825
}

826
static void linphone_friend_list_send_list_subscription(LinphoneFriendList *list){
827
	const LinphoneAddress *address = _linphone_friend_list_get_rls_address(list);
828 829 830 831 832 833 834 835 836
	char *xml_content = create_resource_list_xml(list);
	if ((address != NULL) && (xml_content != NULL) && (linphone_friend_list_has_subscribe_inactive(list) == TRUE)) {
		unsigned char digest[16];
		bctbx_md5((unsigned char *)xml_content, strlen(xml_content), digest);
		if ((list->event != NULL) && (list->content_digest != NULL) && (memcmp(list->content_digest, digest, sizeof(digest)) == 0)) {
			/* The content has not changed, only refresh the event. */
			linphone_event_refresh_subscribe(list->event);
		} else {
			LinphoneContent *content;
837
			bctbx_list_t * elem = NULL;
838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861
			int expires = lp_config_get_int(list->lc->config, "sip", "rls_presence_expires", 3600);
			list->expected_notification_version = 0;
			if (list->content_digest != NULL) ms_free(list->content_digest);
			list->content_digest = ms_malloc(sizeof(digest));
			memcpy(list->content_digest, digest, sizeof(digest));
			if (list->event != NULL) {
				linphone_event_terminate(list->event);
				linphone_event_unref(list->event);
			}
			list->event = linphone_core_create_subscribe(list->lc, address, "presence", expires);
			linphone_event_ref(list->event);
			linphone_event_set_internal(list->event, TRUE);
			linphone_event_add_custom_header(list->event, "Require", "recipient-list-subscribe");
			linphone_event_add_custom_header(list->event, "Supported", "eventlist");
			linphone_event_add_custom_header(list->event, "Accept", "multipart/related, application/pidf+xml, application/rlmi+xml");
			linphone_event_add_custom_header(list->event, "Content-Disposition", "recipient-list");
			content = linphone_core_create_content(list->lc);
			linphone_content_set_type(content, "application");
			linphone_content_set_subtype(content, "resource-lists+xml");
			linphone_content_set_string_buffer(content, xml_content);
			if (linphone_core_content_encoding_supported(list->lc, "deflate")) {
				linphone_content_set_encoding(content, "deflate");
				linphone_event_add_custom_header(list->event, "Accept-Encoding", "deflate");
			}
862 863 864 865
			for (elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) {
				LinphoneFriend *lf = (LinphoneFriend *)elem->data;
				lf->subscribe_active = TRUE;
			}
866 867 868 869 870 871 872 873 874 875
			linphone_event_send_subscribe(list->event, content);
			linphone_content_unref(content);
			linphone_event_set_user_data(list->event, list);
		}
	}
	if (xml_content != NULL) ms_free(xml_content);
}

void linphone_friend_list_update_subscriptions(LinphoneFriendList *list){
	LinphoneProxyConfig *cfg = NULL;
jehan's avatar
jehan committed
876
	const LinphoneAddress *address = _linphone_friend_list_get_rls_address(list);
877 878 879 880 881 882 883 884 885 886
	bool_t only_when_registered = FALSE;
	bool_t should_send_list_subscribe = FALSE;
	
	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);
	}
	
jehan's avatar
jehan committed
887
	if (address != NULL) {
888
		if (list->enable_subscriptions) {
889 890 891 892 893 894 895 896 897 898
			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);
899
				}
900
			}
901 902
		} else {
			ms_message("Friends list [%p] subscription update skipped since subscriptions not enabled yet", list);
903
		}
904
	} else if (list->enable_subscriptions) {
905
		const bctbx_list_t *elem;
906 907
		for (elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) {
			LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(elem);
908
			linphone_friend_update_subscribes(lf, only_when_registered);
909
		}
910 911 912 913
	}
}

void linphone_friend_list_invalidate_subscriptions(LinphoneFriendList *list) {
914
	const bctbx_list_t *elem;
915 916 917 918 919 920 921 922

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

923 924
	for (elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) {
		LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(elem);
925
		linphone_friend_invalidate_subscription(lf);
926 927