linphonecore.c 102 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
24
#include "lpconfig.h"
#include "private.h"
#include "mediastreamer2/mediastream.h"
smorlat's avatar
smorlat committed
25
#include "mediastreamer2/msvolume.h"
26
#include "mediastreamer2/msequalizer.h"
aymeric's avatar
aymeric committed
27
28
29
30
31
32
33
#include <eXosip2/eXosip.h>
#include "sdphandler.h"

#include <ortp/telephonyevents.h>
#include "exevents.h"


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

smorlat's avatar
smorlat committed
40
/*#define UNSTANDART_GSM_11K 1*/
aymeric's avatar
aymeric committed
41
42
43
44
45
46
47
48
49
50
51
52

static const char *liblinphone_version=LIBLINPHONE_VERSION;

#include "enum.h"

void linphone_core_get_local_ip(LinphoneCore *lc, const char *dest, char *result);
static void apply_nat_settings(LinphoneCore *lc);
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)*/
53
#define REMOTE_RING "ringback.wav"
aymeric's avatar
aymeric committed
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75


sdp_handler_t linphone_sdphandler={
	linphone_accept_audio_offer,   /*from remote sdp */
	linphone_accept_video_offer,   /*from remote sdp */
	linphone_set_audio_offer,	/*to local sdp */
	linphone_set_video_offer,	/*to local sdp */
	linphone_read_audio_answer,	/*from incoming answer  */
	linphone_read_video_answer	/*from incoming answer  */
};

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

76
static void  linphone_call_init_common(LinphoneCall *call, LinphoneAddress *from, LinphoneAddress *to){
aymeric's avatar
aymeric committed
77
78
	call->state=LCStateInit;
	call->start_time=time(NULL);
smorlat's avatar
smorlat committed
79
	call->media_start_time=0;
aymeric's avatar
aymeric committed
80
81
	call->log=linphone_call_log_new(call, from, to);
	linphone_core_notify_all_friends(call->core,LINPHONE_STATUS_ONTHEPHONE);
82
	if (linphone_core_get_firewall_policy(call->core)==LINPHONE_POLICY_USE_STUN)
smorlat's avatar
smorlat committed
83
		linphone_core_run_stun_tests(call->core,call);
smorlat's avatar
smorlat committed
84
	call->profile=rtp_profile_new("Call RTP profile");
aymeric's avatar
aymeric committed
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
}

void linphone_call_init_media_params(LinphoneCall *call){
	memset(&call->audio_params,0,sizeof(call->audio_params));
	memset(&call->video_params,0,sizeof(call->video_params));
}

static void discover_mtu(LinphoneCore *lc, const char *remote){
	int mtu;
	if (lc->net_conf.mtu==0	){
		/*attempt to discover mtu*/
		mtu=ms_discover_mtu(remote);
		if (mtu>0){
			ms_set_mtu(mtu);
			ms_message("Discovered mtu is %i, RTP payload max size is %i",
				mtu, ms_get_payload_max_size());
		}
	}
}

105
LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to)
aymeric's avatar
aymeric committed
106
107
108
109
110
111
112
{
	LinphoneCall *call=ms_new0(LinphoneCall,1);
	call->dir=LinphoneCallOutgoing;
	call->cid=-1;
	call->did=-1;
	call->tid=-1;
	call->core=lc;
113
	linphone_core_get_local_ip(lc,linphone_address_get_domain(to),call->localip);
114
	linphone_call_init_common(call,from,to);
smorlat's avatar
smorlat committed
115
	call->sdpctx=sdp_handler_create_context(&linphone_sdphandler,
116
		call->audio_params.natd_port>0 ? call->audio_params.natd_addr : call->localip,
117
		linphone_address_get_username (from),NULL);
smorlat's avatar
smorlat committed
118
	sdp_context_set_user_pointer(call->sdpctx,(void*)call);
119
	discover_mtu(lc,linphone_address_get_domain (to));
aymeric's avatar
aymeric committed
120
121
122
123
	return call;
}


124
LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to, eXosip_event_t *ev){
aymeric's avatar
aymeric committed
125
	LinphoneCall *call=ms_new0(LinphoneCall,1);
126
	LinphoneAddress *me=linphone_core_get_primary_contact_parsed(lc);
smorlat's avatar
smorlat committed
127
128
	osip_header_t *h=NULL;

aymeric's avatar
aymeric committed
129
	call->dir=LinphoneCallIncoming;
smorlat's avatar
smorlat committed
130
131
132
	call->cid=ev->cid;
	call->did=ev->did;
	call->tid=ev->tid;
aymeric's avatar
aymeric committed
133
	call->core=lc;
134
	
135
	linphone_address_clean(from);
136
	
137
	linphone_core_get_local_ip(lc,linphone_address_get_domain(from),call->localip);
138
	linphone_call_init_common(call, from, to);
smorlat's avatar
smorlat committed
139
	call->sdpctx=sdp_handler_create_context(&linphone_sdphandler,
140
		call->audio_params.natd_port>0 ? call->audio_params.natd_addr : call->localip,
141
		linphone_address_get_username (me),NULL);
smorlat's avatar
smorlat committed
142
	sdp_context_set_user_pointer(call->sdpctx,(void*)call);
143
144
	discover_mtu(lc,linphone_address_get_domain(from));
	linphone_address_destroy(me);
smorlat's avatar
smorlat committed
145
146
	osip_message_header_get_byname(ev->request,"Session-expires",0,&h);
	if (h) call->supports_session_timers=TRUE;
aymeric's avatar
aymeric committed
147
148
149
150
151
152
153
	return call;
}

void linphone_call_destroy(LinphoneCall *obj)
{
	linphone_core_notify_all_friends(obj->core,obj->core->prev_mode);
	linphone_call_log_completed(obj->log,obj);
154
	linphone_core_update_allocated_audio_bandwidth(obj->core);
smorlat's avatar
smorlat committed
155
	if (obj->profile!=NULL) rtp_profile_destroy(obj->profile);
aymeric's avatar
aymeric committed
156
157
158
159
160
161
	if (obj->sdpctx!=NULL) sdp_context_free(obj->sdpctx);
	ms_free(obj);
}

/*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
162
#if !defined(_WIN32_WCE)
aymeric's avatar
aymeric committed
163
	return strftime(s, max, fmt, tm);
Jehan Monnier's avatar
Jehan Monnier committed
164
165
166
167
#else
	return 0;
	/*FIXME*/
#endif /*_WIN32_WCE*/
aymeric's avatar
aymeric committed
168
169
}

170
171
172
173
static void set_call_log_date(LinphoneCallLog *cl, const struct tm *loctime){
	my_strftime(cl->start_date,sizeof(cl->start_date),"%c",loctime);
}

174
LinphoneCallLog * linphone_call_log_new(LinphoneCall *call, LinphoneAddress *from, LinphoneAddress *to){
aymeric's avatar
aymeric committed
175
176
177
178
	LinphoneCallLog *cl=ms_new0(LinphoneCallLog,1);
	struct tm loctime;
	cl->dir=call->dir;
#ifdef WIN32
Jehan Monnier's avatar
Jehan Monnier committed
179
#if !defined(_WIN32_WCE)
aymeric's avatar
aymeric committed
180
	loctime=*localtime(&call->start_time);
Jehan Monnier's avatar
Jehan Monnier committed
181
182
	/*FIXME*/
#endif /*_WIN32_WCE*/
aymeric's avatar
aymeric committed
183
184
185
#else
	localtime_r(&call->start_time,&loctime);
#endif
186
	set_call_log_date(cl,&loctime);
aymeric's avatar
aymeric committed
187
188
189
190
	cl->from=from;
	cl->to=to;
	return cl;
}
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247

static void call_logs_write_to_config_file(LinphoneCore *lc){
	MSList *elem;
	char logsection[32];
	int i;
	char *tmp;
	LpConfig *cfg=lc->config;

	if (!lc->ready) return;
	
	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);
	}
	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);
			lc->call_logs=ms_list_append(lc->call_logs,cl);
		}else break;	
	}
}


aymeric's avatar
aymeric committed
248
249
void linphone_call_log_completed(LinphoneCallLog *calllog, LinphoneCall *call){
	LinphoneCore *lc=call->core;
250
	
aymeric's avatar
aymeric committed
251
252
253
254
255
256
257
258
259
260
	calllog->duration=time(NULL)-call->start_time;
	switch(call->state){
		case LCStateInit:
			calllog->status=LinphoneCallAborted;
			break;
		case LCStateRinging:
			if (calllog->dir==LinphoneCallIncoming){
				char *info;
				calllog->status=LinphoneCallMissed;
				lc->missed_calls++;
261
262
263
				info=ortp_strdup_printf(ngettext("You have missed %i call.",
                            "You have missed %i calls.", lc->missed_calls),
                        lc->missed_calls);
aymeric's avatar
aymeric committed
264
265
266
267
268
269
270
271
272
				lc->vtable.display_status(lc,info);
				ms_free(info);
			}
			else calllog->status=LinphoneCallAborted;
			break;
		case LCStateAVRunning:
			calllog->status=LinphoneCallSuccess;
			break;
	}
Simon Morlat's avatar
Simon Morlat committed
273
	lc->call_logs=ms_list_prepend(lc->call_logs,(void *)calllog);
aymeric's avatar
aymeric committed
274
	if (ms_list_size(lc->call_logs)>lc->max_call_logs){
Simon Morlat's avatar
Simon Morlat committed
275
276
277
278
279
280
		MSList *elem,*prevelem=NULL;
		/*find the last element*/
		for(elem=lc->call_logs;elem!=NULL;elem=elem->next){
			prevelem=elem;
		}
		elem=prevelem;
aymeric's avatar
aymeric committed
281
282
283
284
285
286
		linphone_call_log_destroy((LinphoneCallLog*)elem->data);
		lc->call_logs=ms_list_remove_link(lc->call_logs,elem);
	}
	if (lc->vtable.call_log_updated!=NULL){
		lc->vtable.call_log_updated(lc,calllog);
	}
287
	call_logs_write_to_config_file(lc);
aymeric's avatar
aymeric committed
288
289
}

smorlat's avatar
smorlat committed
290
291
292
293
294
295
296
297
298
299
/**
 * @addtogroup call_logs
 * @{
**/

/**
 * Returns a human readable string describing the call.
 * 
 * @note: the returned char* must be freed by the application (use ms_free()).
**/
aymeric's avatar
aymeric committed
300
301
char * linphone_call_log_to_str(LinphoneCallLog *cl){
	char *status;
302
	char *tmp;
303
304
	char *from=linphone_address_as_string (cl->from);
	char *to=linphone_address_as_string (cl->to);
aymeric's avatar
aymeric committed
305
306
307
308
309
310
311
312
313
314
315
316
317
	switch(cl->status){
		case LinphoneCallAborted:
			status=_("aborted");
			break;
		case LinphoneCallSuccess:
			status=_("completed");
			break;
		case LinphoneCallMissed:
			status=_("missed");
			break;
		default:
			status="unknown";
	}
318
	tmp=ortp_strdup_printf(_("%s at %s\nFrom: %s\nTo: %s\nStatus: %s\nDuration: %i mn %i sec\n"),
aymeric's avatar
aymeric committed
319
320
			(cl->dir==LinphoneCallIncoming) ? _("Incoming call") : _("Outgoing call"),
			cl->start_date,
321
322
			from,
			to,
aymeric's avatar
aymeric committed
323
324
325
			status,
			cl->duration/60,
			cl->duration%60);
326
327
328
	ms_free(from);
	ms_free(to);
	return tmp;
aymeric's avatar
aymeric committed
329
330
}

smorlat's avatar
smorlat committed
331
332
333
334
335
336
337
338
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
339
340


341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
/**
 * 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);
	call_logs_write_to_config_file(cl->lc);
}

/**
 * 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
368
369
/** @} */

aymeric's avatar
aymeric committed
370
void linphone_call_log_destroy(LinphoneCallLog *cl){
371
372
373
	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
374
375
376
	ms_free(cl);
}

smorlat's avatar
smorlat committed
377
378
379
int linphone_core_get_current_call_duration(const LinphoneCore *lc){
	LinphoneCall *call=lc->call;
	if (call==NULL) return 0;
smorlat's avatar
smorlat committed
380
381
382
383
	if (call->media_start_time==0) return 0;
	return time(NULL)-call->media_start_time;
}

384
const LinphoneAddress *linphone_core_get_remote_uri(LinphoneCore *lc){
smorlat's avatar
smorlat committed
385
386
387
	LinphoneCall *call=lc->call;
	if (call==NULL) return 0;
	return call->dir==LinphoneCallIncoming ? call->log->from : call->log->to;
smorlat's avatar
smorlat committed
388
389
}

aymeric's avatar
aymeric committed
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
void _osip_trace_func(char *fi, int li, osip_trace_level_t level, char *chfr, va_list ap){
	int ortp_level=ORTP_DEBUG;
	switch(level){
		case OSIP_INFO1:
		case OSIP_INFO2:
		case OSIP_INFO3:
		case OSIP_INFO4:
			ortp_level=ORTP_MESSAGE;
			break;
		case OSIP_WARNING:
			ortp_level=ORTP_WARNING;
			break;
		case OSIP_ERROR:
		case OSIP_BUG:
			ortp_level=ORTP_ERROR;
			break;
		case OSIP_FATAL:
			ortp_level=ORTP_FATAL;
			break;
		case END_TRACE_LEVEL:
410
			break;
aymeric's avatar
aymeric committed
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
	}
	if (ortp_log_level_enabled(level)){
		int len=strlen(chfr);
		char *chfrdup=ortp_strdup(chfr);
		/*need to remove endline*/
		if (len>1){
			if (chfrdup[len-1]=='\n')
				chfrdup[len-1]='\0';
			if (chfrdup[len-2]=='\r')
				chfrdup[len-2]='\0';
		}
		ortp_logv(ortp_level,chfrdup,ap);
		ortp_free(chfrdup);
	}
}

427
428
429
430
431
432
433
434
/**
 * Enable logs in supplied FILE*.
 *
 * @ingroup misc
 *
 * @param file a C FILE* where to fprintf logs. If null stdout is used.
 * 
**/
aymeric's avatar
aymeric committed
435
436
437
438
439
440
441
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);
	osip_trace_initialize_func (OSIP_INFO4,&_osip_trace_func);
}

442
443
444
445
446
447
448
449
450
/**
 * 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);
 * 
**/
aymeric's avatar
aymeric committed
451
452
453
454
455
456
void linphone_core_enable_logs_with_cb(OrtpLogFunc logfunc){
	ortp_set_log_level_mask(ORTP_MESSAGE|ORTP_WARNING|ORTP_ERROR|ORTP_FATAL);
	osip_trace_initialize_func (OSIP_INFO4,&_osip_trace_func);
	ortp_set_log_handler(logfunc);
}

457
458
459
460
461
/**
 * Entirely disable logging.
 *
 * @ingroup misc
**/
aymeric's avatar
aymeric committed
462
463
464
465
466
467
468
void linphone_core_disable_logs(){
	int tl;
	for (tl=0;tl<=OSIP_INFO4;tl++) osip_trace_disable_level(tl);
	ortp_set_log_level_mask(ORTP_ERROR|ORTP_FATAL);
}


469
static void
aymeric's avatar
aymeric committed
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
net_config_read (LinphoneCore *lc)
{
	int tmp;
	const char *tmpstr;
	LpConfig *config=lc->config;

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

492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
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
508

509
static void sound_config_read(LinphoneCore *lc)
aymeric's avatar
aymeric committed
510
511
512
513
{
	/*int tmp;*/
	const char *tmpbuf;
	const char *devid;
514
#ifdef __linux
aymeric's avatar
aymeric committed
515
516
517
518
519
520
521
522
	/*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);
	}
#endif
	/* retrieve all sound devices */
523
524
	build_sound_devices_table(lc);

aymeric's avatar
aymeric committed
525
526
	devid=lp_config_get_string(lc->config,"sound","playback_dev_id",NULL);
	linphone_core_set_playback_device(lc,devid);
527

aymeric's avatar
aymeric committed
528
529
	devid=lp_config_get_string(lc->config,"sound","ringer_dev_id",NULL);
	linphone_core_set_ringer_device(lc,devid);
530

aymeric's avatar
aymeric committed
531
532
	devid=lp_config_get_string(lc->config,"sound","capture_dev_id",NULL);
	linphone_core_set_capture_device(lc,devid);
533

aymeric's avatar
aymeric committed
534
535
536
537
538
539
540
541
542
543
/*
	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]);
*/
544

aymeric's avatar
aymeric committed
545
546
	tmpbuf=PACKAGE_SOUND_DIR "/" LOCAL_RING;
	tmpbuf=lp_config_get_string(lc->config,"sound","local_ring",tmpbuf);
Jehan Monnier's avatar
Jehan Monnier committed
547
	if (ortp_file_exist(tmpbuf)==-1) {
aymeric's avatar
aymeric committed
548
549
550
551
552
553
		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;
	}
554

aymeric's avatar
aymeric committed
555
	linphone_core_set_ring(lc,tmpbuf);
556

557
	tmpbuf=PACKAGE_SOUND_DIR "/" REMOTE_RING;
aymeric's avatar
aymeric committed
558
	tmpbuf=lp_config_get_string(lc->config,"sound","remote_ring",tmpbuf);
Jehan Monnier's avatar
Jehan Monnier committed
559
	if (ortp_file_exist(tmpbuf)==-1){
560
		tmpbuf=PACKAGE_SOUND_DIR "/" REMOTE_RING;
aymeric's avatar
aymeric committed
561
562
563
	}
	if (strstr(tmpbuf,".wav")==NULL){
		/* it currently uses old sound files, so replace them */
564
		tmpbuf=PACKAGE_SOUND_DIR "/" REMOTE_RING;
aymeric's avatar
aymeric committed
565
	}
566
	linphone_core_set_ringback(lc,tmpbuf);
aymeric's avatar
aymeric committed
567
568
569
	check_sound_device(lc);
	lc->sound_conf.latency=0;

570
571
572
573
	linphone_core_enable_echo_cancellation(lc,
	    lp_config_get_int(lc->config,"sound","echocancelation",0) |
	    lp_config_get_int(lc->config,"sound","echocancellation",0)
		);
smorlat's avatar
smorlat committed
574
575
576

	linphone_core_enable_echo_limiter(lc,
		lp_config_get_int(lc->config,"sound","echolimiter",0));
577
578
	linphone_core_enable_agc(lc,
		lp_config_get_int(lc->config,"sound","agc",0));
aymeric's avatar
aymeric committed
579
580
}

581
static void sip_config_read(LinphoneCore *lc)
aymeric's avatar
aymeric committed
582
583
584
585
586
587
588
589
590
{
	char *contact;
	const char *tmpstr;
	int port;
	int i,tmp;
	int ipv6;
	port=lp_config_get_int(lc->config,"sip","use_info",0);
	linphone_core_set_use_info_for_dtmf(lc,port);

591
592
593
	port=lp_config_get_int(lc->config,"sip","use_rfc2833",0);
	linphone_core_set_use_rfc2833_for_dtmf(lc,port);

aymeric's avatar
aymeric committed
594
595
596
597
598
599
600
601
602
603
	ipv6=lp_config_get_int(lc->config,"sip","use_ipv6",-1);
	if (ipv6==-1){
		ipv6=0;
		if (host_has_ipv6_network()){
			lc->vtable.display_message(lc,_("Your machine appears to be connected to an IPv6 network. By default linphone always uses IPv4. Please update your configuration if you want to use IPv6"));
		}
	}
	linphone_core_enable_ipv6(lc,ipv6);
	port=lp_config_get_int(lc->config,"sip","sip_port",5060);
	linphone_core_set_sip_port(lc,port);
604

aymeric's avatar
aymeric committed
605
606
	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
607
608
		const char *hostname=NULL;
		const char *username=NULL;
609
#ifdef HAVE_GETENV
smorlat's avatar
smorlat committed
610
611
		hostname=getenv("HOST");
		username=getenv("USER");
aymeric's avatar
aymeric committed
612
		if (hostname==NULL) hostname=getenv("HOSTNAME");
613
#endif /*HAVE_GETENV*/
aymeric's avatar
aymeric committed
614
615
616
617
618
619
620
621
622
623
624
625
		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);
626
627


aymeric's avatar
aymeric committed
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
	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);
643

aymeric's avatar
aymeric committed
644
645
646
647
648
649
650
651
652
	/* 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);
		}else{
			break;
		}
	}
653
	
aymeric's avatar
aymeric committed
654
655
	/*for test*/
	lc->sip_conf.sdp_200_ack=lp_config_get_int(lc->config,"sip","sdp_200_ack",0);
smorlat's avatar
smorlat committed
656
	lc->sip_conf.only_one_codec=lp_config_get_int(lc->config,"sip","only_one_codec",0);
657
658
	lc->sip_conf.register_only_when_network_is_up=
		lp_config_get_int(lc->config,"sip","register_only_when_network_is_up",0);
aymeric's avatar
aymeric committed
659
660
}

661
static void rtp_config_read(LinphoneCore *lc)
aymeric's avatar
aymeric committed
662
663
664
665
666
667
{
	int port;
	int jitt_comp;
	int nortp_timeout;
	port=lp_config_get_int(lc->config,"rtp","audio_rtp_port",7078);
	linphone_core_set_audio_port(lc,port);
668

aymeric's avatar
aymeric committed
669
670
671
	port=lp_config_get_int(lc->config,"rtp","video_rtp_port",9078);
	if (port==0) port=9078;
	linphone_core_set_video_port(lc,port);
672

aymeric's avatar
aymeric committed
673
	jitt_comp=lp_config_get_int(lc->config,"rtp","audio_jitt_comp",60);
674
	linphone_core_set_audio_jittcomp(lc,jitt_comp);
aymeric's avatar
aymeric committed
675
676
	jitt_comp=lp_config_get_int(lc->config,"rtp","video_jitt_comp",60);
	nortp_timeout=lp_config_get_int(lc->config,"rtp","nortp_timeout",30);
677
	linphone_core_set_nortp_timeout(lc,nortp_timeout);
aymeric's avatar
aymeric committed
678
679
680
}


681
static PayloadType * get_codec(LpConfig *config, char* type,int index){
aymeric's avatar
aymeric committed
682
683
684
685
	char codeckey[50];
	const char *mime,*fmtp;
	int rate,enabled;
	PayloadType *pt;
686

aymeric's avatar
aymeric committed
687
688
689
	snprintf(codeckey,50,"%s_%i",type,index);
	mime=lp_config_get_string(config,codeckey,"mime",NULL);
	if (mime==NULL || strlen(mime)==0 ) return NULL;
690

aymeric's avatar
aymeric committed
691
692
	pt=payload_type_new();
	pt->mime_type=ms_strdup(mime);
693

aymeric's avatar
aymeric committed
694
695
696
697
698
699
700
701
702
703
	rate=lp_config_get_int(config,codeckey,"rate",8000);
	pt->clock_rate=rate;
	fmtp=lp_config_get_string(config,codeckey,"recv_fmtp",NULL);
	if (fmtp) pt->recv_fmtp=ms_strdup(fmtp);
	enabled=lp_config_get_int(config,codeckey,"enabled",1);
	if (enabled ) pt->flags|=PAYLOAD_TYPE_ENABLED;
	//ms_message("Found codec %s/%i",pt->mime_type,pt->clock_rate);
	return pt;
}

704
static void codecs_config_read(LinphoneCore *lc)
aymeric's avatar
aymeric committed
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
{
	int i;
	PayloadType *pt;
	MSList *audio_codecs=NULL;
	MSList *video_codecs=NULL;
	for (i=0;;i++){
		pt=get_codec(lc->config,"audio_codec",i);
		if (pt==NULL) break;
		audio_codecs=ms_list_append(audio_codecs,(void *)pt);
	}
	for (i=0;;i++){
		pt=get_codec(lc->config,"video_codec",i);
		if (pt==NULL) break;
		video_codecs=ms_list_append(video_codecs,(void *)pt);
	}
	linphone_core_set_audio_codecs(lc,audio_codecs);
	linphone_core_set_video_codecs(lc,video_codecs);
	linphone_core_setup_local_rtp_profile(lc);
}

725
static void video_config_read(LinphoneCore *lc)
aymeric's avatar
aymeric committed
726
{
Simon Morlat's avatar
Simon Morlat committed
727
	int capture, display, self_view;
aymeric's avatar
aymeric committed
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
	int enabled;
	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);
748

smorlat's avatar
smorlat committed
749
750
751
	linphone_core_set_preferred_video_size_by_name(lc,
		lp_config_get_string(lc->config,"video","size","cif"));

aymeric's avatar
aymeric committed
752
753
754
	enabled=lp_config_get_int(lc->config,"video","enabled",1);
	capture=lp_config_get_int(lc->config,"video","capture",enabled);
	display=lp_config_get_int(lc->config,"video","display",enabled);
Simon Morlat's avatar
Simon Morlat committed
755
	self_view=lp_config_get_int(lc->config,"video","self_view",enabled);
aymeric's avatar
aymeric committed
756
757
#ifdef VIDEO_ENABLED
	linphone_core_enable_video(lc,capture,display);
Simon Morlat's avatar
Simon Morlat committed
758
	linphone_core_enable_self_view(lc,self_view);
aymeric's avatar
aymeric committed
759
760
761
#endif
}

762
static void ui_config_read(LinphoneCore *lc)
aymeric's avatar
aymeric committed
763
764
765
766
767
768
{
	LinphoneFriend *lf;
	int i;
	for (i=0;(lf=linphone_friend_new_from_config_file(lc,i))!=NULL;i++){
		linphone_core_add_friend(lc,lf);
	}
769
	call_logs_read_from_config_file(lc);
aymeric's avatar
aymeric committed
770
771
}

772
773
/*
static void autoreplier_config_init(LinphoneCore *lc)
aymeric's avatar
aymeric committed
774
775
776
777
778
779
780
781
782
{
	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);
}
783
*/
aymeric's avatar
aymeric committed
784

785
786
787
788
789
790
791
792
793
794
795
796
797
798
/**
 * 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
799
800
801
802
803
804
void linphone_core_set_download_bandwidth(LinphoneCore *lc, int bw){
	lc->net_conf.download_bw=bw;
	if (bw==0){ /*infinite*/
		lc->dw_audio_bw=-1;
		lc->dw_video_bw=-1;
	}else {
805
806
		lc->dw_audio_bw=MIN(lc->audio_bw,bw);
		lc->dw_video_bw=MAX(bw-lc->dw_audio_bw-10,0);/*-10: security margin*/
aymeric's avatar
aymeric committed
807
808
809
	}
}

810
811
812
813
814
815
816
817
818
819
820
821
822
/**
 * 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
823
824
825
826
827
828
void linphone_core_set_upload_bandwidth(LinphoneCore *lc, int bw){
	lc->net_conf.upload_bw=bw;
	if (bw==0){ /*infinite*/
		lc->up_audio_bw=-1;
		lc->up_video_bw=-1;
	}else{
829
830
		lc->up_audio_bw=MIN(lc->audio_bw,bw);
		lc->up_video_bw=MAX(bw-lc->up_audio_bw-10,0);/*-10: security margin*/
aymeric's avatar
aymeric committed
831
832
833
	}
}

834
835
836
837
838
839
840
841
/**
 * Retrieve the maximum available download bandwidth.
 *
 * @ingroup media_parameters
 *
 * This value was set by linphone_core_set_download_bandwidth().
 *
**/
aymeric's avatar
aymeric committed
842
843
844
845
int linphone_core_get_download_bandwidth(const LinphoneCore *lc){
	return lc->net_conf.download_bw;
}

846
847
848
849
850
851
852
853
/**
 * Retrieve the maximum available upload bandwidth.
 *
 * @ingroup media_parameters
 *
 * This value was set by linphone_core_set_upload_bandwidth().
 *
**/
aymeric's avatar
aymeric committed
854
855
856
857
int linphone_core_get_upload_bandwidth(const LinphoneCore *lc){
	return lc->net_conf.upload_bw;
}

858
859
860
861
862
863
/**
 * Returns liblinphone's version as a string.
 *
 * @ingroup misc
 *
**/
aymeric's avatar
aymeric committed
864
865
866
867
868
const char * linphone_core_get_version(void){
	return liblinphone_version;
}


869
static MSList *linphone_payload_types=NULL;
aymeric's avatar
aymeric committed
870

871
872
873
874
875
static void linphone_core_assign_payload_type(PayloadType *const_pt, int number, const char *recv_fmtp){
	PayloadType *pt;
	pt=payload_type_clone(const_pt);
	if (recv_fmtp!=NULL) payload_type_set_recv_fmtp(pt,recv_fmtp);
	rtp_profile_set_payload(&av_profile,number,pt);
876
	linphone_payload_types=ms_list_append(linphone_payload_types,pt);
877
878
879
880
881
882
883
}

static void linphone_core_free_payload_types(void){
	ms_list_for_each(linphone_payload_types,(void (*)(void*))payload_type_destroy);
	ms_list_free(linphone_payload_types);
	linphone_payload_types=NULL;
}
884

885
886
static void linphone_core_init (LinphoneCore * lc, const LinphoneCoreVTable *vtable, const char *config_path, 
    const char *factory_config_path, void * userdata)
aymeric's avatar
aymeric committed
887
888
889
{
	memset (lc, 0, sizeof (LinphoneCore));
	lc->data=userdata;
smorlat's avatar
fixes    
smorlat committed
890

aymeric's avatar
aymeric committed
891
892
	memcpy(&lc->vtable,vtable,sizeof(LinphoneCoreVTable));

smorlat's avatar
fixes    
smorlat committed
893
894
	gstate_initialize(lc);
	gstate_new_state(lc, GSTATE_POWER_STARTUP, NULL);
895

aymeric's avatar
aymeric committed
896
	ortp_init();
897
898
899
900
901
	linphone_core_assign_payload_type(&payload_type_lpc1015,115,NULL);
	linphone_core_assign_payload_type(&payload_type_speex_nb,110,"vbr=on");
	linphone_core_assign_payload_type(&payload_type_speex_wb,111,"vbr=on");
	linphone_core_assign_payload_type(&payload_type_speex_uwb,112,"vbr=on");
	linphone_core_assign_payload_type(&payload_type_telephone_event,101,NULL);
902
	linphone_core_assign_payload_type(&payload_type_ilbc,113,"mode=30");
903
904
905
906
907
908
909

#ifdef ENABLE_NONSTANDARD_GSM
	{
		PayloadType *pt;
		pt=payload_type_clone(&payload_type_gsm);
		pt->clock_rate=11025;
		rtp_profile_set_payload(&av_profile,114,pt);
910
911
912
913
914
		linphone_payload_types=ms_list_append(linphone_payload_types,pt);
		pt=payload_type_clone(&payload_type_gsm);
		pt->clock_rate=22050;
		rtp_profile_set_payload(&av_profile,115,pt);
		linphone_payload_types=ms_list_append(linphone_payload_types,pt);
915
	}
aymeric's avatar
aymeric committed
916
917
#endif

918
919
920
921
922
923
924
925
#ifdef VIDEO_ENABLED
	linphone_core_assign_payload_type(&payload_type_h263,34,NULL);
	linphone_core_assign_payload_type(&payload_type_theora,97,NULL);
	linphone_core_assign_payload_type(&payload_type_h263_1998,98,"CIF=1;QCIF=1");
	linphone_core_assign_payload_type(&payload_type_mp4v,99,"profile-level-id=3");
	linphone_core_assign_payload_type(&payload_type_x_snow,100,NULL);
	linphone_core_assign_payload_type(&payload_type_h264,102,NULL);
	linphone_core_assign_payload_type(&payload_type_h264,103,"packetization-mode=1");
926
927
#endif

aymeric's avatar
aymeric committed
928
	ms_init();
929

aymeric's avatar
aymeric committed
930
	lc->config=lp_config_new(config_path);
931
932
	if (factory_config_path)
		lp_config_read_file(lc->config,factory_config_path);
933

aymeric's avatar
aymeric committed
934
935
936
937
938
#ifdef VINCENT_MAURY_RSVP
	/* default qos parameters : rsvp on, rpc off */
	lc->rsvp_enable = 1;
	lc->rpc_enable = 0;
#endif
smorlat's avatar
smorlat committed
939
	sip_setup_register_all();
aymeric's avatar
aymeric committed
940
941
942
943
944
945
946
947
948
949
950
951
952
953
	sound_config_read(lc);
	net_config_read(lc);
	rtp_config_read(lc);
	codecs_config_read(lc);
	sip_config_read(lc); /* this will start eXosip*/
	video_config_read(lc);
	//autoreplier_config_init(&lc->autoreplier_conf);
	lc->prev_mode=LINPHONE_STATUS_ONLINE;
	lc->presence_mode=LINPHONE_STATUS_ONLINE;
	lc->max_call_logs=15;
	ui_config_read(lc);
	ms_mutex_init(&lc->lock,NULL);
	lc->vtable.display_status(lc,_("Ready"));
        gstate_new_state(lc, GSTATE_POWER_ON, NULL);
954
	lc->ready=TRUE;
aymeric's avatar
aymeric committed
955
956
}

957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
/**
 * Instanciates a LinphoneCore object.
 * @ingroup initializing
 * 
 * The LinphoneCore object is the primary handle for doing all phone actions.
 * It should be unique within your application.
 * @param vtable a LinphoneCoreVTable structure holding your application callbacks
 * @param config_path a path to a config file. If it does not exists it will be created.
 *        The config file is used to store all user settings, call logs, friends, proxies...
 * @param factory_config_path a path to a read-only config file that can be used to 
 *        to store hard-coded preference such as proxy settings or internal preferences.
 *        The settings in this factory file always override the one in the normal config file.
 *        It is OPTIONAL, use NULL if unneeded.
 * @param userdata an opaque user pointer that can be retrieved at any time (for example in
 *        callbacks) using linphone_core_get_user_data().
 * 
**/
aymeric's avatar
aymeric committed
974
LinphoneCore *linphone_core_new(const LinphoneCoreVTable *vtable,
975
						const char *config_path, const char *factory_config_path, void * userdata)
aymeric's avatar
aymeric committed
976
977
{
	LinphoneCore *core=ms_new(LinphoneCore,1);
978
	linphone_core_init(core,vtable,config_path, factory_config_path, userdata);
aymeric's avatar
aymeric committed
979
980
981
	return core;
}

982
983
984
985
986
987
988
989
/**
 * Returns the list of available audio codecs.
 *
 * This list is unmodifiable. The ->data field of the MSList points a PayloadType
 * structure holding the codec information.
 * It is possible to make copy of the list with ms_list_copy() in order to modify it
 * (such as the order of codecs).
**/
aymeric's avatar
aymeric committed
990
991
992
993
994
const MSList *linphone_core_get_audio_codecs(const LinphoneCore *lc)
{
	return lc->codecs_conf.audio_codecs;
}

995
996
997
998
999
1000
1001
1002
/**
 * Returns the list of available video codecs.
 *
 * This list is unmodifiable. The ->data field of the MSList points a PayloadType
 * structure holding the codec information.
 * It is possible to make copy of the list with ms_list_copy() in order to modify it
 * (such as the order of codecs).
**/
aymeric's avatar
aymeric committed
1003
1004
1005
1006
1007
const MSList *linphone_core_get_video_codecs(const LinphoneCore *lc)
{
	return lc->codecs_conf.video_codecs;
}

smorlat's avatar
smorlat committed
1008
1009
1010
1011
1012
1013
1014
/**
 * Sets the local "from" identity.
 *
 * @ingroup proxies
 * This data is used in absence of any proxy configuration or when no
 * default proxy configuration is set. See LinphoneProxyConfig
**/
aymeric's avatar
aymeric committed
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
int linphone_core_set_primary_contact(LinphoneCore *lc, const char *contact)
{
	osip_from_t *ctt=NULL;
	osip_from_init(&ctt);
	if (osip_from_parse(ctt,contact)!=0){
		ms_error("Bad contact url: %s",contact);
		osip_from_free(ctt);
		return -1;
	}
	if (lc->sip_conf.contact!=NULL) ms_free(lc->sip_conf.contact);
	lc->sip_conf.contact=ms_strdup(contact);
	if (lc->sip_conf.guessed_contact!=NULL){
		ms_free(lc->sip_conf.guessed_contact);
		lc->sip_conf.guessed_contact=NULL;
	}
	osip_from_free(ctt);
	return 0;
}


/*result must be an array of chars at least LINPHONE_IPADDR_SIZE */
void linphone_core_get_local_ip(LinphoneCore *lc, const char *dest, char *result){
	if (lc->apply_nat_settings){
		apply_nat_settings(lc);
		lc->apply_nat_settings=FALSE;
	}
	if (linphone_core_get_firewall_policy(lc)==LINPHONE_POLICY_USE_NAT_ADDRESS){
		strncpy(result,linphone_core_get_nat_address(lc),LINPHONE_IPADDR_SIZE);
		return;
	}
1045
	if (dest==NULL) dest="87.98.157.38"; /*a public IP address*/
1046
1047
1048
	if (linphone_core_get_local_ip_for(dest,result)==0)
		return;
	/*else fallback to exosip routine that will attempt to find the most realistic interface */
aymeric's avatar
aymeric committed
1049
1050
1051
	if (eXosip_guess_localip(lc->sip_conf.ipv6_enabled ? AF_INET6 : AF_INET,result,LINPHONE_IPADDR_SIZE)<0){
		/*default to something */
		strncpy(result,lc->sip_conf.ipv6_enabled ? "::1" : "127.0.0.1",LINPHONE_IPADDR_SIZE);
1052
		ms_error("Could not find default routable ip address !");
1053
	}
aymeric's avatar
aymeric committed
1054
1055
}

smorlat's avatar
smorlat committed
1056
1057
1058
1059
1060
/**
 * Returns the default identity when no proxy configuration is used.
 *
 * @ingroup proxies
**/
aymeric's avatar
aymeric committed
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
const char *linphone_core_get_primary_contact(LinphoneCore *lc)
{
	char *identity;
	char tmp[LINPHONE_IPADDR_SIZE];
	if (lc->sip_conf.guess_hostname){
		if (lc->sip_conf.guessed_contact==NULL || lc->sip_conf.loopback_only){
			char *guessed=NULL;
			osip_from_t *url;
			if (lc->sip_conf.guessed_contact!=NULL){
				ms_free(lc->sip_conf.guessed_contact);
				lc->sip_conf.guessed_contact=NULL;
			}
1073

aymeric's avatar
aymeric committed
1074
1075
			osip_from_init(&url);
			if (osip_from_parse(url,lc->sip_conf.contact)==0){
1076

aymeric's avatar
aymeric committed
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
			}else ms_error("Could not parse identity contact !");
			linphone_core_get_local_ip(lc, NULL, tmp);
			if (strcmp(tmp,"127.0.0.1")==0 || strcmp(tmp,"::1")==0 ){
				ms_warning("Local loopback network only !");
				lc->sip_conf.loopback_only=TRUE;
			}else lc->sip_conf.loopback_only=FALSE;
			osip_free(url->url->host);
			url->url->host=osip_strdup(tmp);
			if (url->url->port!=NULL){
				osip_free(url->url->port);
				url->url->port=NULL;
			}
			if (lc->sip_conf.sip_port!=5060){
				url->url->port=ortp_strdup_printf("%i",lc->sip_conf.sip_port);
			}
			osip_from_to_str(url,&guessed);
			lc->sip_conf.guessed_contact=guessed;
1094

aymeric's avatar
aymeric committed
1095
			osip_from_free(url);
1096

aymeric's avatar
aymeric committed
1097
1098
1099
1100
1101
1102
1103
1104
		}
		identity=lc->sip_conf.guessed_contact;
	}else{
		identity=lc->sip_conf.contact;
	}
	return identity;
}

smorlat's avatar
smorlat committed
1105
1106
1107
1108
1109
/**
 * Tells LinphoneCore to guess local hostname automatically in primary contact.
 *
 * @ingroup proxies
**/
aymeric's avatar
aymeric committed
1110
1111
1112
void linphone_core_set_guess_hostname(LinphoneCore *lc, bool_t val){
	lc->sip_conf.guess_hostname=val;
}
smorlat's avatar
smorlat committed
1113
1114
1115
1116
1117
1118

/**
 * Returns TRUE if hostname part of primary contact is guessed automatically.
 *
 * @ingroup proxies
**/
aymeric's avatar
aymeric committed
1119
1120
1121
1122
bool_t linphone_core_get_guess_hostname(LinphoneCore *lc){
	return lc->sip_conf.guess_hostname;
}

smorlat's avatar
smorlat committed
1123
1124
1125
1126
1127
1128
/**
 * Same as linphone_core_get_primary_contact() but the result is a LinphoneAddress object
 * instead of const char*
 *
 * @ingroup proxies
**/
1129
1130
LinphoneAddress *linphone_core_get_primary_contact_parsed(LinphoneCore *lc){
	return linphone_address_new(linphone_core_get_primary_contact(lc));
aymeric's avatar
aymeric committed
1131
1132
}

smorlat's avatar
smorlat committed
1133
1134
1135
1136
1137
1138
1139
/**
 * Sets the list of audio codecs.
 *
 * @ingroup media_parameters
 * The list is taken by the LinphoneCore thus the application should not free it.
 * This list is made of struct PayloadType describing the codec parameters.
**/
aymeric's avatar
aymeric committed
1140
1141
1142
1143
1144
1145
1146
int linphone_core_set_audio_codecs(LinphoneCore *lc, MSList *codecs)
{
	if (lc->codecs_conf.audio_codecs!=NULL) ms_list_free(lc->codecs_conf.audio_codecs);
	lc->codecs_conf.audio_codecs=codecs;
	return 0;
}

smorlat's avatar
smorlat committed
1147
1148
1149
1150
1151
1152
1153
/**
 * Sets the list of video codecs.
 *
 * @ingroup media_parameters
 * The list is taken by the LinphoneCore thus the application should not free it.
 * This list is made of struct PayloadType describing the codec parameters.
**/
aymeric's avatar
aymeric committed
1154
1155
1156
1157
1158
1159
1160
int linphone_core_set_video_codecs(LinphoneCore *lc, MSList *codecs)
{
	if (lc->codecs_conf.video_codecs!=NULL) ms_list_free(lc->codecs_conf.video_codecs);
	lc->codecs_conf.video_codecs=codecs;
	return 0;
}

1161
const MSList * linphone_core_get_friend_list(const LinphoneCore *lc)
aymeric's avatar
aymeric committed
1162
1163
1164
1165
{
	return lc->friends;
}

smorlat's avatar
smorlat committed
1166
1167
1168
1169
1170
/**
 * Returns the nominal jitter buffer size in milliseconds.
 *
 * @ingroup media_parameters
**/
aymeric's avatar
aymeric committed
1171
1172
1173
1174
1175
int linphone_core_get_audio_jittcomp(LinphoneCore *lc)
{
	return lc->rtp_conf.audio_jitt_comp;
}

smorlat's avatar
smorlat committed
1176
1177
1178
1179
1180
/**
 * Returns the UDP port used for audio streaming.
 *
 * @ingroup network_parameters
**/
aymeric's avatar
aymeric committed
1181
1182
1183
1184
1185
int linphone_core_get_audio_port(const LinphoneCore *lc)
{
	return lc->rtp_conf.audio_rtp_port;
}

smorlat's avatar
smorlat committed
1186
1187
1188
1189
1190
/**
 * Returns the UDP port used for video streaming.
 *
 * @ingroup network_parameters
**/
aymeric's avatar
aymeric committed
1191
1192
1193
1194
int linphone_core_get_video_port(const LinphoneCore *lc){
	return lc->rtp_conf.video_rtp_port;
}

smorlat's avatar
smorlat committed
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204

/**
 * Returns the value in seconds of the no-rtp timeout.
 *
 * @ingroup media_parameters
 * When no RTP or RTCP packets have been received for a while
 * LinphoneCore will consider the call is broken (remote end crashed or
 * disconnected from the network), and thus will terminate the call.
 * The no-rtp timeout is the duration above which the call is considered broken.
**/
aymeric's avatar
aymeric committed
1205
1206
1207
1208
int linphone_core_get_nortp_timeout(const LinphoneCore *lc){
	return lc->rtp_conf.nortp_timeout;
}

smorlat's avatar
smorlat committed
1209
1210
1211
1212
1213
/**
 * Sets the nominal audio jitter buffer size in milliseconds.
 *
 * @ingroup media_parameters
**/
aymeric's avatar
aymeric committed
1214
1215
1216
1217
1218
void linphone_core_set_audio_jittcomp(LinphoneCore *lc, int value)
{
	lc->rtp_conf.audio_jitt_comp=value;
}

smorlat's avatar
smorlat committed
1219
1220
1221
1222
1223
/**
 * Sets the UDP port used for audio streaming.
 *
 * @ingroup network_parameters
**/
aymeric's avatar
aymeric committed
1224
1225
1226
1227
1228
void linphone_core_set_audio_port(LinphoneCore *lc, int port)
{
	lc->rtp_conf.audio_rtp_port=port;
}

smorlat's avatar
smorlat committed
1229
1230
1231
1232
1233
/**
 * Sets the UDP port used for video streaming.
 *
 * @ingroup network_parameters
**/
aymeric's avatar
aymeric committed
1234
1235
1236
1237
void linphone_core_set_video_port(LinphoneCore *lc, int port){
	lc->rtp_conf.video_rtp_port=port;
}

smorlat's avatar
smorlat committed
1238
1239
1240
1241
1242
1243
/**
 * Sets the no-rtp timeout value in seconds.
 * 
 * @ingroup media_parameters
 * See linphone_core_get_nortp_timeout() for details.
**/
aymeric's avatar
aymeric committed
1244
1245
1246
1247
void linphone_core_set_nortp_timeout(LinphoneCore *lc, int nortp_timeout){
	lc->rtp_conf.nortp_timeout=nortp_timeout;
}

smorlat's avatar
smorlat committed
1248
1249
1250
1251
1252
/**
 * Indicates whether SIP INFO is used for sending digits.
 *
 * @ingroup media_parameters
**/
aymeric's avatar
aymeric committed
1253
1254
1255
1256
1257
bool_t linphone_core_get_use_info_for_dtmf(LinphoneCore *lc)
{
	return lc->sip_conf.use_info;
}

smorlat's avatar
smorlat committed
1258
1259
1260
1261
1262
/**
 * Sets whether SIP INFO is to be used for sending digits.
 *
 * @ingroup media_parameters
**/
aymeric's avatar
aymeric committed
1263
1264
1265
1266
1267
void linphone_core_set_use_info_for_dtmf(LinphoneCore *lc,bool_t use_info)
{
	lc->sip_conf.use_info=use_info;
}

smorlat's avatar
smorlat committed
1268
1269
1270
1271
1272
/**
 * Indicates whether RFC2833 is used for sending digits.
 *
 * @ingroup media_parameters
**/
1273
1274
1275
1276
1277
bool_t linphone_core_get_use_rfc2833_for_dtmf(LinphoneCore *lc)
{
	return lc->sip_conf.use_rfc2833;
}

smorlat's avatar
smorlat committed
1278
1279
1280
1281
1282
/**
 * Sets whether RFC2833 is to be used for sending digits.
 *
 * @ingroup media_parameters
**/
1283
1284
1285
1286
1287
void linphone_core_set_use_rfc2833_for_dtmf(LinphoneCore *lc,bool_t use_rfc2833)
{
	lc->sip_conf.use_rfc2833=use_rfc2833;
}

smorlat's avatar
smorlat committed
1288
1289
1290
1291
1292
/**
 * Returns the UDP port used by SIP.
 *
 * @ingroup network_parameters
**/
aymeric's avatar
aymeric committed
1293
1294
1295
1296
1297
1298
int linphone_core_get_sip_port(LinphoneCore *lc)
{
	return lc->sip_conf.sip_port;
}

static bool_t exosip_running=FALSE;
smorlat's avatar
smorlat committed
1299
1300
1301
static char _ua_name[64]="Linphone";
static char _ua_version[64]=LINPHONE_VERSION;

1302
static void apply_user_agent(void){
smorlat's avatar
smorlat committed
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
	char ua_string[256];
	snprintf(ua_string,sizeof(ua_string),"%s/%s (eXosip2/%s)",_ua_name,_ua_version,
#ifdef HAVE_EXOSIP_GET_VERSION
		 eXosip_get_version()
#else
		 "unknown"
#endif
	);
	eXosip_set_user_agent(ua_string);
}

smorlat's avatar
smorlat committed
1314
1315
1316
1317
1318
/**
 * Sets the user agent string used in SIP messages.
 *
 * @ingroup misc
**/
smorlat's avatar
smorlat committed
1319
1320
1321
1322
void linphone_core_set_user_agent(const char *name, const char *ver){
	strncpy(_ua_name,name,sizeof(_ua_name)-1);
	strncpy(_ua_version,ver,sizeof(_ua_version));
}
aymeric's avatar
aymeric committed
1323

smorlat's avatar
smorlat committed
1324
1325
1326
1327
1328
/**
 * Sets the UDP port to be used by SIP.
 *
 * @ingroup network_parameters
**/
aymeric's avatar
aymeric committed
1329
1330
1331
1332
1333
1334
1335
1336
void linphone_core_set_sip_port(LinphoneCore *lc,int port)
{
	const char *anyaddr;
	int err=0;
	if (port==lc->sip_conf.sip_port) return;
	lc->sip_conf.sip_port=port;
	if (exosip_running) eXosip_quit();
	eXosip_init();
smorlat's avatar
smorlat committed
1337
1338
1339
	err=0;
	eXosip_set_option(13,&err); /*13=EXOSIP_OPT_SRV_WITH_NAPTR, as it is an enum value, we can't use it unless we are sure of the
					version of eXosip, which is not the case*/
aymeric's avatar
aymeric committed
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
	eXosip_enable_ipv6(lc->sip_conf.ipv6_enabled);
	if (lc->sip_conf.ipv6_enabled)
		anyaddr="::0";
	else
		anyaddr="0.0.0.0";
	err=eXosip_listen_addr (IPPROTO_UDP, anyaddr, port,
		lc->sip_conf.ipv6_enabled ?  PF_INET6 : PF_INET, 0);
	if (err<0){
		char *msg=ortp_strdup_printf("UDP port %i seems already in use ! Cannot initialize.",port);
		ms_warning(msg);
		lc->vtable.display_warning(lc,msg);
		ms_free(msg);
		return;
	}
#ifdef VINCENT_MAURY_RSVP
	/* tell exosip the qos settings according to default linphone parameters */
	eXosip_set_rsvp_mode (lc->rsvp_enable);
	eXosip_set_rpc_mode (lc->rpc_enable);
#endif
1359
	apply_user_agent();
aymeric's avatar
aymeric committed
1360
1361
1362
	exosip_running=TRUE;
}

smorlat's avatar
smorlat committed
1363
1364
1365
1366
1367
1368
/**
 * Returns TRUE if IPv6 is enabled.
 *
 * @ingroup network_parameters
 * See linphone_core_enable_ipv6() for more details on how IPv6 is supported in liblinphone.
**/
aymeric's avatar
aymeric committed
1369
1370
1371
bool_t linphone_core_ipv6_enabled(LinphoneCore *lc){
	return lc->sip_conf.ipv6_enabled;
}
smorlat's avatar
smorlat committed
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381

/**
 * Turns IPv6 support on or off.
 *
 * @ingroup network_parameters
 *
 * @note IPv6 support is exclusive with IPv4 in liblinphone:
 * when IPv6 is turned on, IPv4 calls won't be possible anymore.
 * By default IPv6 support is off.
**/
aymeric's avatar
aymeric committed
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
void linphone_core_enable_ipv6(LinphoneCore *lc, bool_t val){
	if (lc->sip_conf.ipv6_enabled!=val){
		lc->sip_conf.ipv6_enabled=val;
		if (exosip_running){
			/* we need to restart eXosip */
			linphone_core_set_sip_port(lc, lc->sip_conf.sip_port);
		}
	}
}

static void display_bandwidth(RtpSession *as, RtpSession *vs){
	ms_message("bandwidth usage: audio=[d=%.1f,u=%.1f] video=[d=%.1f,u=%.1f] kbit/sec",
	(as!=NULL) ? (rtp_session_compute_recv_bandwidth(as)*1e-3) : 0,
	(as!=NULL) ? (rtp_session_compute_send_bandwidth(as)*1e-3) : 0,
	(vs!=NULL) ? (rtp_session_compute_recv_bandwidth(vs)*1e-3) : 0,
	(vs!=NULL) ? (rtp_session_compute_send_bandwidth(vs)*1e-3) : 0);
}

static void linphone_core_disconnected(LinphoneCore *lc){
	lc->vtable.display_warning(lc,_("Remote end seems to have disconnected, the call is going to be closed."));
	linphone_core_terminate_call(lc,NULL);
}

1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
static void proxy_update(LinphoneCore *lc, time_t curtime){
	bool_t doit=FALSE;
	static time_t last_check=0;
	static bool_t last_status=FALSE;
	if (lc->sip_conf.register_only_when_network_is_up){
		char result[LINPHONE_IPADDR_SIZE];
		/* only do the network up checking every five seconds */
		if (last_check==0 || (curtime-last_check)>=5){
			if (eXosip_guess_localip(lc->sip_conf.ipv6_enabled ? AF_INET6 : AF_INET,result,LINPHONE_IPADDR_SIZE)==0){
				if (strcmp(result,"::1")!=0 && strcmp(result,"127.0.0.1")!=0){
					last_status=TRUE;
					ms_message("Network is up, registering now (%s)",result);
				}else last_status=FALSE;
			}
			last_check=curtime;
		}
		doit=last_status;
	}else doit=TRUE;
	if (doit) ms_list_for_each(lc->sip_conf.proxies,(void (*)(void*))&linphone_proxy_config_update);
}

1426
1427
1428
1429
static void assign_buddy_info(LinphoneCore *lc, BuddyInfo *info){
	LinphoneFriend *lf=linphone_core_get_friend_by_uri(lc,info->sip_uri);
	if (lf!=NULL){
		lf->info=info;
1430
1431
1432
		ms_message("%s has a BuddyInfo assigned with image %p",info->sip_uri, info->image_data);
		if (lc->vtable.buddy_info_updated)
			lc->vtable.buddy_info_updated(lc,lf);
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
	}else{
		ms_warning("Could not any friend with uri %s",info->sip_uri);
	}
}

static void analyze_buddy_lookup_results(LinphoneCore *lc, LinphoneProxyConfig *cfg){
	MSList *elem;
	SipSetupContext *ctx=linphone_proxy_config_get_sip_setup_context(cfg);
	for (elem=lc->bl_reqs;elem!=NULL;elem=ms_list_next(elem)){
		BuddyLookupRequest *req=(BuddyLookupRequest *)elem->data;
		if (req->status==BuddyLookupDone || req->status==BuddyLookupFailure){
			if (req->results!=NULL){
				BuddyInfo *i=(BuddyInfo*)req->results->data;
				ms_list_free(req->results);
				req->results=NULL;
				assign_buddy_info(lc,i);
			}
			sip_setup_context_buddy_lookup_free(ctx,req);
			elem->data=NULL;
		}
	}
	/*purge completed requests */
	while((elem=ms_list_find(lc->bl_reqs,NULL))!=NULL){
		lc->bl_reqs=ms_list_remove_link(lc->bl_reqs,elem);
	}
}

static void linphone_core_grab_buddy_infos(LinphoneCore *lc, LinphoneProxyConfig *cfg){
	const MSList *elem;
	SipSetupContext *ctx=linphone_proxy_config_get_sip_setup_context(cfg);
	for(elem=linphone_core_get_friend_list(lc);elem!=NULL;elem=elem->next){
		LinphoneFriend *lf=(LinphoneFriend*)elem->data;