friend.c 60.4 KB
Newer Older
Simon Morlat's avatar
Simon Morlat committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/*
 * Copyright (c) 2010-2019 Belledonne Communications SARL.
 *
 * This file is part of Liblinphone.
 *
 * 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 3 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.
aymeric's avatar
aymeric committed
15
 *
Simon Morlat's avatar
Simon Morlat committed
16 17 18
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
aymeric's avatar
aymeric committed
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35

/*
 *  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 Library 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
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

36 37
#include "linphone/core.h"
#include "linphone/lpconfig.h"
aymeric's avatar
aymeric committed
38

Ronan's avatar
Ronan committed
39 40 41 42 43
#if !defined(_WIN32) && !defined(__ANDROID__) && !defined(__QNXNTO__)
	#include <langinfo.h>
	#include <iconv.h>
	#include <string.h>
#endif // if !defined(_WIN32) && !defined(__ANDROID__) && !defined(__QNXNTO__)
44 45 46

#define MAX_PATH_SIZE 1024

47
#include "c-wrapper/c-wrapper.h"
Ronan's avatar
Ronan committed
48 49
#include "core/core-p.h"
#include "db/main-db.h"
50

51 52 53
// TODO: From coreapi. Remove me later.
#include "private.h"

54
using namespace std;
Ronan's avatar
Ronan committed
55

56
using namespace LinphonePrivate;
57

aymeric's avatar
aymeric committed
58 59 60
const char *linphone_online_status_to_string(LinphoneOnlineStatus ss){
	const char *str=NULL;
	switch(ss){
61
		case LinphoneStatusOnline:
62
		str="Online";
aymeric's avatar
aymeric committed
63
		break;
64
		case LinphoneStatusBusy:
65
		str="Busy";
aymeric's avatar
aymeric committed
66
		break;
67
		case LinphoneStatusBeRightBack:
68
		str="Be right back";
aymeric's avatar
aymeric committed
69
		break;
70
		case LinphoneStatusAway:
71
		str="Away";
aymeric's avatar
aymeric committed
72
		break;
73
		case LinphoneStatusOnThePhone:
74
		str="On the phone";
aymeric's avatar
aymeric committed
75
		break;
76
		case LinphoneStatusOutToLunch:
77
		str="Out to lunch";
aymeric's avatar
aymeric committed
78
		break;
79
		case LinphoneStatusDoNotDisturb:
80
		str="Do not disturb";
aymeric's avatar
aymeric committed
81
		break;
82
		case LinphoneStatusMoved:
83
		str="Moved";
aymeric's avatar
aymeric committed
84
		break;
85
		case LinphoneStatusAltService:
86
		str="Using another messaging service";
aymeric's avatar
aymeric committed
87
		break;
88
		case LinphoneStatusOffline:
89
		str="Offline";
aymeric's avatar
aymeric committed
90
		break;
91
		case LinphoneStatusPending:
92
		str="Pending";
aymeric's avatar
aymeric committed
93
		break;
94
		case LinphoneStatusVacation:
95
		str="Vacation";
François Grisez's avatar
François Grisez committed
96
		break;
aymeric's avatar
aymeric committed
97
		default:
98
		str="Unknown status";
aymeric's avatar
aymeric committed
99 100 101 102
	}
	return str;
}

103 104 105
static int friend_compare(const void * a, const void * b) {
	LinphoneFriend *lfa = (LinphoneFriend *)a;
	LinphoneFriend *lfb = (LinphoneFriend *)b;
106 107 108 109
	const bctbx_list_t *addressesa = linphone_friend_get_addresses(lfa);
	const bctbx_list_t *addressesb = linphone_friend_get_addresses(lfb);
	bctbx_list_t *iteratora = (bctbx_list_t *)addressesa;
	bctbx_list_t *iteratorb = (bctbx_list_t *)addressesb;
110 111 112 113 114 115 116 117 118 119 120 121 122
	int ret = 1;

	while (iteratora && (ret == 1)) {
		LinphoneAddress *fa = (LinphoneAddress *)bctbx_list_get_data(iteratora);
		while (iteratorb && (ret == 1)) {
			LinphoneAddress *fb = (LinphoneAddress *)bctbx_list_get_data(iteratorb);
			if (linphone_address_weak_equal(fa, fb)) ret = 0;
			iteratorb = bctbx_list_next(iteratorb);
		}
		iteratora = bctbx_list_next(iteratora);
	}

	return ret;
aymeric's avatar
aymeric committed
123 124
}

125
static LinphoneFriendPresence * find_presence_model_for_uri_or_tel(const LinphoneFriend *lf, const char *uri_or_tel) {
126 127
	bctbx_list_t *iterator = NULL;
	LinphoneAddress *uri_or_tel_addr = NULL;
128
	LinphoneFriendPresence *result=NULL;
129 130 131 132
	if (!lf->lc) {
		ms_warning("Cannot find uri of tel [%s] from friend [%p] because not associated to any Linphone core object",uri_or_tel,lf);
		return NULL;
	}
133 134 135 136
	if ((iterator = lf->presence_models) == NULL) {
		/*no need to move forward, just reutn to avoid useless uri parsing*/
		return NULL;
	};
137

138 139
	uri_or_tel_addr = linphone_core_interpret_url(lf->lc, uri_or_tel);

140
	while (uri_or_tel_addr && iterator) {
141
		LinphoneFriendPresence *lfp = (LinphoneFriendPresence *)bctbx_list_get_data(iterator);
142 143 144 145 146 147 148 149 150
		LinphoneAddress *lfp_addr = linphone_core_interpret_url(lf->lc, lfp->uri_or_tel);
		if (lfp_addr && linphone_address_weak_equal(uri_or_tel_addr, lfp_addr)) {
			result = lfp;
		}
		if (lfp_addr) linphone_address_unref(lfp_addr);
		if (result == NULL)
			iterator = bctbx_list_next(iterator);
		else
			break;
151
	}
152 153
	if (uri_or_tel_addr) linphone_address_unref(uri_or_tel_addr);
	return result;
154 155 156
}

static void add_presence_model_for_uri_or_tel(LinphoneFriend *lf, const char *uri_or_tel, LinphonePresenceModel *presence) {
157
	LinphoneFriendPresence *lfp = ms_new0(LinphoneFriendPresence, 1);
158
	lfp->uri_or_tel = ms_strdup(uri_or_tel);
159
	lfp->presence = linphone_presence_model_ref(presence);
160 161 162 163 164 165
	lf->presence_models = bctbx_list_append(lf->presence_models, lfp);
}

static void free_friend_presence(LinphoneFriendPresence *lfp) {
	ms_free(lfp->uri_or_tel);
	if (lfp->presence) linphone_presence_model_unref(lfp->presence);
166
	ms_free(lfp);
167 168
}

169 170 171
static void free_phone_number_sip_uri(LinphoneFriendPhoneNumberSipUri *lfpnsu) {
	ms_free(lfpnsu->number);
	ms_free(lfpnsu->uri);
172
	ms_free(lfpnsu);
173 174
}

175

aymeric's avatar
aymeric committed
176

177 178
bctbx_list_t *linphone_find_friend_by_address(bctbx_list_t *fl, const LinphoneAddress *addr, LinphoneFriend **lf){
	bctbx_list_t *res=NULL;
aymeric's avatar
aymeric committed
179 180
	LinphoneFriend dummy;
	if (lf!=NULL) *lf=NULL;
181
	memset(&dummy, 0, sizeof(LinphoneFriend));
182
	dummy.uri=(LinphoneAddress*)addr;
183
	res=bctbx_list_find_custom(fl,friend_compare,&dummy);
184
	if (lf!=NULL && res!=NULL) *lf=(LinphoneFriend*)bctbx_list_get_data(res);
aymeric's avatar
aymeric committed
185 186 187 188
	return res;
}

void __linphone_friend_do_subscribe(LinphoneFriend *fr){
Simon Morlat's avatar
Simon Morlat committed
189
	LinphoneCore *lc=fr->lc;
190
	const LinphoneAddress *addr = linphone_friend_get_address(fr);
191

192 193 194 195 196 197 198 199 200
	if (addr != NULL) {
		if (fr->outsub==NULL){
			/* people for which we don't have yet an answer should appear as offline */
			fr->presence_models = bctbx_list_free_with_data(fr->presence_models, (bctbx_list_free_func)free_friend_presence);
			/*
			if (fr->lc->vtable.notify_recv)
				fr->lc->vtable.notify_recv(fr->lc,(LinphoneFriend*)fr);
			*/
		}else{
201
			fr->outsub->release();
202 203
			fr->outsub=NULL;
		}
204
		fr->outsub=new SalPresenceOp(lc->sal);
205
		linphone_configure_op(lc,fr->outsub,addr,NULL,TRUE);
Ghislain MARY's avatar
Ghislain MARY committed
206
		fr->outsub->subscribe(lp_config_get_int(lc->config,"sip","subscribe_expires",600));
207
		fr->subscribe_active=TRUE;
208 209
	} else {
		ms_error("Can't send a SUBSCRIBE for friend [%p] without an address!", fr);
210
	}
aymeric's avatar
aymeric committed
211 212
}

213
LinphoneFriend * linphone_friend_new(void){
214 215 216
	LinphoneFriend *obj = belle_sip_object_new(LinphoneFriend);
	obj->pol = LinphoneSPAccept;
	obj->subscribe = TRUE;
217
	obj->vcard = NULL;
218
	obj->storage_id = 0;
219
	obj->rc_index = -1;
220
	return obj;
aymeric's avatar
aymeric committed
221 222
}

223 224 225 226 227 228 229 230
#if __clang__ || ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4)
#pragma GCC diagnostic push
#endif
#ifdef _MSC_VER
#pragma warning(disable : 4996)
#else
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
Ghislain MARY's avatar
Ghislain MARY committed
231
LinphoneFriend *linphone_friend_new_with_address(const char *addr){
jehan's avatar
jehan committed
232
	LinphoneAddress* linphone_address = linphone_address_new(addr);
Sylvain Berfini's avatar
Sylvain Berfini committed
233 234
	LinphoneFriend *fr;

jehan's avatar
jehan committed
235 236 237 238
	if (linphone_address == NULL) {
		ms_error("Cannot create friend for address [%s]",addr?addr:"null");
		return NULL;
	}
Sylvain Berfini's avatar
Sylvain Berfini committed
239
	fr=linphone_friend_new();
Ghislain MARY's avatar
Ghislain MARY committed
240
	linphone_friend_set_address(fr,linphone_address);
241
	linphone_address_unref(linphone_address);
aymeric's avatar
aymeric committed
242 243
	return fr;
}
244 245 246
#if __clang__ || ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4)
#pragma GCC diagnostic pop
#endif
aymeric's avatar
aymeric committed
247

Simon Morlat's avatar
Simon Morlat committed
248
void linphone_friend_set_user_data(LinphoneFriend *lf, void *data){
249
	lf->user_data=data;
Simon Morlat's avatar
Simon Morlat committed
250 251 252
}

void* linphone_friend_get_user_data(const LinphoneFriend *lf){
253
	return lf->user_data;
Simon Morlat's avatar
Simon Morlat committed
254 255
}

256 257
bool_t linphone_friend_in_list(const LinphoneFriend *lf) {
	return lf->friend_list != NULL;
258 259
}

smorlat's avatar
smorlat committed
260
void linphone_core_interpret_friend_uri(LinphoneCore *lc, const char *uri, char **result){
261
	LinphoneAddress *fr=NULL;
262
	*result=NULL;
263
	fr=linphone_address_new(uri);
264
	if (fr==NULL){
smorlat's avatar
smorlat committed
265 266
		char *tmp=NULL;
		if (strchr(uri,'@')!=NULL){
267
			LinphoneAddress *u;
smorlat's avatar
smorlat committed
268 269
			/*try adding sip:*/
			tmp=ms_strdup_printf("sip:%s",uri);
270
			u=linphone_address_new(tmp);
271 272 273
			if (u!=NULL){
				*result=tmp;
			}
smorlat's avatar
smorlat committed
274 275
		}else if (lc->default_proxy!=NULL){
			/*try adding domain part from default current proxy*/
276
			LinphoneAddress * id=linphone_address_new(linphone_core_get_identity(lc));
277
			if ((id!=NULL) && (uri[0] != '\0')){
278
				linphone_address_set_display_name(id,NULL);
279 280
				linphone_address_set_username(id,uri);
				*result=linphone_address_as_string(id);
281
				linphone_address_unref(id);
smorlat's avatar
smorlat committed
282 283
			}
		}
284
		if (*result){
smorlat's avatar
smorlat committed
285
			/*looks good */
286 287 288 289
			ms_message("%s interpreted as %s",uri,*result);
		}else{
			ms_warning("Fail to interpret friend uri %s",uri);
		}
jehan's avatar
jehan committed
290 291
	}else {
		*result=linphone_address_as_string(fr);
292
		linphone_address_unref(fr);
jehan's avatar
jehan committed
293
	}
smorlat's avatar
smorlat committed
294
}
aymeric's avatar
aymeric committed
295

296
const LinphoneAddress * linphone_friend_get_address(const LinphoneFriend *lf) {
297 298
	if (linphone_core_vcard_supported()) {
		if (lf->vcard) {
299
			const bctbx_list_t *sip_addresses = linphone_vcard_get_sip_addresses(lf->vcard);
300
			if (sip_addresses) {
301
				LinphoneAddress *addr = (LinphoneAddress *)bctbx_list_nth_data(sip_addresses, 0);
Ghislain MARY's avatar
Ghislain MARY committed
302
				return addr;
303 304 305
			}
		}
		return NULL;
306
	}
307
	if (lf->uri) return lf->uri;
308
	return NULL;
309 310
}

311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344
static void add_friend_to_list_map_if_not_in_it_yet(LinphoneFriend *lf, const char *uri) {
	if (!lf || !lf->friend_list || !uri || strlen(uri) == 0) return;

	bctbx_iterator_t *it = bctbx_map_cchar_find_key(lf->friend_list->friends_map_uri, uri);
	bctbx_iterator_t *end = bctbx_map_cchar_end(lf->friend_list->friends_map_uri);
	bool_t found = FALSE;

	// Map is sorted, check if next entry matches key otherwise stop
	while (!found && !bctbx_iterator_cchar_equals(it, end)) {
		bctbx_pair_t *pair = bctbx_iterator_cchar_get_pair(it);
		const char *key = bctbx_pair_cchar_get_first(reinterpret_cast<bctbx_pair_cchar_t *>(pair));
		if (!key || strcmp(uri, key) != 0) break;
		LinphoneFriend *lf2 = (LinphoneFriend*) bctbx_pair_cchar_get_second(pair);
		if (lf2 == lf) {
			found = TRUE;
		}
		it = bctbx_iterator_cchar_get_next(it);
	}
	bctbx_iterator_cchar_delete(it);
	bctbx_iterator_cchar_delete(end);

	if (!found) {
		bctbx_pair_t *pair = (bctbx_pair_t*) bctbx_pair_cchar_new(uri, linphone_friend_ref(lf));
		bctbx_map_cchar_insert_and_delete(lf->friend_list->friends_map_uri, pair);
	}
}

static void remove_friend_from_list_map_if_already_in_it(LinphoneFriend *lf, const char *uri) {
	if (!lf || !lf->friend_list || !uri || strlen(uri) == 0) return;

	bctbx_iterator_t *it = bctbx_map_cchar_find_key(lf->friend_list->friends_map_uri, uri);
	bctbx_iterator_t *end = bctbx_map_cchar_end(lf->friend_list->friends_map_uri);

	// Map is sorted, check if next entry matches key otherwise stop
345
	while (!bctbx_iterator_cchar_equals(it, end)) {
346 347 348 349 350
		bctbx_pair_t *pair = bctbx_iterator_cchar_get_pair(it);
		const char *key = bctbx_pair_cchar_get_first(reinterpret_cast<bctbx_pair_cchar_t *>(pair));
		if (!key || strcmp(uri, key) != 0) break;
		LinphoneFriend *lf2 = (LinphoneFriend*) bctbx_pair_cchar_get_second(pair);
		if (lf2 == lf) {
351
			linphone_friend_unref(lf2);
352
			bctbx_map_cchar_erase(lf->friend_list->friends_map_uri, it);
353
			break;
354 355 356 357 358 359 360
		}
		it = bctbx_iterator_cchar_get_next(it);
	}
	bctbx_iterator_cchar_delete(it);
	bctbx_iterator_cchar_delete(end);
}

361
LinphoneStatus linphone_friend_set_address(LinphoneFriend *lf, const LinphoneAddress *addr) {
362
	LinphoneAddress *fr = linphone_address_clone(addr);
363
	char *address;
364
	const LinphoneAddress *mAddr = linphone_friend_get_address(lf);
365
	if (mAddr && lf->friend_list) {
366
		char *mainAddress = linphone_address_as_string_uri_only(mAddr);
367
		remove_friend_from_list_map_if_already_in_it(lf, mainAddress);
368
	}
369
	linphone_address_clean(fr);
370

371
	address = linphone_address_as_string_uri_only(fr);
372 373
	if (lf->friend_list) {
		add_friend_to_list_map_if_not_in_it_yet(lf, address);
374 375
	}

376 377
	if (linphone_core_vcard_supported()) {
		if (!lf->vcard) {
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
378 379
			const char *dpname = linphone_address_get_display_name(fr) ? linphone_address_get_display_name(fr) : linphone_address_get_username(fr);
			linphone_friend_create_vcard(lf, dpname);
380 381 382 383 384 385
		}
		linphone_vcard_edit_main_sip_address(lf->vcard, address);
		linphone_address_unref(fr);
	} else {
		if (lf->uri != NULL) linphone_address_unref(lf->uri);
		lf->uri = fr;
386
	}
387

388
	ms_free(address);
aymeric's avatar
aymeric committed
389 390 391
	return 0;
}

392
void linphone_friend_add_address(LinphoneFriend *lf, const LinphoneAddress *addr) {
393
	LinphoneAddress *fr;
394
	char *uri;
395 396 397 398
	if (!lf || !addr) return;

	fr = linphone_address_clone(addr);
	linphone_address_clean(fr);
399

400
	uri = linphone_address_as_string_uri_only(fr);
401 402
	if (lf->friend_list) {
		add_friend_to_list_map_if_not_in_it_yet(lf, uri);
403
	}
404

405 406
	if (linphone_core_vcard_supported()) {
		if (lf->vcard) {
407
			linphone_vcard_add_sip_address(lf->vcard, uri);
408 409 410 411 412 413
			linphone_address_unref(fr);
		}
	} else {
		if (lf->uri == NULL) lf->uri = fr;
		else linphone_address_unref(fr);
	}
414
	ms_free(uri);
415 416
}

417
const bctbx_list_t* linphone_friend_get_addresses(const LinphoneFriend *lf) {
418
	if (!lf) return NULL;
419

420
	if (linphone_core_vcard_supported()) {
421
		const bctbx_list_t * addresses = linphone_vcard_get_sip_addresses(lf->vcard);
422
		return addresses;
423
	} else {
424
		bctbx_list_t *addresses = NULL;
425
		return lf->uri ? bctbx_list_append(addresses, lf->uri) : NULL;
426 427 428 429
	}
}

void linphone_friend_remove_address(LinphoneFriend *lf, const LinphoneAddress *addr) {
430
	char *address ;
431
	if (!lf || !addr || !lf->vcard) return;
432

433
	address = linphone_address_as_string_uri_only(addr);
434 435
	if (lf->friend_list) {
		remove_friend_from_list_map_if_already_in_it(lf, address);
436 437
	}

438 439
	if (linphone_core_vcard_supported()) {
		linphone_vcard_remove_sip_address(lf->vcard, address);
440
	}
441
	ms_free(address);
442 443 444
}

void linphone_friend_add_phone_number(LinphoneFriend *lf, const char *phone) {
445
	if (!lf || !phone) return;
446

447
	if (lf->friend_list) {
448
		const char *uri = linphone_friend_phone_number_to_sip_uri(lf, phone);
449
		add_friend_to_list_map_if_not_in_it_yet(lf, uri);
450 451
	}

452 453 454 455 456
	if (linphone_core_vcard_supported()) {
		if (!lf->vcard) {
			linphone_friend_create_vcard(lf, phone);
		}
		linphone_vcard_add_phone_number(lf->vcard, phone);
457 458 459
	}
}

Erwan Croze's avatar
Erwan Croze committed
460
bctbx_list_t* linphone_friend_get_phone_numbers(const LinphoneFriend *lf) {
461
	if (!lf || !lf->vcard) return NULL;
462

463 464
	if (linphone_core_vcard_supported()) {
		return linphone_vcard_get_phone_numbers(lf->vcard);
465
	}
466
	return NULL;
467 468 469
}

void linphone_friend_remove_phone_number(LinphoneFriend *lf, const char *phone) {
470
	if (!lf || !phone || !lf->vcard) return;
471

472
	if (lf->friend_list) {
473
		const char *uri = linphone_friend_phone_number_to_sip_uri(lf, phone);
474 475
		if (uri) {
			remove_friend_from_list_map_if_already_in_it(lf, uri);
476 477 478
		}
	}

479 480
	if (linphone_core_vcard_supported()) {
		linphone_vcard_remove_phone_number(lf->vcard, phone);
481 482 483
	}
}

484
LinphoneStatus linphone_friend_set_name(LinphoneFriend *lf, const char *name){
485 486 487 488 489 490 491
	if (linphone_core_vcard_supported()) {
		if (!lf->vcard) linphone_friend_create_vcard(lf, name);
		linphone_vcard_set_full_name(lf->vcard, name);
	} else {
		if (!lf->uri) {
			ms_warning("linphone_friend_set_address() must be called before linphone_friend_set_name() to be able to set display name.");
			return -1;
492
		}
493
		linphone_address_set_display_name(lf->uri, name);
494
	}
aymeric's avatar
aymeric committed
495 496 497
	return 0;
}

498
LinphoneStatus linphone_friend_enable_subscribes(LinphoneFriend *fr, bool_t val){
aymeric's avatar
aymeric committed
499 500 501 502
	fr->subscribe=val;
	return 0;
}

503
LinphoneStatus linphone_friend_set_inc_subscribe_policy(LinphoneFriend *fr, LinphoneSubscribePolicy pol) {
aymeric's avatar
aymeric committed
504 505 506 507
	fr->pol=pol;
	return 0;
}

508
void linphone_friend_notify(LinphoneFriend *lf, LinphonePresenceModel *presence){
509
	bctbx_list_t *elem;
510
	if (lf->insubs){
511
		const LinphoneAddress *addr = linphone_friend_get_address(lf);
512 513 514 515 516
		if (addr) {
			char *addr_str = linphone_address_as_string(addr);
			ms_message("Want to notify %s", addr_str);
			ms_free(addr_str);
		}
517
	}
518
	for(elem=lf->insubs; elem!=NULL; elem=bctbx_list_next(elem)){
519
		auto op = reinterpret_cast<SalPresenceOp *>(bctbx_list_get_data(elem));
520
		op->notifyPresence((SalPresenceModel *)presence);
aymeric's avatar
aymeric committed
521 522 523
	}
}

524
void linphone_friend_add_incoming_subscription(LinphoneFriend *lf, SalOp *op){
525
	/*ownership of the op is transfered from sal to the LinphoneFriend*/
526
	lf->insubs = bctbx_list_append(lf->insubs, op);
527 528 529
}

void linphone_friend_remove_incoming_subscription(LinphoneFriend *lf, SalOp *op){
530
	if (bctbx_list_find(lf->insubs, op)){
531
		op->release();
532
		lf->insubs = bctbx_list_remove(lf->insubs, op);
533
	}
534 535
}

aymeric's avatar
aymeric committed
536
static void linphone_friend_unsubscribe(LinphoneFriend *lf){
537
	if (lf->outsub!=NULL) {
538
		lf->outsub->unsubscribe();
aymeric's avatar
aymeric committed
539
	}
540 541
	/* for friend list there is no necessary outsub*/
	lf->subscribe_active=FALSE;
aymeric's avatar
aymeric committed
542 543
}

544
void linphone_friend_invalidate_subscription(LinphoneFriend *lf){
545
	bctbx_list_t *iterator;
546
	LinphoneCore *lc = lf->lc;
547

Simon Morlat's avatar
Simon Morlat committed
548
	if (lf->outsub!=NULL) {
549
		lf->outsub->release();
Simon Morlat's avatar
Simon Morlat committed
550 551
		lf->outsub=NULL;
	}
552

553 554 555
	// To resend a subscribe on the next network_reachable(TRUE)
	lf->subscribe_active=FALSE;

556
	/* Notify application that we no longer know the presence activity */
557 558 559 560
	iterator = lf->presence_models;
	while (iterator) {
		LinphoneFriendPresence *lfp = (LinphoneFriendPresence *)bctbx_list_get_data(iterator);
		linphone_presence_model_unref(lfp->presence);
561 562
		lfp->presence = linphone_presence_model_new();
		linphone_presence_model_set_basic_status(lfp->presence, LinphonePresenceBasicStatusClosed);
563 564
		linphone_core_notify_notify_presence_received_for_uri_or_tel(lc, lf, lfp->uri_or_tel, lfp->presence);
		iterator = bctbx_list_next(iterator);
565
	}
566 567
	if (bctbx_list_size(lf->presence_models) > 0) {
		// Deprecated
568
		linphone_core_notify_notify_presence_received(lc, lf);
569
	}
570
	lf->initial_subscribes_sent=FALSE;
Simon Morlat's avatar
Simon Morlat committed
571 572
}

573
static void close_presence_notification(SalPresenceOp *op) {
574
	op->notifyPresenceClose();
575 576 577
}

static void release_sal_op(SalOp *op) {
Ronan's avatar
Ronan committed
578
	op->release();
579 580
}

581
static void linphone_friend_close_incoming_subscriptions(LinphoneFriend *lf) {
582 583
	bctbx_list_for_each(lf->insubs, (MSIterateFunc) close_presence_notification);
	lf->insubs = bctbx_list_free_with_data(lf->insubs, (MSIterateFunc)release_sal_op);
584 585
}

586 587 588 589 590
void linphone_friend_close_subscriptions(LinphoneFriend *lf){
	linphone_friend_unsubscribe(lf);
	linphone_friend_close_incoming_subscriptions(lf);
}

591
static void _linphone_friend_release_ops(LinphoneFriend *lf){
592
	lf->insubs = bctbx_list_free_with_data(lf->insubs, (MSIterateFunc) release_sal_op);
593
	if (lf->outsub){
594
		lf->outsub->release();
595 596
		lf->outsub=NULL;
	}
597 598 599 600
}

static void _linphone_friend_destroy(LinphoneFriend *lf){
	_linphone_friend_release_ops(lf);
601
	if (lf->presence_models) bctbx_list_free_with_data(lf->presence_models, (bctbx_list_free_func)free_friend_presence);
602
	if (lf->phone_number_sip_uri_map) bctbx_list_free_with_data(lf->phone_number_sip_uri_map, (bctbx_list_free_func)free_phone_number_sip_uri);
603
	if (lf->uri!=NULL) linphone_address_unref(lf->uri);
604
	if (lf->info!=NULL) buddy_info_free(lf->info);
605
	if (lf->vcard != NULL) linphone_vcard_unref(lf->vcard);
Sylvain Berfini's avatar
Sylvain Berfini committed
606
	if (lf->refkey != NULL) ms_free(lf->refkey);
aymeric's avatar
aymeric committed
607 608
}

609 610 611 612 613 614 615 616 617 618 619
static belle_sip_error_code _linphone_friend_marshall(belle_sip_object_t *obj, char* buff, size_t buff_size, size_t *offset) {
	LinphoneFriend *lf = (LinphoneFriend*)obj;
	belle_sip_error_code err = BELLE_SIP_OK;
	if (lf->uri){
		char *tmp = linphone_address_as_string(lf->uri);
		err = belle_sip_snprintf(buff, buff_size, offset, "%s", tmp);
		ms_free(tmp);
	}
	return err;
}

620
const char * linphone_friend_get_name(const LinphoneFriend *lf) {
621 622 623 624 625 626
	if (!lf) return NULL;

	if (linphone_core_vcard_supported()) {
		if (lf->vcard) return linphone_vcard_get_full_name(lf->vcard);
	} else if (lf->uri) {
		return linphone_address_get_display_name(lf->uri);
627 628
	}
	return NULL;
629 630
}

aymeric's avatar
aymeric committed
631 632 633 634 635 636 637 638 639
bool_t linphone_friend_get_send_subscribe(const LinphoneFriend *lf){
	return lf->subscribe;
}

LinphoneSubscribePolicy linphone_friend_get_inc_subscribe_policy(const LinphoneFriend *lf){
	return lf->pol;
}

LinphoneOnlineStatus linphone_friend_get_status(const LinphoneFriend *lf){
640
	const LinphonePresenceModel *presence = linphone_friend_get_presence_model(lf);
641 642
	LinphoneOnlineStatus online_status = LinphoneStatusOffline;
	LinphonePresenceBasicStatus basic_status = LinphonePresenceBasicStatusClosed;
643
	LinphonePresenceActivity *activity = NULL;
644
	const char *description = NULL;
645 646
	unsigned int nb_activities = 0;

647 648 649
	if (presence != NULL) {
		basic_status = linphone_presence_model_get_basic_status(presence);
		nb_activities = linphone_presence_model_get_nb_activities(presence);
650 651 652
		online_status = (basic_status == LinphonePresenceBasicStatusOpen) ? LinphoneStatusOnline : LinphoneStatusOffline;
		if (nb_activities > 1) {
			char *tmp = NULL;
653
			const LinphoneAddress *addr = linphone_friend_get_address(lf);
654 655
			if (addr) tmp = linphone_address_as_string(addr);
			ms_warning("Friend %s has several activities, get status from the first one", tmp ? tmp : "unknown");
656 657 658
			if (tmp) {
				ms_free(tmp);
			}
659 660 661
			nb_activities = 1;
		}
		if (nb_activities == 1) {
662
			activity = linphone_presence_model_get_activity(presence);
663
			description = linphone_presence_activity_get_description(activity);
664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689
			switch (linphone_presence_activity_get_type(activity)) {
				case LinphonePresenceActivityBreakfast:
				case LinphonePresenceActivityDinner:
				case LinphonePresenceActivityLunch:
				case LinphonePresenceActivityMeal:
					online_status = LinphoneStatusOutToLunch;
					break;
				case LinphonePresenceActivityAppointment:
				case LinphonePresenceActivityMeeting:
				case LinphonePresenceActivityPerformance:
				case LinphonePresenceActivityPresentation:
				case LinphonePresenceActivitySpectator:
				case LinphonePresenceActivityWorking:
				case LinphonePresenceActivityWorship:
					online_status = LinphoneStatusDoNotDisturb;
					break;
				case LinphonePresenceActivityAway:
				case LinphonePresenceActivitySleeping:
					online_status = LinphoneStatusAway;
					break;
				case LinphonePresenceActivityHoliday:
				case LinphonePresenceActivityTravel:
				case LinphonePresenceActivityVacation:
					online_status = LinphoneStatusVacation;
					break;
				case LinphonePresenceActivityBusy:
690 691 692 693 694 695
					if (description && strcmp(description, "Do not disturb") == 0) { // See linphonecore.c linphone_core_set_presence_info() method
						online_status = LinphoneStatusDoNotDisturb;
					} else {
						online_status = LinphoneStatusBusy;
					}
					break;
696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715
				case LinphonePresenceActivityLookingForWork:
				case LinphonePresenceActivityPlaying:
				case LinphonePresenceActivityShopping:
				case LinphonePresenceActivityTV:
					online_status = LinphoneStatusBusy;
					break;
				case LinphonePresenceActivityInTransit:
				case LinphonePresenceActivitySteering:
					online_status = LinphoneStatusBeRightBack;
					break;
				case LinphonePresenceActivityOnThePhone:
					online_status = LinphoneStatusOnThePhone;
					break;
				case LinphonePresenceActivityOther:
				case LinphonePresenceActivityPermanentAbsence:
					online_status = LinphoneStatusMoved;
					break;
				case LinphonePresenceActivityUnknown:
					/* Rely on the basic status information. */
					break;
716 717 718 719 720 721 722
			}
		}
	}

	return online_status;
}

723 724
const LinphonePresenceModel * linphone_friend_get_presence_model(const LinphoneFriend *lf) {
	const LinphonePresenceModel *presence = NULL;
725 726
	LinphoneFriend* const_lf = (LinphoneFriend*)lf;
	const bctbx_list_t* addrs = linphone_friend_get_addresses(const_lf);
727
	bctbx_list_t* phones = NULL;
728
	bctbx_list_t *it;
729

730
	for (it = (bctbx_list_t *)addrs; it!= NULL; it = it->next) {
731
		LinphoneAddress *addr = (LinphoneAddress*)it->data;
732
		char *uri = linphone_address_as_string_uri_only(addr);
733
		presence = linphone_friend_get_presence_model_for_uri_or_tel(const_lf, uri);
734
		ms_free(uri);
735
		if (presence) break;
736
	}
737
	if (presence) return presence;
738

739
	phones = linphone_friend_get_phone_numbers(const_lf);
740
	for (it = phones; it!= NULL; it = it->next) {
Ghislain MARY's avatar
Ghislain MARY committed
741
		presence = linphone_friend_get_presence_model_for_uri_or_tel(const_lf, reinterpret_cast<const char *>(it->data));
742
		if (presence) break;
743
	}
744 745
	bctbx_list_free(phones);
	return presence;
746 747
}

748 749 750 751 752 753
LinphoneConsolidatedPresence linphone_friend_get_consolidated_presence(const LinphoneFriend *lf) {
	const LinphonePresenceModel *model = linphone_friend_get_presence_model(lf);
	if (!model) return LinphoneConsolidatedPresenceOffline;
	return linphone_presence_model_get_consolidated_presence(model);
}

754 755 756 757
const LinphonePresenceModel * linphone_friend_get_presence_model_for_uri_or_tel(const LinphoneFriend *lf, const char *uri_or_tel) {
	LinphoneFriendPresence *lfp = find_presence_model_for_uri_or_tel(lf, uri_or_tel);
	if (lfp) return lfp->presence;
	return NULL;
758 759
}

760
void linphone_friend_set_presence_model(LinphoneFriend *lf, LinphonePresenceModel *presence) {
761
	const LinphoneAddress *addr = linphone_friend_get_address(lf);
762 763 764 765 766 767 768 769 770 771 772
	if (addr) {
		char *uri = linphone_address_as_string_uri_only(addr);
		linphone_friend_set_presence_model_for_uri_or_tel(lf, uri, presence);
		ms_free(uri);
	}
}

void linphone_friend_set_presence_model_for_uri_or_tel(LinphoneFriend *lf, const char *uri_or_tel, LinphonePresenceModel *presence) {
	LinphoneFriendPresence *lfp = find_presence_model_for_uri_or_tel(lf, uri_or_tel);
	if (lfp) {
		if (lfp->presence) linphone_presence_model_unref(lfp->presence);
773
		lfp->presence = linphone_presence_model_ref(presence);
774 775
	} else {
		add_presence_model_for_uri_or_tel(lf, uri_or_tel, presence);
776 777 778
	}
}

779 780 781 782
bool_t linphone_friend_is_presence_received(const LinphoneFriend *lf) {
	return lf->presence_received;
}

783 784 785
BuddyInfo * linphone_friend_get_info(const LinphoneFriend *lf){
	return lf->info;
}
aymeric's avatar
aymeric committed
786

787
/*
788
 * updates the p2p subscriptions.
789 790 791 792
 * If only_when_registered is TRUE, subscribe will be sent only if the friend's corresponding proxy config is in registered.
 * Otherwise if the proxy config goes to unregistered state, the subscription refresh will be suspended.
 * An optional proxy whose state has changed can be passed to optimize the processing.
**/
793
void linphone_friend_update_subscribes(LinphoneFriend *fr, bool_t only_when_registered){
794
	int can_subscribe=1;
795

796
	if (only_when_registered && (fr->subscribe || fr->subscribe_active)){
797
		const LinphoneAddress *addr = linphone_friend_get_address(fr);
798 799 800 801 802 803 804 805 806
		if (addr != NULL) {
			LinphoneProxyConfig *cfg=linphone_core_lookup_known_proxy(fr->lc, addr);
			if (cfg && cfg->state!=LinphoneRegistrationOk){
				char *tmp=linphone_address_as_string(addr);
				ms_message("Friend [%s] belongs to proxy config with identity [%s], but this one isn't registered. Subscription is suspended.",
					tmp,linphone_proxy_config_get_identity(cfg));
				ms_free(tmp);
				can_subscribe=0;
			}
807 808 809
		}
	}
	if (can_subscribe && fr->subscribe && fr->subscribe_active==FALSE){
810
		ms_message("Sending a new SUBSCRIBE for friend [%p]", fr);