linphonecore.c 136 KB
Newer Older
aymeric's avatar
aymeric committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/*
linphone
Copyright (C) 2000  Simon MORLAT (simon.morlat@linphone.org)

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

#include "linphonecore.h"
smorlat's avatar
smorlat committed
21
#include "sipsetup.h"
aymeric's avatar
aymeric committed
22 23
#include "lpconfig.h"
#include "private.h"
24 25

#include <ortp/telephonyevents.h>
26
#include <ortp/zrtp.h>
aymeric's avatar
aymeric committed
27
#include "mediastreamer2/mediastream.h"
28
#include "mediastreamer2/mseventqueue.h"
smorlat's avatar
smorlat committed
29
#include "mediastreamer2/msvolume.h"
30
#include "mediastreamer2/msequalizer.h"
31
#include "mediastreamer2/dtmfgen.h"
aymeric's avatar
aymeric committed
32

33
#ifdef INET6
aymeric's avatar
aymeric committed
34
#ifndef WIN32
35
#include <netdb.h>
aymeric's avatar
aymeric committed
36 37 38
#endif
#endif

39 40 41 42
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

smorlat's avatar
smorlat committed
43
/*#define UNSTANDART_GSM_11K 1*/
aymeric's avatar
aymeric committed
44

45 46
#define ROOT_CA_FILE PACKAGE_DATA_DIR "/linphone/rootca.pem"

aymeric's avatar
aymeric committed
47
static const char *liblinphone_version=LIBLINPHONE_VERSION;
48
static void set_network_reachable(LinphoneCore* lc,bool_t isReachable, time_t curtime);
Simon Morlat's avatar
Simon Morlat committed
49 50
static void linphone_core_run_hooks(LinphoneCore *lc);
static void linphone_core_free_hooks(LinphoneCore *lc);
aymeric's avatar
aymeric committed
51 52

#include "enum.h"
53
const char *linphone_core_get_nat_address_resolved(LinphoneCore *lc);
aymeric's avatar
aymeric committed
54 55 56 57 58 59
void linphone_core_get_local_ip(LinphoneCore *lc, const char *dest, char *result);
static void toggle_video_preview(LinphoneCore *lc, bool_t val);

/* relative path where is stored local ring*/
#define LOCAL_RING "rings/oldphone.wav"
/* same for remote ring (ringback)*/
60
#define REMOTE_RING "ringback.wav"
61 62
#define HOLD_MUSIC "rings/toy-mono.wav"

aymeric's avatar
aymeric committed
63

Simon Morlat's avatar
Simon Morlat committed
64
extern SalCallbacks linphone_sal_callbacks;
aymeric's avatar
aymeric committed
65 66 67 68 69 70 71 72 73 74 75 76

void lc_callback_obj_init(LCCallbackObj *obj,LinphoneCoreCbFunc func,void* ud)
{
  obj->_func=func;
  obj->_user_data=ud;
}

int lc_callback_obj_invoke(LCCallbackObj *obj, LinphoneCore *lc){
	if (obj->_func!=NULL) obj->_func(lc,obj->_user_data);
	return 0;
}

77

aymeric's avatar
aymeric committed
78 79
/*prevent a gcc bug with %c*/
static size_t my_strftime(char *s, size_t max, const char  *fmt,  const struct tm *tm){
Jehan Monnier's avatar
Jehan Monnier committed
80
#if !defined(_WIN32_WCE)
aymeric's avatar
aymeric committed
81
	return strftime(s, max, fmt, tm);
Jehan Monnier's avatar
Jehan Monnier committed
82 83 84 85
#else
	return 0;
	/*FIXME*/
#endif /*_WIN32_WCE*/
aymeric's avatar
aymeric committed
86 87
}

88 89 90 91
static void set_call_log_date(LinphoneCallLog *cl, const struct tm *loctime){
	my_strftime(cl->start_date,sizeof(cl->start_date),"%c",loctime);
}

92
LinphoneCallLog * linphone_call_log_new(LinphoneCall *call, LinphoneAddress *from, LinphoneAddress *to){
aymeric's avatar
aymeric committed
93 94 95 96
	LinphoneCallLog *cl=ms_new0(LinphoneCallLog,1);
	struct tm loctime;
	cl->dir=call->dir;
#ifdef WIN32
Jehan Monnier's avatar
Jehan Monnier committed
97
#if !defined(_WIN32_WCE)
aymeric's avatar
aymeric committed
98
	loctime=*localtime(&call->start_time);
Jehan Monnier's avatar
Jehan Monnier committed
99 100
	/*FIXME*/
#endif /*_WIN32_WCE*/
aymeric's avatar
aymeric committed
101 102 103
#else
	localtime_r(&call->start_time,&loctime);
#endif
104
	set_call_log_date(cl,&loctime);
aymeric's avatar
aymeric committed
105 106
	cl->from=from;
	cl->to=to;
107
    cl->status=LinphoneCallAborted; /*default status*/
aymeric's avatar
aymeric committed
108 109
	return cl;
}
110

111
void call_logs_write_to_config_file(LinphoneCore *lc){
112 113 114 115 116 117
	MSList *elem;
	char logsection[32];
	int i;
	char *tmp;
	LpConfig *cfg=lc->config;

118
	if (linphone_core_get_global_state (lc)==LinphoneGlobalStartup) return;
119

120 121 122 123 124 125 126 127 128 129 130 131 132 133
	for(i=0,elem=lc->call_logs;elem!=NULL;elem=elem->next,++i){
		LinphoneCallLog *cl=(LinphoneCallLog*)elem->data;
		snprintf(logsection,sizeof(logsection),"call_log_%i",i);
		lp_config_set_int(cfg,logsection,"dir",cl->dir);
		lp_config_set_int(cfg,logsection,"status",cl->status);
		tmp=linphone_address_as_string(cl->from);
		lp_config_set_string(cfg,logsection,"from",tmp);
		ms_free(tmp);
		tmp=linphone_address_as_string(cl->to);
		lp_config_set_string(cfg,logsection,"to",tmp);
		ms_free(tmp);
		lp_config_set_string(cfg,logsection,"start_date",cl->start_date);
		lp_config_set_int(cfg,logsection,"duration",cl->duration);
		if (cl->refkey) lp_config_set_string(cfg,logsection,"refkey",cl->refkey);
134
		lp_config_set_float(cfg,logsection,"quality",cl->quality);
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
	}
	for(;i<lc->max_call_logs;++i){
		snprintf(logsection,sizeof(logsection),"call_log_%i",i);
		lp_config_clean_section(cfg,logsection);
	}
}

static void call_logs_read_from_config_file(LinphoneCore *lc){
	char logsection[32];
	int i;
	const char *tmp;
	LpConfig *cfg=lc->config;
	for(i=0;;++i){
		snprintf(logsection,sizeof(logsection),"call_log_%i",i);
		if (lp_config_has_section(cfg,logsection)){
			LinphoneCallLog *cl=ms_new0(LinphoneCallLog,1);
			cl->dir=lp_config_get_int(cfg,logsection,"dir",0);
			cl->status=lp_config_get_int(cfg,logsection,"status",0);
			tmp=lp_config_get_string(cfg,logsection,"from",NULL);
			if (tmp) cl->from=linphone_address_new(tmp);
			tmp=lp_config_get_string(cfg,logsection,"to",NULL);
			if (tmp) cl->to=linphone_address_new(tmp);
			tmp=lp_config_get_string(cfg,logsection,"start_date",NULL);
			if (tmp) strncpy(cl->start_date,tmp,sizeof(cl->start_date));
			cl->duration=lp_config_get_int(cfg,logsection,"duration",0);
			tmp=lp_config_get_string(cfg,logsection,"refkey",NULL);
			if (tmp) cl->refkey=ms_strdup(tmp);
162
			cl->quality=lp_config_get_float(cfg,logsection,"quality",-1);
163
			lc->call_logs=ms_list_append(lc->call_logs,cl);
164
		}else break;
165 166 167 168
	}
}


aymeric's avatar
aymeric committed
169

smorlat's avatar
smorlat committed
170 171 172 173 174 175 176
/**
 * @addtogroup call_logs
 * @{
**/

/**
 * Returns a human readable string describing the call.
177
 *
smorlat's avatar
smorlat committed
178 179
 * @note: the returned char* must be freed by the application (use ms_free()).
**/
aymeric's avatar
aymeric committed
180 181
char * linphone_call_log_to_str(LinphoneCallLog *cl){
	char *status;
182
	char *tmp;
183 184
	char *from=linphone_address_as_string (cl->from);
	char *to=linphone_address_as_string (cl->to);
aymeric's avatar
aymeric committed
185 186 187 188 189 190 191 192 193 194 195 196 197
	switch(cl->status){
		case LinphoneCallAborted:
			status=_("aborted");
			break;
		case LinphoneCallSuccess:
			status=_("completed");
			break;
		case LinphoneCallMissed:
			status=_("missed");
			break;
		default:
			status="unknown";
	}
198
	tmp=ortp_strdup_printf(_("%s at %s\nFrom: %s\nTo: %s\nStatus: %s\nDuration: %i mn %i sec\n"),
aymeric's avatar
aymeric committed
199 200
			(cl->dir==LinphoneCallIncoming) ? _("Incoming call") : _("Outgoing call"),
			cl->start_date,
201 202
			from,
			to,
aymeric's avatar
aymeric committed
203 204 205
			status,
			cl->duration/60,
			cl->duration%60);
206 207 208
	ms_free(from);
	ms_free(to);
	return tmp;
aymeric's avatar
aymeric committed
209 210
}

211 212
/**
 * Returns RTP statistics computed locally regarding the call.
213
 *
214 215 216 217 218 219 220 221 222 223 224 225 226 227
**/
const rtp_stats_t *linphone_call_log_get_local_stats(const LinphoneCallLog *cl){
	return &cl->local_stats;
}

/**
 * Returns RTP statistics computed by remote end and sent back via RTCP.
 *
 * @note Not implemented yet.
**/
const rtp_stats_t *linphone_call_log_get_remote_stats(const LinphoneCallLog *cl){
	return &cl->remote_stats;
}

smorlat's avatar
smorlat committed
228 229 230 231 232 233 234 235
void linphone_call_log_set_user_pointer(LinphoneCallLog *cl, void *up){
	cl->user_pointer=up;
}

void *linphone_call_log_get_user_pointer(const LinphoneCallLog *cl){
	return cl->user_pointer;
}

smorlat's avatar
smorlat committed
236 237


238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
/**
 * Associate a persistent reference key to the call log.
 *
 * The reference key can be for example an id to an external database.
 * It is stored in the config file, thus can survive to process exits/restarts.
 *
**/
void linphone_call_log_set_ref_key(LinphoneCallLog *cl, const char *refkey){
	if (cl->refkey!=NULL){
		ms_free(cl->refkey);
		cl->refkey=NULL;
	}
	if (refkey) cl->refkey=ms_strdup(refkey);
}

/**
 * Get the persistent reference key associated to the call log.
 *
 * The reference key can be for example an id to an external database.
 * It is stored in the config file, thus can survive to process exits/restarts.
 *
**/
const char *linphone_call_log_get_ref_key(const LinphoneCallLog *cl){
	return cl->refkey;
}

smorlat's avatar
smorlat committed
264 265
/** @} */

aymeric's avatar
aymeric committed
266
void linphone_call_log_destroy(LinphoneCallLog *cl){
267 268 269
	if (cl->from!=NULL) linphone_address_destroy(cl->from);
	if (cl->to!=NULL) linphone_address_destroy(cl->to);
	if (cl->refkey!=NULL) ms_free(cl->refkey);
aymeric's avatar
aymeric committed
270 271 272
	ms_free(cl);
}

273 274 275 276 277 278 279 280 281 282 283 284
/**
 * Returns TRUE if the LinphoneCall asked to autoanswer
 *
**/
bool_t linphone_call_asked_to_autoanswer(LinphoneCall *call){
	//return TRUE if the unique(for the moment) incoming call asked to be autoanswered
	if(call)
		return sal_call_autoanswer_asked(call->op);
	else
		return FALSE;
}

285 286
int linphone_core_get_current_call_duration(const LinphoneCore *lc){
	LinphoneCall *call=linphone_core_get_current_call((LinphoneCore *)lc);
287 288
	if (call)  return linphone_call_get_duration(call);
	return -1;
289 290 291 292
}

const LinphoneAddress *linphone_core_get_current_call_remote_address(struct _LinphoneCore *lc){
	LinphoneCall *call=linphone_core_get_current_call(lc);
Simon Morlat's avatar
Simon Morlat committed
293
	if (call==NULL) return NULL;
294
	return linphone_call_get_remote_address(call);
smorlat's avatar
smorlat committed
295 296
}

297 298 299 300 301 302
/**
 * Enable logs in supplied FILE*.
 *
 * @ingroup misc
 *
 * @param file a C FILE* where to fprintf logs. If null stdout is used.
303
 *
304
**/
aymeric's avatar
aymeric committed
305 306 307 308 309 310
void linphone_core_enable_logs(FILE *file){
	if (file==NULL) file=stdout;
	ortp_set_log_file(file);
	ortp_set_log_level_mask(ORTP_MESSAGE|ORTP_WARNING|ORTP_ERROR|ORTP_FATAL);
}

311 312 313 314 315 316 317
/**
 * Enable logs through the user's supplied log callback.
 *
 * @ingroup misc
 *
 * @param logfunc The address of a OrtpLogFunc callback whose protoype is
 *            	  typedef void (*OrtpLogFunc)(OrtpLogLevel lev, const char *fmt, va_list args);
318
 *
319
**/
aymeric's avatar
aymeric committed
320 321 322 323 324
void linphone_core_enable_logs_with_cb(OrtpLogFunc logfunc){
	ortp_set_log_level_mask(ORTP_MESSAGE|ORTP_WARNING|ORTP_ERROR|ORTP_FATAL);
	ortp_set_log_handler(logfunc);
}

325 326 327 328 329
/**
 * Entirely disable logging.
 *
 * @ingroup misc
**/
aymeric's avatar
aymeric committed
330 331 332 333 334
void linphone_core_disable_logs(){
	ortp_set_log_level_mask(ORTP_ERROR|ORTP_FATAL);
}


335
static void net_config_read (LinphoneCore *lc)
aymeric's avatar
aymeric committed
336 337 338 339 340
{
	int tmp;
	const char *tmpstr;
	LpConfig *config=lc->config;

341
	lc->net_conf.nat_address_ip = NULL;
aymeric's avatar
aymeric committed
342 343 344 345 346 347 348 349 350 351 352 353 354 355
	tmp=lp_config_get_int(config,"net","download_bw",0);
	linphone_core_set_download_bandwidth(lc,tmp);
	tmp=lp_config_get_int(config,"net","upload_bw",0);
	linphone_core_set_upload_bandwidth(lc,tmp);
	linphone_core_set_stun_server(lc,lp_config_get_string(config,"net","stun_server",NULL));
	tmpstr=lp_config_get_string(lc->config,"net","nat_address",NULL);
	if (tmpstr!=NULL && (strlen(tmpstr)<1)) tmpstr=NULL;
	linphone_core_set_nat_address(lc,tmpstr);
	tmp=lp_config_get_int(lc->config,"net","firewall_policy",0);
	linphone_core_set_firewall_policy(lc,tmp);
	tmp=lp_config_get_int(lc->config,"net","nat_sdp_only",0);
	lc->net_conf.nat_sdp_only=tmp;
	tmp=lp_config_get_int(lc->config,"net","mtu",0);
	linphone_core_set_mtu(lc,tmp);
jehan's avatar
jehan committed
356 357 358
	tmp=lp_config_get_int(lc->config,"net","download_ptime",0);
	linphone_core_set_download_ptime(lc,tmp);

aymeric's avatar
aymeric committed
359 360
}

361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376
static void build_sound_devices_table(LinphoneCore *lc){
	const char **devices;
	const char **old;
	int ndev;
	int i;
	const MSList *elem=ms_snd_card_manager_get_list(ms_snd_card_manager_get());
	ndev=ms_list_size(elem);
	devices=ms_malloc((ndev+1)*sizeof(const char *));
	for (i=0;elem!=NULL;elem=elem->next,i++){
		devices[i]=ms_snd_card_get_string_id((MSSndCard *)elem->data);
	}
	devices[ndev]=NULL;
	old=lc->sound_conf.cards;
	lc->sound_conf.cards=devices;
	if (old!=NULL) ms_free(old);
}
aymeric's avatar
aymeric committed
377

378
static void sound_config_read(LinphoneCore *lc)
aymeric's avatar
aymeric committed
379
{
380
	int tmp;
aymeric's avatar
aymeric committed
381 382
	const char *tmpbuf;
	const char *devid;
383
	float gain=0;
384
#ifdef __linux
aymeric's avatar
aymeric committed
385 386 387 388 389 390
	/*alsadev let the user use custom alsa device within linphone*/
	devid=lp_config_get_string(lc->config,"sound","alsadev",NULL);
	if (devid){
		MSSndCard *card=ms_alsa_card_new_custom(devid,devid);
		ms_snd_card_manager_add_card(ms_snd_card_manager_get(),card);
	}
Simon Morlat's avatar
Simon Morlat committed
391
	tmp=lp_config_get_int(lc->config,"sound","alsa_forced_rate",-1);
392
	if (tmp>0) ms_alsa_card_set_forced_sample_rate(tmp);
aymeric's avatar
aymeric committed
393 394
#endif
	/* retrieve all sound devices */
395 396
	build_sound_devices_table(lc);

aymeric's avatar
aymeric committed
397 398
	devid=lp_config_get_string(lc->config,"sound","playback_dev_id",NULL);
	linphone_core_set_playback_device(lc,devid);
399

aymeric's avatar
aymeric committed
400 401
	devid=lp_config_get_string(lc->config,"sound","ringer_dev_id",NULL);
	linphone_core_set_ringer_device(lc,devid);
402

aymeric's avatar
aymeric committed
403 404
	devid=lp_config_get_string(lc->config,"sound","capture_dev_id",NULL);
	linphone_core_set_capture_device(lc,devid);
405

aymeric's avatar
aymeric committed
406 407 408 409 410 411 412 413 414 415
/*
	tmp=lp_config_get_int(lc->config,"sound","play_lev",80);
	linphone_core_set_play_level(lc,tmp);
	tmp=lp_config_get_int(lc->config,"sound","ring_lev",80);
	linphone_core_set_ring_level(lc,tmp);
	tmp=lp_config_get_int(lc->config,"sound","rec_lev",80);
	linphone_core_set_rec_level(lc,tmp);
	tmpbuf=lp_config_get_string(lc->config,"sound","source","m");
	linphone_core_set_sound_source(lc,tmpbuf[0]);
*/
416

aymeric's avatar
aymeric committed
417 418
	tmpbuf=PACKAGE_SOUND_DIR "/" LOCAL_RING;
	tmpbuf=lp_config_get_string(lc->config,"sound","local_ring",tmpbuf);
Jehan Monnier's avatar
Jehan Monnier committed
419
	if (ortp_file_exist(tmpbuf)==-1) {
420
		ms_warning("%s does not exist",tmpbuf);
aymeric's avatar
aymeric committed
421 422 423 424 425 426 427
		tmpbuf=PACKAGE_SOUND_DIR "/" LOCAL_RING;
	}
	if (strstr(tmpbuf,".wav")==NULL){
		/* it currently uses old sound files, so replace them */
		tmpbuf=PACKAGE_SOUND_DIR "/" LOCAL_RING;
	}
	linphone_core_set_ring(lc,tmpbuf);
428

429
	tmpbuf=PACKAGE_SOUND_DIR "/" REMOTE_RING;
aymeric's avatar
aymeric committed
430
	tmpbuf=lp_config_get_string(lc->config,"sound","remote_ring",tmpbuf);
Jehan Monnier's avatar
Jehan Monnier committed
431
	if (ortp_file_exist(tmpbuf)==-1){
432
		tmpbuf=PACKAGE_SOUND_DIR "/" REMOTE_RING;
aymeric's avatar
aymeric committed
433 434 435
	}
	if (strstr(tmpbuf,".wav")==NULL){
		/* it currently uses old sound files, so replace them */
436
		tmpbuf=PACKAGE_SOUND_DIR "/" REMOTE_RING;
aymeric's avatar
aymeric committed
437
	}
438
	linphone_core_set_ringback(lc,tmpbuf);
439 440

	linphone_core_set_play_file(lc,lp_config_get_string(lc->config,"sound","hold_music",PACKAGE_SOUND_DIR "/" HOLD_MUSIC));
aymeric's avatar
aymeric committed
441 442
	check_sound_device(lc);
	lc->sound_conf.latency=0;
Simon Morlat's avatar
Simon Morlat committed
443
#ifndef __ios 
444
    tmp=TRUE;
445
#else
Simon Morlat's avatar
Simon Morlat committed
446
    tmp=FALSE; /* on iOS we have builtin echo cancellation.*/
447 448 449
#endif
    tmp=lp_config_get_int(lc->config,"sound","echocancellation",tmp);
	linphone_core_enable_echo_cancellation(lc,tmp);
smorlat's avatar
smorlat committed
450 451
	linphone_core_enable_echo_limiter(lc,
		lp_config_get_int(lc->config,"sound","echolimiter",0));
452 453
	linphone_core_enable_agc(lc,
		lp_config_get_int(lc->config,"sound","agc",0));
454

455 456
	gain=lp_config_get_float(lc->config,"sound","playback_gain_db",0);
	linphone_core_set_playback_gain_db (lc,gain);
457 458

	linphone_core_set_remote_ringback_tone (lc,lp_config_get_string(lc->config,"sound","ringback_tone",NULL));
aymeric's avatar
aymeric committed
459 460
}

461
static void sip_config_read(LinphoneCore *lc)
aymeric's avatar
aymeric committed
462 463 464
{
	char *contact;
	const char *tmpstr;
465
	LCSipTransports tr;
aymeric's avatar
aymeric committed
466 467
	int i,tmp;
	int ipv6;
468

469 470
	tmp=lp_config_get_int(lc->config,"sip","use_info",0);
	linphone_core_set_use_info_for_dtmf(lc,tmp);
aymeric's avatar
aymeric committed
471

472 473 474
	if (lp_config_get_int(lc->config,"sip","use_session_timers",0)==1){
		sal_use_session_timers(lc->sal,200);
	}
aymeric's avatar
aymeric committed
475

476
	sal_use_rport(lc->sal,lp_config_get_int(lc->config,"sip","use_rport",1));
477
	sal_use_101(lc->sal,lp_config_get_int(lc->config,"sip","use_101",1));
478
	sal_reuse_authorization(lc->sal, lp_config_get_int(lc->config,"sip","reuse_authorization",0));
479

480 481
	tmp=lp_config_get_int(lc->config,"sip","use_rfc2833",0);
	linphone_core_set_use_rfc2833_for_dtmf(lc,tmp);
482

aymeric's avatar
aymeric committed
483 484 485 486 487
	ipv6=lp_config_get_int(lc->config,"sip","use_ipv6",-1);
	if (ipv6==-1){
		ipv6=0;
	}
	linphone_core_enable_ipv6(lc,ipv6);
488
	memset(&tr,0,sizeof(tr));
jehan's avatar
jehan committed
489 490 491 492 493 494 495 496 497 498
	if (lp_config_get_int(lc->config,"sip","sip_random_port",0)) {
		tr.udp_port=(0xDFF&+random())+1024;
	} else {
		tr.udp_port=lp_config_get_int(lc->config,"sip","sip_port",5060);
	}
	if (lp_config_get_int(lc->config,"sip","sip_tcp_random_port",0)) {
		tr.tcp_port=(0xDFF&+random())+1024;
	} else {
		tr.tcp_port=lp_config_get_int(lc->config,"sip","sip_tcp_port",0);
	}
Pierre-Eric's avatar
Pierre-Eric committed
499 500 501 502 503
	if (lp_config_get_int(lc->config,"sip","sip_tls_random_port",0)) {
		tr.tls_port=(0xDFF&+random())+1024;
	} else {
		tr.tls_port=lp_config_get_int(lc->config,"sip","sip_tls_port",0);
	}
504 505 506 507 508 509 510

#ifdef __linux
	sal_set_root_ca(lc->sal, lp_config_get_string(lc->config,"sip","root_ca", "/etc/ssl/certs"));
#else
	sal_set_root_ca(lc->sal, lp_config_get_string(lc->config,"sip","root_ca", ROOT_CA_FILE));
#endif
	linphone_core_verify_server_certificates(lc,lp_config_get_int(lc->config,"sip","verify_server_certs",TRUE));
511 512
	/*start listening on ports*/
 	linphone_core_set_sip_transports(lc,&tr);
513

aymeric's avatar
aymeric committed
514 515
	tmpstr=lp_config_get_string(lc->config,"sip","contact",NULL);
	if (tmpstr==NULL || linphone_core_set_primary_contact(lc,tmpstr)==-1) {
smorlat's avatar
smorlat committed
516 517
		const char *hostname=NULL;
		const char *username=NULL;
518
#ifdef HAVE_GETENV
smorlat's avatar
smorlat committed
519 520
		hostname=getenv("HOST");
		username=getenv("USER");
aymeric's avatar
aymeric committed
521
		if (hostname==NULL) hostname=getenv("HOSTNAME");
522
#endif /*HAVE_GETENV*/
aymeric's avatar
aymeric committed
523 524 525 526 527 528 529 530 531 532 533 534
		if (hostname==NULL)
			hostname="unknown-host";
		if (username==NULL){
			username="toto";
		}
		contact=ortp_strdup_printf("sip:%s@%s",username,hostname);
		linphone_core_set_primary_contact(lc,contact);
		ms_free(contact);
	}

	tmp=lp_config_get_int(lc->config,"sip","guess_hostname",1);
	linphone_core_set_guess_hostname(lc,tmp);
535 536


aymeric's avatar
aymeric committed
537 538 539 540 541 542 543 544 545 546 547 548 549 550 551
	tmp=lp_config_get_int(lc->config,"sip","inc_timeout",15);
	linphone_core_set_inc_timeout(lc,tmp);

	/* get proxies config */
	for(i=0;; i++){
		LinphoneProxyConfig *cfg=linphone_proxy_config_new_from_config_file(lc->config,i);
		if (cfg!=NULL){
			linphone_core_add_proxy_config(lc,cfg);
		}else{
			break;
		}
	}
	/* get the default proxy */
	tmp=lp_config_get_int(lc->config,"sip","default_proxy",-1);
	linphone_core_set_default_proxy_index(lc,tmp);
552

aymeric's avatar
aymeric committed
553 554 555 556 557
	/* read authentication information */
	for(i=0;; i++){
		LinphoneAuthInfo *ai=linphone_auth_info_new_from_config_file(lc->config,i);
		if (ai!=NULL){
			linphone_core_add_auth_info(lc,ai);
Simon Morlat's avatar
Simon Morlat committed
558
			linphone_auth_info_destroy(ai);
aymeric's avatar
aymeric committed
559 560 561 562
		}else{
			break;
		}
	}
563 564
	/*this is to filter out unsupported encryption schemes*/
	linphone_core_set_media_encryption(lc,linphone_core_get_media_encryption(lc));
565

566
	/*for tuning or test*/
aymeric's avatar
aymeric committed
567
	lc->sip_conf.sdp_200_ack=lp_config_get_int(lc->config,"sip","sdp_200_ack",0);
568
	lc->sip_conf.register_only_when_network_is_up=
569
		lp_config_get_int(lc->config,"sip","register_only_when_network_is_up",1);
570
	lc->sip_conf.ping_with_options=lp_config_get_int(lc->config,"sip","ping_with_options",1);
jehan's avatar
jehan committed
571
	lc->sip_conf.auto_net_state_mon=lp_config_get_int(lc->config,"sip","auto_net_state_mon",1);
572 573
	lc->sip_conf.keepalive_period=lp_config_get_int(lc->config,"sip","keepalive_period",10000);
	sal_set_keepalive_period(lc->sal,lc->sip_conf.keepalive_period);
Simon Morlat's avatar
Simon Morlat committed
574
	sal_use_one_matching_codec_policy(lc->sal,lp_config_get_int(lc->config,"sip","only_one_codec",0));
575
	sal_use_double_registrations(lc->sal,lp_config_get_int(lc->config,"sip","use_double_registrations",1));
aymeric's avatar
aymeric committed
576 577
}

578
static void rtp_config_read(LinphoneCore *lc)
aymeric's avatar
aymeric committed
579 580 581 582
{
	int port;
	int jitt_comp;
	int nortp_timeout;
Simon Morlat's avatar
Simon Morlat committed
583 584
	bool_t rtp_no_xmit_on_audio_mute;

aymeric's avatar
aymeric committed
585 586
	port=lp_config_get_int(lc->config,"rtp","audio_rtp_port",7078);
	linphone_core_set_audio_port(lc,port);
587

aymeric's avatar
aymeric committed
588 589 590
	port=lp_config_get_int(lc->config,"rtp","video_rtp_port",9078);
	if (port==0) port=9078;
	linphone_core_set_video_port(lc,port);
591

aymeric's avatar
aymeric committed
592
	jitt_comp=lp_config_get_int(lc->config,"rtp","audio_jitt_comp",60);
593
	linphone_core_set_audio_jittcomp(lc,jitt_comp);
aymeric's avatar
aymeric committed
594
	jitt_comp=lp_config_get_int(lc->config,"rtp","video_jitt_comp",60);
595 596
	if (jitt_comp==0) jitt_comp=60;
	lc->rtp_conf.video_jitt_comp=jitt_comp;
aymeric's avatar
aymeric committed
597
	nortp_timeout=lp_config_get_int(lc->config,"rtp","nortp_timeout",30);
598
	linphone_core_set_nortp_timeout(lc,nortp_timeout);
Simon Morlat's avatar
Simon Morlat committed
599
	rtp_no_xmit_on_audio_mute=lp_config_get_int(lc->config,"rtp","rtp_no_xmit_on_audio_mute",FALSE);
600
	linphone_core_set_rtp_no_xmit_on_audio_mute(lc,rtp_no_xmit_on_audio_mute);
aymeric's avatar
aymeric committed
601 602
}

603 604 605 606 607 608 609 610
static PayloadType * find_payload(RtpProfile *prof, const char *mime_type, int clock_rate, const char *recv_fmtp){
	PayloadType *candidate=NULL;
	int i;
	PayloadType *it;
	for(i=0;i<127;++i){
		it=rtp_profile_get_payload(prof,i);
		if (it!=NULL && strcasecmp(mime_type,it->mime_type)==0
			&& (clock_rate==it->clock_rate || clock_rate<=0) ){
611
			if ( (recv_fmtp && it->recv_fmtp && strstr(recv_fmtp,it->recv_fmtp)!=NULL) ||
612 613
				(recv_fmtp==NULL && it->recv_fmtp==NULL) ){
				/*exact match*/
614
				if (recv_fmtp) payload_type_set_recv_fmtp(it,recv_fmtp);
615
				return it;
616 617 618 619 620
			}else {
				if (candidate){
					if (it->recv_fmtp==NULL) candidate=it;
				}else candidate=it;
			}
621 622
		}
	}
623 624 625
	if (candidate && recv_fmtp){
		payload_type_set_recv_fmtp(candidate,recv_fmtp);
	}
626 627
	return candidate;
}
aymeric's avatar
aymeric committed
628

629
static bool_t get_codec(LpConfig *config, const char* type, int index, PayloadType **ret){
aymeric's avatar
aymeric committed
630 631 632 633
	char codeckey[50];
	const char *mime,*fmtp;
	int rate,enabled;
	PayloadType *pt;
634

635
	*ret=NULL;
aymeric's avatar
aymeric committed
636 637
	snprintf(codeckey,50,"%s_%i",type,index);
	mime=lp_config_get_string(config,codeckey,"mime",NULL);
638
	if (mime==NULL || strlen(mime)==0 ) return FALSE;
639

aymeric's avatar
aymeric committed
640 641 642
	rate=lp_config_get_int(config,codeckey,"rate",8000);
	fmtp=lp_config_get_string(config,codeckey,"recv_fmtp",NULL);
	enabled=lp_config_get_int(config,codeckey,"enabled",1);
643 644
	pt=find_payload(&av_profile,mime,rate,fmtp);
	if (pt && enabled ) pt->flags|=PAYLOAD_TYPE_ENABLED;
aymeric's avatar
aymeric committed
645
	//ms_message("Found codec %s/%i",pt->mime_type,pt->clock_rate);
646 647 648 649 650 651
	if (pt==NULL) ms_warning("Ignoring codec config %s/%i with fmtp=%s because unsupported",
	    		mime,rate,fmtp ? fmtp : "");
	*ret=pt;
	return TRUE;
}

652
#define RANK_END 10000
653 654
static const char *codec_pref_order[]={
	"speex",
655 656
	"iLBC",
	"amr",
657 658 659
	"gsm",
	"pcmu",
	"pcma",
660
	"VP8",
661 662 663 664 665 666 667 668 669 670
	"H264",
	"MP4V-ES",
	"H263-1998",
	NULL,
};

static int find_codec_rank(const char *mime){
	int i;
	for(i=0;codec_pref_order[i]!=NULL;++i){
		if (strcasecmp(codec_pref_order[i],mime)==0)
671
			return i;
672
	}
673
	return RANK_END;
674 675 676 677 678 679 680 681
}

static int codec_compare(const PayloadType *a, const PayloadType *b){
	int ra,rb;
	ra=find_codec_rank(a->mime_type);
	rb=find_codec_rank(b->mime_type);
	if (ra>rb) return 1;
	if (ra<rb) return -1;
682
	return 0;
683 684
}

685 686 687 688 689 690 691
static MSList *add_missing_codecs(SalStreamType mtype, MSList *l){
	int i;
	for(i=0;i<127;++i){
		PayloadType *pt=rtp_profile_get_payload(&av_profile,i);
		if (pt){
			if (mtype==SalVideo && pt->type!=PAYLOAD_VIDEO)
				pt=NULL;
692
			else if (mtype==SalAudio && (pt->type!=PAYLOAD_AUDIO_PACKETIZED
693 694 695 696 697
			    && pt->type!=PAYLOAD_AUDIO_CONTINUOUS)){
				pt=NULL;
			}
			if (pt && ms_filter_codec_supported(pt->mime_type)){
				if (ms_list_find(l,pt)==NULL){
698 699
					/*unranked codecs are disabled by default*/
					if (find_codec_rank(pt->mime_type)!=RANK_END){
700 701
						payload_type_set_flag(pt,PAYLOAD_TYPE_ENABLED);
					}
702 703
					ms_message("Adding new codec %s/%i with fmtp %s",
					    pt->mime_type,pt->clock_rate,pt->recv_fmtp ? pt->recv_fmtp : "");
704
					l=ms_list_insert_sorted(l,pt,(int (*)(const void *, const void *))codec_compare);
705 706 707 708 709
				}
			}
		}
	}
	return l;
aymeric's avatar
aymeric committed
710 711
}

712 713
static MSList *codec_append_if_new(MSList *l, PayloadType *pt){
	MSList *elem;
714
	for (elem=l;elem!=NULL;elem=elem->next){
715 716 717 718 719 720 721 722
		PayloadType *ept=(PayloadType*)elem->data;
		if (pt==ept)
			return l;
	}
	l=ms_list_append(l,pt);
	return l;
}

723
static void codecs_config_read(LinphoneCore *lc)
aymeric's avatar
aymeric committed
724 725 726 727 728
{
	int i;
	PayloadType *pt;
	MSList *audio_codecs=NULL;
	MSList *video_codecs=NULL;
729 730 731 732
	for (i=0;get_codec(lc->config,"audio_codec",i,&pt);i++){
		if (pt){
			if (!ms_filter_codec_supported(pt->mime_type)){
				ms_warning("Codec %s is not supported by mediastreamer2, removed.",pt->mime_type);
733
			}else audio_codecs=codec_append_if_new(audio_codecs,pt);
734
		}
aymeric's avatar
aymeric committed
735
	}
736 737 738 739 740
	audio_codecs=add_missing_codecs(SalAudio,audio_codecs);
	for (i=0;get_codec(lc->config,"video_codec",i,&pt);i++){
		if (pt){
			if (!ms_filter_codec_supported(pt->mime_type)){
				ms_warning("Codec %s is not supported by mediastreamer2, removed.",pt->mime_type);
741
			}else video_codecs=codec_append_if_new(video_codecs,(void *)pt);
742
		}
aymeric's avatar
aymeric committed
743
	}
744
	video_codecs=add_missing_codecs(SalVideo,video_codecs);
aymeric's avatar
aymeric committed
745 746
	linphone_core_set_audio_codecs(lc,audio_codecs);
	linphone_core_set_video_codecs(lc,video_codecs);
747
	linphone_core_update_allocated_audio_bandwidth(lc);
aymeric's avatar
aymeric committed
748 749
}

750
static void video_config_read(LinphoneCore *lc){
751
#ifdef VIDEO_ENABLED
Simon Morlat's avatar
Simon Morlat committed
752
	int capture, display, self_view;
753
#endif
aymeric's avatar
aymeric committed
754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772
	const char *str;
	int ndev;
	const char **devices;
	const MSList *elem;
	int i;

	/* retrieve all video devices */
	elem=ms_web_cam_manager_get_list(ms_web_cam_manager_get());
	ndev=ms_list_size(elem);
	devices=ms_malloc((ndev+1)*sizeof(const char *));
	for (i=0;elem!=NULL;elem=elem->next,i++){
		devices[i]=ms_web_cam_get_string_id((MSWebCam *)elem->data);
	}
	devices[ndev]=NULL;
	lc->video_conf.cams=devices;

	str=lp_config_get_string(lc->config,"video","device",NULL);
	if (str && str[0]==0) str=NULL;
	linphone_core_set_video_device(lc,str);
773

smorlat's avatar
smorlat committed
774 775 776
	linphone_core_set_preferred_video_size_by_name(lc,
		lp_config_get_string(lc->config,"video","size","cif"));

777
#ifdef VIDEO_ENABLED
778 779 780
	capture=lp_config_get_int(lc->config,"video","capture",1);
	display=lp_config_get_int(lc->config,"video","display",1);
	self_view=lp_config_get_int(lc->config,"video","self_view",1);
781 782 783
	lc->video_conf.displaytype=lp_config_get_string(lc->config,"video","displaytype",NULL);
	if(lc->video_conf.displaytype)
		ms_message("we are using a specific display:%s\n",lc->video_conf.displaytype);
784

aymeric's avatar
aymeric committed
785
	linphone_core_enable_video(lc,capture,display);
Simon Morlat's avatar
Simon Morlat committed
786
	linphone_core_enable_self_view(lc,self_view);
aymeric's avatar
aymeric committed
787 788 789
#endif
}

790
static void ui_config_read(LinphoneCore *lc)
aymeric's avatar
aymeric committed
791 792 793 794 795 796
{
	LinphoneFriend *lf;
	int i;
	for (i=0;(lf=linphone_friend_new_from_config_file(lc,i))!=NULL;i++){
		linphone_core_add_friend(lc,lf);
	}
797
	call_logs_read_from_config_file(lc);
aymeric's avatar
aymeric committed
798 799
}

800 801
/*
static void autoreplier_config_init(LinphoneCore *lc)
aymeric's avatar
aymeric committed
802 803 804 805 806 807 808 809 810
{
	autoreplier_config_t *config=&lc->autoreplier_conf;
	config->enabled=lp_config_get_int(lc->config,"autoreplier","enabled",0);
	config->after_seconds=lp_config_get_int(lc->config,"autoreplier","after_seconds",6);
	config->max_users=lp_config_get_int(lc->config,"autoreplier","max_users",1);
	config->max_rec_time=lp_config_get_int(lc->config,"autoreplier","max_rec_time",60);
	config->max_rec_msg=lp_config_get_int(lc->config,"autoreplier","max_rec_msg",10);
	config->message=lp_config_get_string(lc->config,"autoreplier","message",NULL);
}
811
*/
aymeric's avatar
aymeric committed
812

Guillaume Beraudo's avatar
Guillaume Beraudo committed
813 814 815 816 817 818 819 820
bool_t linphone_core_tunnel_available(){
#ifdef TUNNEL_ENABLED
	return TRUE;
#else
	return FALSE;
#endif
}

821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837
/**
 * Enable adaptive rate control (experimental feature, audio-only).
 *
 * Adaptive rate control consists in using RTCP feedback provided information to dynamically
 * control the output bitrate of the encoders, so that we can adapt to the network conditions and
 * available bandwidth.
**/
void linphone_core_enable_adaptive_rate_control(LinphoneCore *lc, bool_t enabled){
	lp_config_set_int(lc->config,"net","adaptive_rate_control",(int)enabled);
}

/**
 * Returns whether adaptive rate control is enabled.
 *
 * See linphone_core_enable_adaptive_rate_control().
**/
bool_t linphone_core_adaptive_rate_control_enabled(const LinphoneCore *lc){
838
	return lp_config_get_int(lc->config,"net","adaptive_rate_control",TRUE);
839 840
}

841 842 843 844
bool_t linphone_core_rtcp_enabled(const LinphoneCore *lc){
	return lp_config_get_int(lc->config,"rtp","rtcp_enabled",TRUE);
}

845 846 847 848 849 850 851 852 853 854 855 856 857 858
/**
 * Sets maximum available download bandwidth
 *
 * @ingroup media_parameters
 *
 * This is IP bandwidth, in kbit/s.
 * This information is used signaled to other parties during
 * calls (within SDP messages) so that the remote end can have
 * sufficient knowledge to properly configure its audio & video
 * codec output bitrate to not overflow available bandwidth.
 *
 * @param lc the LinphoneCore object
 * @param bw the bandwidth in kbits/s, 0 for infinite
 */
aymeric's avatar
aymeric committed
859 860
void linphone_core_set_download_bandwidth(LinphoneCore *lc, int bw){
	lc->net_conf.download_bw=bw;
861
	if (linphone_core_ready(lc)) lp_config_set_int(lc->config,"net","download_bw",bw);
aymeric's avatar
aymeric committed
862 863
}

864 865 866 867 868 869 870 871 872 873 874 875 876
/**
 * Sets maximum available upload bandwidth
 *
 * @ingroup media_parameters
 *
 * This is IP bandwidth, in kbit/s.
 * This information is used by liblinphone together with remote
 * side available bandwidth signaled in SDP messages to properly
 * configure audio & video codec's output bitrate.
 *
 * @param lc the LinphoneCore object
 * @param bw the bandwidth in kbits/s, 0 for infinite
 */
aymeric's avatar
aymeric committed
877 878
void linphone_core_set_upload_bandwidth(LinphoneCore *lc, int bw){
	lc->net_conf.upload_bw=bw;
879
	if (linphone_core_ready(lc)) lp_config_set_int(lc->config,"net","upload_bw",bw);
aymeric's avatar
aymeric committed
880 881
}

882 883 884 885 886 887 888 889
/**
 * Retrieve the maximum available download bandwidth.
 *
 * @ingroup media_parameters
 *
 * This value was set by linphone_core_set_download_bandwidth().
 *
**/
aymeric's avatar
aymeric committed
890 891 892 893
int linphone_core_get_download_bandwidth(const LinphoneCore *lc){
	return lc->net_conf.download_bw;
}

894 895 896 897 898 899 900 901
/**
 * Retrieve the maximum available upload bandwidth.
 *
 * @ingroup media_parameters
 *
 * This value was set by linphone_core_set_upload_bandwidth().
 *
**/
aymeric's avatar
aymeric committed
902 903 904
int linphone_core_get_upload_bandwidth(const LinphoneCore *lc){
	return lc->net_conf.upload_bw;
}
jehan's avatar
jehan committed
905
/**
906
 * Set audio packetization time linphone expects to receive from peer
jehan's avatar
jehan committed
907 908
 */
void linphone_core_set_download_ptime(LinphoneCore *lc, int ptime) {
909
	lc->net_conf.down_ptime=ptime;
jehan's avatar
jehan committed
910
}
911

912 913 914 915
/**
 * Get audio packetization time linphone expects to receive from peer
 */
int linphone_core_get_download_ptime(LinphoneCore *lc) {
916
	return lc->net_conf.down_ptime;
jehan's avatar
jehan committed
917 918
}

919 920 921 922 923 924
/**
 * Set audio packetization time linphone will send (in absence of requirement from peer)
 * A value of 0 stands for the current codec default packetization time.
 *
**/
void linphone_core_set_upload_ptime(LinphoneCore *lc, int ptime){
925
	lp_config_set_int(lc->config,"rtp","upload_ptime",ptime);
926 927 928 929 930 931 932 933
}

/**
 * Set audio packetization time linphone will send (in absence of requirement from peer)
 * A value of 0 stands for the current codec default packetization time.
 *
**/
int linphone_core_get_upload_ptime(LinphoneCore *lc){
934
	return lp_config_get_int(lc->config,"rtp","upload_ptime",0);
935 936 937
}


aymeric's avatar
aymeric committed
938

939 940 941 942 943 944
/**
 * Returns liblinphone's version as a string.
 *
 * @ingroup misc
 *
**/
aymeric's avatar
aymeric committed
945 946 947 948
const char * linphone_core_get_version(void){
	return liblinphone_version;
}

949
static void linphone_core_assign_payload_type(LinphoneCore *lc, PayloadType *const_pt, int number, const char *recv_fmtp){
950 951
	PayloadType *pt;
	pt=payload_type_clone(const_pt);
952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975
	if (number==-1){
		/*look for a free number */
		MSList *elem;
		int i;
		for(i=lc->dyn_pt;i<=127;++i){
			bool_t already_assigned=FALSE;
			for(elem=lc->payload_types;elem!=NULL;elem=elem->next){
				PayloadType *it=(PayloadType*)elem->data;
				if (payload_type_get_number(it)==i){
					already_assigned=TRUE;
					break;
				}
			}
			if (!already_assigned){
				number=i;
				lc->dyn_pt=i+1;
				break;
			}
		}
		if (number==-1){
			ms_fatal("FIXME: too many codecs, no more free numbers.");
		}
	}
	ms_message("assigning %s/%i payload type number %i",pt->mime_type,pt->clock_rate,number);
976
	payload_type_set_number(pt,number);
977 978
	if (recv_fmtp!=NULL) payload_type_set_recv_fmtp(pt,recv_fmtp);
	rtp_profile_set_payload(&av_profile,number,pt);
979
	lc->payload_types=ms_list_append(lc->payload_types,pt);
980 981
}

982 983 984 985
static void linphone_core_free_payload_types(LinphoneCore *lc){
	ms_list_for_each(lc->payload_types,(void (*)(void*))payload_type_destroy);
	ms_list_free(lc->payload_types);
	lc->payload_types=NULL;
986
}
987

988
void linphone_core_set_state(LinphoneCore *lc, LinphoneGlobalState gstate, const char *message){
Simon Morlat's avatar
Simon Morlat committed
989
	lc->state=gstate;
990 991 992 993
	if (lc->vtable.global_state_changed){
		lc->vtable.global_state_changed(lc,gstate,message);
	}
}
994 995 996
static void misc_config_read (LinphoneCore *lc) {
	LpConfig *config=lc->config;
    lc->max_call_logs=lp_config_get_int(config,"misc","history_max_size",15);
997
    lc->max_calls=lp_config_get_int(config,"misc","max_calls",NB_MAX_CALLS);
998
}
999

Guillaume Beraudo's avatar
Guillaume Beraudo committed
1000
#ifdef TUNNEL_ENABLED
1001
static void tunnel_add_servers_from_config(LinphoneTunnel *tunnel, const char* confaddress){
Guillaume Beraudo's avatar
Guillaume Beraudo committed
1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029
	char *addresses=(char*)ms_strdup(confaddress);
	char *str1;
	for(str1=addresses;;str1=NULL){
		char *port;
		char *address=strtok(str1," "); // Not thread safe
		if (!address) break;
		port=strchr(address, ':');
		if (!port) ms_fatal("Bad tunnel address %s", address);
		*port++='\0';
		linphone_tunnel_add_server(tunnel, address, atoi(port));
	}
	ms_free(addresses);
}
#endif

void linphone_core_update_tunnel(LinphoneCore *lc){
#ifdef TUNNEL_ENABLED
	bool_t enabled;
	const char* addresses=linphone_tunnel_get_server_addresses(lc->tunnel);
	if (addresses){
		linphone_tunnel_clean_servers(lc->tunnel);
		tunnel_add_servers_from_config(lc->tunnel,addresses);
	}
	enabled=linphone_tunnel_get_state(lc->tunnel)==LinphoneTunnelEnabled && addresses!=NULL;
	linphone_tunnel_enable(lc->tunnel, enabled);
#endif
}

1030
static void linphone_core_init (LinphoneCore * lc, const LinphoneCoreVTable *vtable, const char *config_path,