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

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
18
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
aymeric's avatar
aymeric committed
19 20 21
*/

#include "linphonecore.h"
smorlat's avatar
smorlat committed
22
#include "sipsetup.h"
aymeric's avatar
aymeric committed
23 24
#include "lpconfig.h"
#include "private.h"
25
#include "quality_reporting.h"
johan's avatar
johan committed
26
#include "lime.h"
27
#include "conference_private.h"
28

29 30 31 32
#ifdef SQLITE_STORAGE_ENABLED
#include "sqlite3_bctbx_vfs.h"
#endif

Yann Diorcet's avatar
Yann Diorcet committed
33
#include <math.h>
34 35
#include <sys/types.h>
#include <sys/stat.h>
36
#include <ortp/telephonyevents.h>
37
#include <mediastreamer2/zrtp.h>
johan's avatar
johan committed
38
#include <mediastreamer2/dtls_srtp.h>
aymeric's avatar
aymeric committed
39
#include "mediastreamer2/mediastream.h"
40
#include "mediastreamer2/msfactory.h"
41
#include "mediastreamer2/mseventqueue.h"
smorlat's avatar
smorlat committed
42
#include "mediastreamer2/msvolume.h"
43
#include "mediastreamer2/msequalizer.h"
44
#include "mediastreamer2/dtmfgen.h"
45
#include "mediastreamer2/msjpegwriter.h"
aymeric's avatar
aymeric committed
46

47
#ifdef INET6
48
#ifndef _WIN32
49
#include <netdb.h>
aymeric's avatar
aymeric committed
50 51 52
#endif
#endif

53
#ifdef HAVE_CONFIG_H
54
#include "config.h"
55
#include "liblinphone_gitversion.h"
56 57
#endif

58 59 60
#ifdef __APPLE__
#include "TargetConditionals.h"
#endif
61

62
#ifdef HAVE_ZLIB
Ghislain MARY's avatar
Ghislain MARY committed
63
#define COMPRESSED_LOG_COLLECTION_EXTENSION "gz"
64
#ifdef _WIN32
65 66
#include <fcntl.h>
#include <io.h>
67
#ifndef fileno
68
#define fileno _fileno
69
#endif
70
#define unlink _unlink
71 72 73 74 75
#define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
#else
#define SET_BINARY_MODE(file)
#endif
#include <zlib.h>
76
#else
Ghislain MARY's avatar
Ghislain MARY committed
77
#define COMPRESSED_LOG_COLLECTION_EXTENSION "txt"
78
#endif
Ghislain MARY's avatar
Ghislain MARY committed
79 80 81 82
#define LOG_COLLECTION_DEFAULT_PATH "."
#define LOG_COLLECTION_DEFAULT_PREFIX "linphone"
#define LOG_COLLECTION_DEFAULT_MAX_FILE_SIZE (10 * 1024 * 1024)

83

smorlat's avatar
smorlat committed
84
/*#define UNSTANDART_GSM_11K 1*/
aymeric's avatar
aymeric committed
85

86 87
#define ROOT_CA_FILE PACKAGE_DATA_DIR "/linphone/rootca.pem"

88 89 90 91 92 93 94
static const char *liblinphone_version=
#ifdef LIBLINPHONE_GIT_VERSION
	LIBLINPHONE_GIT_VERSION
#else
	LIBLINPHONE_VERSION
#endif
;
95
static OrtpLogFunc liblinphone_log_func = NULL;
Ghislain MARY's avatar
Ghislain MARY committed
96
static LinphoneLogCollectionState liblinphone_log_collection_state = LinphoneLogCollectionDisabled;
97
static char * liblinphone_log_collection_path = NULL;
Ghislain MARY's avatar
Ghislain MARY committed
98
static char * liblinphone_log_collection_prefix = NULL;
99
static size_t liblinphone_log_collection_max_file_size = LOG_COLLECTION_DEFAULT_MAX_FILE_SIZE;
100
static ortp_mutex_t liblinphone_log_collection_mutex;
101 102
static FILE * liblinphone_log_collection_file = NULL;
static size_t liblinphone_log_collection_file_size = 0;
103
static bool_t liblinphone_serialize_logs = FALSE;
104
static void set_network_reachable(LinphoneCore* lc,bool_t isReachable, time_t curtime);
105 106
static void set_sip_network_reachable(LinphoneCore* lc,bool_t isReachable, time_t curtime);
static void set_media_network_reachable(LinphoneCore* lc,bool_t isReachable);
Simon Morlat's avatar
Simon Morlat committed
107
static void linphone_core_run_hooks(LinphoneCore *lc);
aymeric's avatar
aymeric committed
108 109

#include "enum.h"
110
#include "contact_providers_priv.h"
111

112

113
const char *linphone_core_get_nat_address_resolved(LinphoneCore *lc);
aymeric's avatar
aymeric committed
114 115
static void toggle_video_preview(LinphoneCore *lc, bool_t val);

116
#if defined(LINPHONE_WINDOWS_PHONE) || defined(LINPHONE_WINDOWS_UNIVERSAL)
117
#define SOUNDS_PREFIX "Assets/Sounds/"
118 119
#else
#define SOUNDS_PREFIX
120
#endif
aymeric's avatar
aymeric committed
121
/* relative path where is stored local ring*/
122
#define LOCAL_RING SOUNDS_PREFIX "rings/oldphone-mono.wav"
123
#define LOCAL_RING_MKV SOUNDS_PREFIX "rings/notes_of_the_optimistic.mkv"
aymeric's avatar
aymeric committed
124
/* same for remote ring (ringback)*/
125
#define REMOTE_RING SOUNDS_PREFIX "ringback.wav"
126

127 128
#define HOLD_MUSIC SOUNDS_PREFIX "toy-mono.wav"
#define HOLD_MUSIC_MKV SOUNDS_PREFIX "dont_wait_too_long.mkv"
aymeric's avatar
aymeric committed
129

Simon Morlat's avatar
Simon Morlat committed
130
extern SalCallbacks linphone_sal_callbacks;
aymeric's avatar
aymeric committed
131 132 133 134 135 136 137 138 139 140 141 142

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

143

144 145 146 147 148 149 150 151 152 153 154 155
/**
 * 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;
}

156 157
int linphone_core_get_current_call_duration(const LinphoneCore *lc){
	LinphoneCall *call=linphone_core_get_current_call((LinphoneCore *)lc);
158 159
	if (call)  return linphone_call_get_duration(call);
	return -1;
160 161 162 163
}

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
164
	if (call==NULL) return NULL;
165
	return linphone_call_get_remote_address(call);
smorlat's avatar
smorlat committed
166 167
}

Simon Morlat's avatar
Simon Morlat committed
168
static void linphone_core_log_collection_handler(const char *domain, OrtpLogLevel level, const char *fmt, va_list args);
jehan's avatar
jehan committed
169

170
void linphone_core_set_log_handler(OrtpLogFunc logfunc) {
171
	if (ortp_get_log_handler() == linphone_core_log_collection_handler) {
172 173 174 175
		ms_message("There is already a log collection handler, keep it");
		liblinphone_log_func = logfunc;
	} else
		ortp_set_log_handler(logfunc);
176 177 178 179 180 181 182 183
}

void linphone_core_set_log_file(FILE *file) {
	if (file == NULL) file = stdout;
	ortp_set_log_file(file);
}

void linphone_core_set_log_level(OrtpLogLevel loglevel) {
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
	OrtpLogLevel mask = loglevel;
	switch (loglevel) {
		case ORTP_TRACE:
		case ORTP_DEBUG:
			mask |= ORTP_DEBUG;
		case ORTP_MESSAGE:
			mask |= ORTP_MESSAGE;
		case ORTP_WARNING:
			mask |= ORTP_WARNING;
		case ORTP_ERROR:
			mask |= ORTP_ERROR;
		case ORTP_FATAL:
			mask |= ORTP_FATAL;
			break;
		case ORTP_LOGLEV_END:
			break;
	}
	linphone_core_set_log_level_mask(mask);
202 203 204
}

void linphone_core_set_log_level_mask(OrtpLogLevel loglevel) {
Simon Morlat's avatar
Simon Morlat committed
205
	ortp_set_log_level_mask(NULL, loglevel);
206
	bctbx_set_log_level_mask(NULL, loglevel);
207
	if (loglevel == 0) {
208
		sal_disable_log();
209
	} else {
210
		sal_enable_log();
211
	}
212 213
}

214 215 216 217 218 219 220 221 222 223 224 225 226
static int _open_log_collection_file_with_idx(int idx) {
	struct stat statbuf;
	char *log_filename;

	log_filename = ortp_strdup_printf("%s/%s%d.log",
		liblinphone_log_collection_path ? liblinphone_log_collection_path : LOG_COLLECTION_DEFAULT_PATH,
		liblinphone_log_collection_prefix ? liblinphone_log_collection_prefix : LOG_COLLECTION_DEFAULT_PREFIX,
		idx);
	liblinphone_log_collection_file = fopen(log_filename, "a");
	ortp_free(log_filename);
	if (liblinphone_log_collection_file == NULL) return -1;

	fstat(fileno(liblinphone_log_collection_file), &statbuf);
227
	if ((size_t)statbuf.st_size > liblinphone_log_collection_max_file_size) {
228 229 230 231 232 233 234 235 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 264 265 266 267 268
		fclose(liblinphone_log_collection_file);
		return -1;
	}

	liblinphone_log_collection_file_size = statbuf.st_size;
	return 0;
}

static void _rotate_log_collection_files(void) {
	char *log_filename1;
	char *log_filename2;

	log_filename1 = ortp_strdup_printf("%s/%s1.log",
		liblinphone_log_collection_path ? liblinphone_log_collection_path : LOG_COLLECTION_DEFAULT_PATH,
		liblinphone_log_collection_prefix ? liblinphone_log_collection_prefix : LOG_COLLECTION_DEFAULT_PREFIX);
	log_filename2 = ortp_strdup_printf("%s/%s2.log",
		liblinphone_log_collection_path ? liblinphone_log_collection_path : LOG_COLLECTION_DEFAULT_PATH,
		liblinphone_log_collection_prefix ? liblinphone_log_collection_prefix : LOG_COLLECTION_DEFAULT_PREFIX);
	unlink(log_filename1);
	rename(log_filename2, log_filename1);
	ortp_free(log_filename1);
	ortp_free(log_filename2);
}

static void _open_log_collection_file(void) {
	if (_open_log_collection_file_with_idx(1) < 0) {
		if (_open_log_collection_file_with_idx(2) < 0) {
			_rotate_log_collection_files();
			_open_log_collection_file_with_idx(2);
		}
	}
}

static void _close_log_collection_file(void) {
	if (liblinphone_log_collection_file) {
		fclose(liblinphone_log_collection_file);
		liblinphone_log_collection_file = NULL;
		liblinphone_log_collection_file_size = 0;
	}
}

Simon Morlat's avatar
Simon Morlat committed
269
static void linphone_core_log_collection_handler(const char *domain, OrtpLogLevel level, const char *fmt, va_list args) {
270 271 272
	const char *lname="undef";
	char *msg;
	struct timeval tp;
273
	struct tm *lt;
274
	time_t tt;
275
	int ret;
276 277

	if (liblinphone_log_func != NULL) {
278
#ifndef _WIN32
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
279 280
		va_list args_copy;
		va_copy(args_copy, args);
Simon Morlat's avatar
Simon Morlat committed
281
		liblinphone_log_func(domain, level, fmt, args_copy);
Guillaume BIENKOWSKI's avatar
Guillaume BIENKOWSKI committed
282
		va_end(args_copy);
283 284 285
#else
		/* This works on 32 bits, luckily. */
		/* TODO: va_copy is available in Visual Studio 2013. */
Simon Morlat's avatar
Simon Morlat committed
286
		liblinphone_log_func(domain, level, fmt, args);
287
#endif
288 289 290 291 292
	}

	ortp_gettimeofday(&tp, NULL);
	tt = (time_t)tp.tv_sec;
	lt = localtime((const time_t*)&tt);
293 294 295 296 297 298 299 300 301 302 303 304 305

	if ((level & ORTP_DEBUG) != 0) {
		lname = "DEBUG";
	} else if ((level & ORTP_MESSAGE) != 0) {
		lname = "MESSAGE";
	} else if ((level & ORTP_WARNING) != 0) {
		lname = "WARNING";
	} else if ((level & ORTP_ERROR) != 0) {
		lname = "ERROR";
	} else if ((level & ORTP_FATAL) != 0) {
		lname = "FATAL";
	} else {
		ortp_fatal("Bad level !");
306 307 308
	}
	msg = ortp_strdup_vprintf(fmt, args);

309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324
	if (liblinphone_log_collection_file == NULL) {
		ortp_mutex_lock(&liblinphone_log_collection_mutex);
		_open_log_collection_file();
		ortp_mutex_unlock(&liblinphone_log_collection_mutex);
	}
	if (liblinphone_log_collection_file) {
		ortp_mutex_lock(&liblinphone_log_collection_mutex);
		ret = fprintf(liblinphone_log_collection_file,"%i-%.2i-%.2i %.2i:%.2i:%.2i:%.3i %s %s\n",
			1900 + lt->tm_year, lt->tm_mon + 1, lt->tm_mday, lt->tm_hour, lt->tm_min, lt->tm_sec, (int)(tp.tv_usec / 1000), lname, msg);
		fflush(liblinphone_log_collection_file);
		if (ret > 0) {
			liblinphone_log_collection_file_size += ret;
			if (liblinphone_log_collection_file_size > liblinphone_log_collection_max_file_size) {
				_close_log_collection_file();
				_open_log_collection_file();
			}
325
		}
326
		ortp_mutex_unlock(&liblinphone_log_collection_mutex);
327 328 329 330 331
	}

	ortp_free(msg);
}

Ghislain MARY's avatar
Ghislain MARY committed
332 333 334 335 336 337 338
const char * linphone_core_get_log_collection_path(void) {
	if (liblinphone_log_collection_path != NULL) {
		return liblinphone_log_collection_path;
	}
	return LOG_COLLECTION_DEFAULT_PATH;
}

339
void linphone_core_set_log_collection_path(const char *path) {
340 341 342 343 344 345 346
	if (liblinphone_log_collection_path != NULL) {
		ms_free(liblinphone_log_collection_path);
		liblinphone_log_collection_path = NULL;
	}
	if (path != NULL) {
		liblinphone_log_collection_path = ms_strdup(path);
	}
347 348
}

Ghislain MARY's avatar
Ghislain MARY committed
349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365
const char * linphone_core_get_log_collection_prefix(void) {
	if (liblinphone_log_collection_prefix != NULL) {
		return liblinphone_log_collection_prefix;
	}
	return LOG_COLLECTION_DEFAULT_PREFIX;
}

void linphone_core_set_log_collection_prefix(const char *prefix) {
	if (liblinphone_log_collection_prefix != NULL) {
		ms_free(liblinphone_log_collection_prefix);
		liblinphone_log_collection_prefix = NULL;
	}
	if (prefix != NULL) {
		liblinphone_log_collection_prefix = ms_strdup(prefix);
	}
}

366
size_t linphone_core_get_log_collection_max_file_size(void) {
Ghislain MARY's avatar
Ghislain MARY committed
367 368 369
	return liblinphone_log_collection_max_file_size;
}

370
void linphone_core_set_log_collection_max_file_size(size_t size) {
Ghislain MARY's avatar
Ghislain MARY committed
371 372 373
	liblinphone_log_collection_max_file_size = size;
}

374 375 376 377 378 379 380 381
const char *linphone_core_get_log_collection_upload_server_url(LinphoneCore *core) {
	return lp_config_get_string(core->config, "misc", "log_collection_upload_server_url", NULL);
}

void linphone_core_set_log_collection_upload_server_url(LinphoneCore *core, const char *server_url) {
	lp_config_set_string(core->config, "misc", "log_collection_upload_server_url", server_url);
}

Ghislain MARY's avatar
Ghislain MARY committed
382 383 384 385
LinphoneLogCollectionState linphone_core_log_collection_enabled(void) {
	return liblinphone_log_collection_state;
}

386
void linphone_core_enable_log_collection(LinphoneLogCollectionState state) {
387 388
	if (liblinphone_log_collection_state == state) return;

389 390 391
	/* at first call of this function, set liblinphone_log_func to the current
	 * ortp log function */
	if( liblinphone_log_func == NULL ){
392
		liblinphone_log_func = ortp_get_log_handler();
393
	}
Ghislain MARY's avatar
Ghislain MARY committed
394 395
	liblinphone_log_collection_state = state;
	if (state != LinphoneLogCollectionDisabled) {
396
		ortp_mutex_init(&liblinphone_log_collection_mutex, NULL);
397 398 399
		if (state == LinphoneLogCollectionEnabledWithoutPreviousLogHandler) {
			liblinphone_log_func = NULL;
		} else {
400
			liblinphone_log_func = ortp_get_log_handler();
401
		}
402 403 404 405 406 407
		ortp_set_log_handler(linphone_core_log_collection_handler);
	} else {
		ortp_set_log_handler(liblinphone_log_func);
	}
}

jehan's avatar
jehan committed
408
static void clean_log_collection_upload_context(LinphoneCore *lc) {
Ghislain MARY's avatar
Ghislain MARY committed
409 410 411 412
	char *filename = ms_strdup_printf("%s/%s_log.%s",
		liblinphone_log_collection_path ? liblinphone_log_collection_path : LOG_COLLECTION_DEFAULT_PATH,
		liblinphone_log_collection_prefix ? liblinphone_log_collection_prefix : LOG_COLLECTION_DEFAULT_PREFIX,
		COMPRESSED_LOG_COLLECTION_EXTENSION);
413
	unlink(filename);
414
	ms_free(filename);
jehan's avatar
jehan committed
415
	if (lc && lc->log_collection_upload_information) {
Ghislain MARY's avatar
Ghislain MARY committed
416
		linphone_content_unref(lc->log_collection_upload_information);
jehan's avatar
jehan committed
417 418
		lc->log_collection_upload_information=NULL;
	}
419 420
}

421 422 423 424
static void process_io_error_upload_log_collection(void *data, const belle_sip_io_error_event_t *event) {
	LinphoneCore *core = (LinphoneCore *)data;
	ms_error("I/O Error during log collection upload to %s", linphone_core_get_log_collection_upload_server_url(core));
	linphone_core_notify_log_collection_upload_state_changed(core, LinphoneCoreLogCollectionUploadStateNotDelivered, "I/O Error");
jehan's avatar
jehan committed
425
	clean_log_collection_upload_context(core);
426 427 428 429 430 431
}

static void process_auth_requested_upload_log_collection(void *data, belle_sip_auth_event_t *event) {
	LinphoneCore *core = (LinphoneCore *)data;
	ms_error("Error during log collection upload: auth requested to connect %s", linphone_core_get_log_collection_upload_server_url(core));
	linphone_core_notify_log_collection_upload_state_changed(core, LinphoneCoreLogCollectionUploadStateNotDelivered, "Auth requested");
jehan's avatar
jehan committed
432
	clean_log_collection_upload_context(core);
433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449
}

/**
 * Callback called when posting a log collection file to server (following rcs5.1 recommendation)
 *
 * @param[in] bh The body handler
 * @param[in] msg The belle sip message
 * @param[in] data The user data associated with the handler, contains the LinphoneCore object
 * @param[in] offset The current position in the input buffer
 * @param[in] buffer The ouput buffer where to copy the data to be uploaded
 * @param[in,out] size The size in byte of the data requested, as output it will contain the effective copied size
 *
 */
static int log_collection_upload_on_send_body(belle_sip_user_body_handler_t *bh, belle_sip_message_t *msg, void *data, size_t offset, uint8_t *buffer, size_t *size) {
	LinphoneCore *core = (LinphoneCore *)data;

	/* If we've not reach the end of file yet, fill the buffer with more data */
450
	if (offset < linphone_content_get_size(core->log_collection_upload_information)) {
Ghislain MARY's avatar
Ghislain MARY committed
451 452 453 454
		char *log_filename = ms_strdup_printf("%s/%s_log.%s",
			liblinphone_log_collection_path ? liblinphone_log_collection_path : LOG_COLLECTION_DEFAULT_PATH,
			liblinphone_log_collection_prefix ? liblinphone_log_collection_prefix : LOG_COLLECTION_DEFAULT_PREFIX,
			COMPRESSED_LOG_COLLECTION_EXTENSION);
455 456 457
#ifdef HAVE_ZLIB
		FILE *log_file = fopen(log_filename, "rb");
#else
458
		FILE *log_file = fopen(log_filename, "r");
459
#endif
460
		if (fseek(log_file, (long)offset, SEEK_SET)) {
jehan's avatar
jehan committed
461 462 463 464 465
			ms_error("Cannot seek file [%s] at position [%lu] errno [%s]",log_filename,(unsigned long)offset,strerror(errno));

		} else {
			*size = fread(buffer, 1, *size, log_file);
		}
466
		fclose(log_file);
467
		ms_free(log_filename);
jehan's avatar
jehan committed
468 469 470 471
		return BELLE_SIP_CONTINUE;
	} else {
		*size=0;
		return BELLE_SIP_STOP;
472 473
	}

jehan's avatar
jehan committed
474

475 476 477 478 479 480 481 482
}

/**
 * Callback called during upload of a log collection to server.
 * It is just forwarding the call and some parameters to the vtable defined callback.
 */
static void log_collection_upload_on_progress(belle_sip_body_handler_t *bh, belle_sip_message_t *msg, void *data, size_t offset, size_t total) {
	LinphoneCore *core = (LinphoneCore *)data;
483
	linphone_core_notify_log_collection_upload_progress_indication(core, offset, total);
484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513
}

/**
 * Callback function called when we have a response from server during the upload of the log collection to the server (rcs5.1 recommandation)
 * Note: The first post is empty and the server shall reply a 204 (No content) message, this will trigger a new post request to the server
 * to upload the file. The server response to this second post is processed by this same function
 *
 * @param[in] data The user-defined pointer associated with the request, it contains the LinphoneCore object
 * @param[in] event The response from server
 */
static void process_response_from_post_file_log_collection(void *data, const belle_http_response_event_t *event) {
	LinphoneCore *core = (LinphoneCore *)data;

	/* Check the answer code */
	if (event->response) {
		int code = belle_http_response_get_status_code(event->response);
		if (code == 204) { /* This is the reply to the first post to the server - an empty file */
			/* Start uploading the file */
			belle_http_request_listener_callbacks_t cbs = { 0 };
			belle_http_request_listener_t *l;
			belle_generic_uri_t *uri;
			belle_http_request_t *req;
			belle_sip_multipart_body_handler_t *bh;
			char* ua;
			char *first_part_header;
			belle_sip_user_body_handler_t *first_part_bh;

			linphone_core_notify_log_collection_upload_state_changed(core, LinphoneCoreLogCollectionUploadStateInProgress, NULL);

			/* Temporary storage for the Content-disposition header value */
514
			first_part_header = belle_sip_strdup_printf("form-data; name=\"File\"; filename=\"%s\"", linphone_content_get_name(core->log_collection_upload_information));
515 516

			/* Create a user body handler to take care of the file and add the content disposition and content-type headers */
517
			first_part_bh = belle_sip_user_body_handler_new(linphone_content_get_size(core->log_collection_upload_information), NULL, NULL, log_collection_upload_on_send_body, core);
518 519
			belle_sip_body_handler_add_header((belle_sip_body_handler_t *)first_part_bh, belle_sip_header_create("Content-disposition", first_part_header));
			belle_sip_free(first_part_header);
520 521
			belle_sip_body_handler_add_header((belle_sip_body_handler_t *)first_part_bh,
				(belle_sip_header_t *)belle_sip_header_content_type_create(linphone_content_get_type(core->log_collection_upload_information), linphone_content_get_subtype(core->log_collection_upload_information)));
522 523

			/* Insert it in a multipart body handler which will manage the boundaries of multipart message */
524
			bh = belle_sip_multipart_body_handler_new(log_collection_upload_on_progress, core, (belle_sip_body_handler_t *)first_part_bh, NULL);
525
			ua = ms_strdup_printf("%s/%s", linphone_core_get_user_agent(core), linphone_core_get_version());
526
			uri = belle_generic_uri_parse(linphone_core_get_log_collection_upload_server_url(core));
527
			req = belle_http_request_create("POST", uri, belle_sip_header_create("User-Agent", ua), NULL);
528 529 530 531 532 533
			ms_free(ua);
			belle_sip_message_set_body_handler(BELLE_SIP_MESSAGE(req), BELLE_SIP_BODY_HANDLER(bh));
			cbs.process_response = process_response_from_post_file_log_collection;
			cbs.process_io_error = process_io_error_upload_log_collection;
			cbs.process_auth_requested = process_auth_requested_upload_log_collection;
			l = belle_http_request_listener_create_from_callbacks(&cbs, core);
534
			belle_sip_object_data_set(BELLE_SIP_OBJECT(req), "http_request_listener", l, belle_sip_object_unref); // Ensure the listener object is destroyed when the request is destroyed
535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567
			belle_http_provider_send_request(core->http_provider, req, l);
		}
		if (code == 200) { /* The file has been uploaded correctly, get the server reply */
			xmlDocPtr xmlMessageBody;
			xmlNodePtr cur;
			xmlChar *file_url = NULL;
			const char *body = belle_sip_message_get_body((belle_sip_message_t *)event->response);
			xmlMessageBody = xmlParseDoc((const xmlChar *)body);
			cur = xmlDocGetRootElement(xmlMessageBody);
			if (cur != NULL) {
				cur = cur->xmlChildrenNode;
				while (cur != NULL) {
					if (!xmlStrcmp(cur->name, (const xmlChar *)"file-info")) { /* we found a file info node, check it has a type="file" attribute */
						xmlChar *typeAttribute = xmlGetProp(cur, (const xmlChar *)"type");
						if (!xmlStrcmp(typeAttribute, (const xmlChar *)"file")) { /* this is the node we are looking for */
							cur = cur->xmlChildrenNode; /* now loop on the content of the file-info node */
							while (cur != NULL) {
								if (!xmlStrcmp(cur->name, (const xmlChar *)"data")) {
									file_url = 	xmlGetProp(cur, (const xmlChar *)"url");
								}
								cur=cur->next;
							}
							xmlFree(typeAttribute);
							break;
						}
						xmlFree(typeAttribute);
					}
					cur = cur->next;
				}
			}
			if (file_url != NULL) {
				linphone_core_notify_log_collection_upload_state_changed(core, LinphoneCoreLogCollectionUploadStateDelivered, (const char *)file_url);
			}
jehan's avatar
jehan committed
568
			clean_log_collection_upload_context(core);
569 570 571 572
		}
	}
}

573
#ifdef HAVE_ZLIB
574 575 576
#define COMPRESS_FILE_PTR gzFile
#define COMPRESS_OPEN gzopen
#define COMPRESS_CLOSE gzclose
577
#else
578 579 580 581
#define COMPRESS_FILE_PTR FILE*
#define COMPRESS_OPEN fopen
#define COMPRESS_CLOSE fclose
#endif
582 583 584 585

/**
 * If zlib is not available the two log files are simply concatenated.
 */
586
static int compress_file(FILE *input_file, COMPRESS_FILE_PTR output_file) {
587
	char buffer[131072]; /* 128kB */
588
	size_t bytes;
Sylvain Berfini's avatar
Sylvain Berfini committed
589
	size_t total_bytes = 0;
590

Simon Morlat's avatar
Simon Morlat committed
591
	while ((bytes = fread(buffer, 1, sizeof(buffer), input_file)) > 0) {
592
#ifdef HAVE_ZLIB
593 594
		int res = gzwrite(output_file, buffer, (unsigned int)bytes);
		if (res < 0) return 0;
Sylvain Berfini's avatar
Sylvain Berfini committed
595
		total_bytes += (size_t)res;
596
#else
Sylvain Berfini's avatar
Sylvain Berfini committed
597
		total_bytes += fwrite(buffer, 1, bytes, output_file);
598
#endif
599
	}
Ghislain MARY's avatar
Ghislain MARY committed
600
	return (int)total_bytes;
601 602
}

603
static int prepare_log_collection_file_to_upload(const char *filename) {
604 605 606
	char *input_filename = NULL;
	char *output_filename = NULL;
	FILE *input_file = NULL;
607 608
	COMPRESS_FILE_PTR output_file = NULL;
	int ret = 0;
609

610
	ortp_mutex_lock(&liblinphone_log_collection_mutex);
Ghislain MARY's avatar
Ghislain MARY committed
611 612
	output_filename = ms_strdup_printf("%s/%s",
		liblinphone_log_collection_path ? liblinphone_log_collection_path : LOG_COLLECTION_DEFAULT_PATH, filename);
613
	output_file = COMPRESS_OPEN(output_filename, "wb");
614
	if (output_file == NULL) goto error;
Ghislain MARY's avatar
Ghislain MARY committed
615 616 617
	input_filename = ms_strdup_printf("%s/%s1.log",
		liblinphone_log_collection_path ? liblinphone_log_collection_path : LOG_COLLECTION_DEFAULT_PATH,
		liblinphone_log_collection_prefix ? liblinphone_log_collection_prefix : LOG_COLLECTION_DEFAULT_PREFIX);
618
	input_file = fopen(input_filename, "rb");
619 620
	if (input_file == NULL) goto error;
	ret = compress_file(input_file, output_file);
Sylvain Berfini's avatar
Sylvain Berfini committed
621
	if (ret <= 0) goto error;
622
	fclose(input_file);
623
	ms_free(input_filename);
Ghislain MARY's avatar
Ghislain MARY committed
624 625 626
	input_filename = ms_strdup_printf("%s/%s2.log",
		liblinphone_log_collection_path ? liblinphone_log_collection_path : LOG_COLLECTION_DEFAULT_PATH,
		liblinphone_log_collection_prefix ? liblinphone_log_collection_prefix : LOG_COLLECTION_DEFAULT_PREFIX);
627
	input_file = fopen(input_filename, "rb");
628 629
	if (input_file != NULL) {
		ret = compress_file(input_file, output_file);
Sylvain Berfini's avatar
Sylvain Berfini committed
630
		if (ret <= 0) goto error;
631
	}
632

633 634
error:
	if (input_file != NULL) fclose(input_file);
635
	if (output_file != NULL) COMPRESS_CLOSE(output_file);
636 637
	if (input_filename != NULL) ms_free(input_filename);
	if (output_filename != NULL) ms_free(output_filename);
638
	ortp_mutex_unlock(&liblinphone_log_collection_mutex);
639 640 641 642 643
	return ret;
}

static size_t get_size_of_file_to_upload(const char *filename) {
	struct stat statbuf;
Ghislain MARY's avatar
Ghislain MARY committed
644 645
	char *output_filename = ms_strdup_printf("%s/%s",
		liblinphone_log_collection_path ? liblinphone_log_collection_path : LOG_COLLECTION_DEFAULT_PATH, filename);
646 647 648
	FILE *output_file = fopen(output_filename, "rb");
	fstat(fileno(output_file), &statbuf);
	fclose(output_file);
649
	ms_free(output_filename);
650
	return statbuf.st_size;
651 652
}

653
void linphone_core_upload_log_collection(LinphoneCore *core) {
Ghislain MARY's avatar
Ghislain MARY committed
654
	if ((core->log_collection_upload_information == NULL) && (linphone_core_get_log_collection_upload_server_url(core) != NULL) && (liblinphone_log_collection_state != LinphoneLogCollectionDisabled)) {
655 656 657 658 659
		/* open a transaction with the server and send an empty request(RCS5.1 section 3.5.4.8.3.1) */
		belle_http_request_listener_callbacks_t cbs = { 0 };
		belle_http_request_listener_t *l;
		belle_generic_uri_t *uri;
		belle_http_request_t *req;
660
		char *name;
661

Ghislain MARY's avatar
Ghislain MARY committed
662
		core->log_collection_upload_information = linphone_core_create_content(core);
663
#ifdef HAVE_ZLIB
664 665
		linphone_content_set_type(core->log_collection_upload_information, "application");
		linphone_content_set_subtype(core->log_collection_upload_information, "gzip");
666
#else
667 668
		linphone_content_set_type(core->log_collection_upload_information, "text");
		linphone_content_set_subtype(core->log_collection_upload_information,"plain");
669
#endif
670
		name = ms_strdup_printf("%s_log.%s",
Ghislain MARY's avatar
Ghislain MARY committed
671 672
			liblinphone_log_collection_prefix ? liblinphone_log_collection_prefix : LOG_COLLECTION_DEFAULT_PREFIX,
			COMPRESSED_LOG_COLLECTION_EXTENSION);
673
		linphone_content_set_name(core->log_collection_upload_information, name);
Sylvain Berfini's avatar
Sylvain Berfini committed
674
		if (prepare_log_collection_file_to_upload(name) <= 0) {
675
			linphone_content_unref(core->log_collection_upload_information);
Sylvain Berfini's avatar
Sylvain Berfini committed
676
			core->log_collection_upload_information = NULL;
677 678
			ms_error("prepare_log_collection_file_to_upload(): error.");
			return;
Sylvain Berfini's avatar
Sylvain Berfini committed
679
		}
680
		linphone_content_set_size(core->log_collection_upload_information, get_size_of_file_to_upload(name));
681 682 683 684 685 686
		uri = belle_generic_uri_parse(linphone_core_get_log_collection_upload_server_url(core));
		req = belle_http_request_create("POST", uri, NULL, NULL, NULL);
		cbs.process_response = process_response_from_post_file_log_collection;
		cbs.process_io_error = process_io_error_upload_log_collection;
		cbs.process_auth_requested = process_auth_requested_upload_log_collection;
		l = belle_http_request_listener_create_from_callbacks(&cbs, core);
687
		belle_sip_object_data_set(BELLE_SIP_OBJECT(req), "http_request_listener", l, belle_sip_object_unref); // Ensure the listener object is destroyed when the request is destroyed
688
		belle_http_provider_send_request(core->http_provider, req, l);
689
		ms_free(name);
690 691 692
	} else {
		ms_warning("Could not upload log collection: log_collection_upload_information=%p, server_url=%s, log_collection_state=%d",
			core->log_collection_upload_information, linphone_core_get_log_collection_upload_server_url(core), liblinphone_log_collection_state);
693 694 695
	}
}

696
char * linphone_core_compress_log_collection(void) {
Ghislain MARY's avatar
Ghislain MARY committed
697 698 699 700 701
	char *filename = NULL;
	if (liblinphone_log_collection_state == LinphoneLogCollectionDisabled) return NULL;
	filename = ms_strdup_printf("%s_log.%s",
		liblinphone_log_collection_prefix ? liblinphone_log_collection_prefix : LOG_COLLECTION_DEFAULT_PREFIX,
		COMPRESSED_LOG_COLLECTION_EXTENSION);
702
	if (prepare_log_collection_file_to_upload(filename) <= 0) {
Ghislain MARY's avatar
Ghislain MARY committed
703 704 705 706 707 708 709 710
		ms_free(filename);
		return NULL;
	}
	ms_free(filename);
	return ms_strdup_printf("%s/%s_log.%s",
		liblinphone_log_collection_path ? liblinphone_log_collection_path : LOG_COLLECTION_DEFAULT_PATH,
		liblinphone_log_collection_prefix ? liblinphone_log_collection_prefix : LOG_COLLECTION_DEFAULT_PREFIX,
		COMPRESSED_LOG_COLLECTION_EXTENSION);
711 712
}

713
void linphone_core_reset_log_collection(void) {
714 715
	char *filename;
	ortp_mutex_lock(&liblinphone_log_collection_mutex);
716
	_close_log_collection_file();
jehan's avatar
jehan committed
717
	clean_log_collection_upload_context(NULL);
Ghislain MARY's avatar
Ghislain MARY committed
718 719 720
	filename = ms_strdup_printf("%s/%s1.log",
			liblinphone_log_collection_path ? liblinphone_log_collection_path : LOG_COLLECTION_DEFAULT_PATH,
			liblinphone_log_collection_prefix ? liblinphone_log_collection_prefix : LOG_COLLECTION_DEFAULT_PREFIX);
721 722
	unlink(filename);
	ms_free(filename);
Ghislain MARY's avatar
Ghislain MARY committed
723 724 725
	filename = ms_strdup_printf("%s/%s2.log",
		liblinphone_log_collection_path ? liblinphone_log_collection_path : LOG_COLLECTION_DEFAULT_PATH,
		liblinphone_log_collection_prefix ? liblinphone_log_collection_prefix : LOG_COLLECTION_DEFAULT_PREFIX);
726 727
	unlink(filename);
	ms_free(filename);
728 729
	liblinphone_log_collection_file = NULL;
	liblinphone_log_collection_file_size = 0;
730 731 732
	ortp_mutex_unlock(&liblinphone_log_collection_mutex);
}

aymeric's avatar
aymeric committed
733 734 735
void linphone_core_enable_logs(FILE *file){
	if (file==NULL) file=stdout;
	ortp_set_log_file(file);
736
	linphone_core_set_log_level(ORTP_MESSAGE);
aymeric's avatar
aymeric committed
737 738 739
}

void linphone_core_enable_logs_with_cb(OrtpLogFunc logfunc){
740
	linphone_core_set_log_level(ORTP_MESSAGE);
741
	linphone_core_set_log_handler(logfunc);
aymeric's avatar
aymeric committed
742 743
}

744
void linphone_core_disable_logs(void){
745
	linphone_core_set_log_level(ORTP_ERROR);
aymeric's avatar
aymeric committed
746 747
}

748 749 750 751
void linphone_core_serialize_logs(void) {
	liblinphone_serialize_logs = TRUE;
}

aymeric's avatar
aymeric committed
752

753
static void net_config_read (LinphoneCore *lc)
aymeric's avatar
aymeric committed
754 755 756 757
{
	int tmp;
	const char *tmpstr;
	LpConfig *config=lc->config;
758 759 760 761
	const char *nat_policy_ref;

	nat_policy_ref = lp_config_get_string(lc->config, "net", "nat_policy_ref", NULL);
	if (nat_policy_ref != NULL) {
762
		lc->nat_policy = linphone_core_create_nat_policy_from_config(lc, nat_policy_ref);
763
	}
aymeric's avatar
aymeric committed
764

765
	lc->net_conf.nat_address_ip = NULL;
aymeric's avatar
aymeric committed
766 767 768 769
	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);
770 771 772 773
	if (lc->nat_policy == NULL) /* For compatibility, now the STUN server is stored in the NAT policy. */
		linphone_core_set_stun_server(lc,lp_config_get_string(config,"net","stun_server",NULL));
	else
		linphone_core_set_stun_server(lc, linphone_nat_policy_get_stun_server(lc->nat_policy));
aymeric's avatar
aymeric committed
774 775 776 777 778
	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","nat_sdp_only",0);
	lc->net_conf.nat_sdp_only=tmp;
779
	tmp=lp_config_get_int(lc->config,"net","mtu",1300);
aymeric's avatar
aymeric committed
780
	linphone_core_set_mtu(lc,tmp);
781 782 783 784 785
	tmp=lp_config_get_int(lc->config,"net","download_ptime",-1);
	if (tmp !=-1 && linphone_core_get_download_ptime(lc) !=0) {
		/*legacy parameter*/
		linphone_core_set_download_ptime(lc,tmp);
	}
786 787
	tmp = lp_config_get_int(lc->config, "net", "dns_srv_enabled", 1);
	linphone_core_enable_dns_srv(lc, tmp);
788 789
	tmp = lp_config_get_int(lc->config, "net", "dns_search_enabled", 1);
	linphone_core_enable_dns_search(lc, tmp);
790 791

	/* This is to filter out unsupported firewall policies */
792 793
	if (nat_policy_ref == NULL)
		linphone_core_set_firewall_policy(lc, linphone_core_get_firewall_policy(lc));
aymeric's avatar
aymeric committed
794 795
}

796 797 798
static void build_sound_devices_table(LinphoneCore *lc){
	const char **devices;
	const char **old;
Ghislain MARY's avatar
Ghislain MARY committed
799
	size_t ndev;
800
	int i;
801 802
	const bctbx_list_t *elem=ms_snd_card_manager_get_list(ms_factory_get_snd_card_manager(lc->factory));
	ndev=bctbx_list_size(elem);
803 804 805 806 807 808 809
	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;
810
	if (old!=NULL) ms_free((void *)old);
811
}
aymeric's avatar
aymeric committed
812

813 814 815 816 817 818 819 820 821 822 823 824 825 826
static const char *get_default_local_ring(LinphoneCore * lc){
	if (linphone_core_file_format_supported(lc, "mkv")){
		return PACKAGE_SOUND_DIR "/" LOCAL_RING_MKV;
	}
	return PACKAGE_SOUND_DIR "/" LOCAL_RING;
}

static const char *get_default_onhold_music(LinphoneCore * lc){
	if (linphone_core_file_format_supported(lc, "mkv")){
		return PACKAGE_SOUND_DIR "/" HOLD_MUSIC_MKV;
	}
	return PACKAGE_SOUND_DIR "/" HOLD_MUSIC;
}

827
static void sound_config_read(LinphoneCore *lc)
aymeric's avatar
aymeric committed
828
{
829
	int tmp;
aymeric's avatar
aymeric committed
830 831
	const char *tmpbuf;
	const char *devid;
832
#ifdef __linux
aymeric's avatar
aymeric committed
833 834 835
	/*alsadev let the user use custom alsa device within linphone*/
	devid=lp_config_get_string(lc->config,"sound","alsadev",NULL);
	if (devid){
836 837 838 839 840 841 842 843 844 845
		MSSndCard* card;
		const char* delim=",";
		size_t l=strlen(devid);
		char* d=malloc(l+1);
		char* i;
		memcpy(d,devid,l+1);
		for (l=0,i=strpbrk(d+l,delim);i;i=strpbrk(d+l,delim)){
			char s=*i;
			*i='\0';
			card=ms_alsa_card_new_custom(d+l,d+l);
846
			ms_snd_card_manager_add_card(ms_factory_get_snd_card_manager(lc->factory),card);
847 848 849 850 851
			*i=s;
			l=i-d+1;
		}
		if(d[l]!='\0') {
			card=ms_alsa_card_new_custom(d+l,d+l);
852
			ms_snd_card_manager_add_card(ms_factory_get_snd_card_manager(lc->factory),card);
853 854
		}
		free(d);
aymeric's avatar
aymeric committed
855
	}
Simon Morlat's avatar
Simon Morlat committed
856
	tmp=lp_config_get_int(lc->config,"sound","alsa_forced_rate",-1);
857
	if (tmp>0) ms_alsa_card_set_forced_sample_rate(tmp);
aymeric's avatar
aymeric committed
858 859
#endif
	/* retrieve all sound devices */
860 861
	build_sound_devices_table(lc);

aymeric's avatar
aymeric committed
862 863
	devid=lp_config_get_string(lc->config,"sound","playback_dev_id",NULL);
	linphone_core_set_playback_device(lc,devid);
864

aymeric's avatar
aymeric committed
865 866
	devid=lp_config_get_string(lc->config,"sound","ringer_dev_id",NULL);
	linphone_core_set_ringer_device(lc,devid);
867

aymeric's avatar
aymeric committed
868 869
	devid=lp_config_get_string(lc->config,"sound","capture_dev_id",NULL);
	linphone_core_set_capture_device(lc,devid);
870

aymeric's avatar
aymeric committed
871 872 873 874 875 876 877 878 879 880
/*
	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]);
*/
881

882 883 884
	tmpbuf=lp_config_get_string(lc->config,"sound","local_ring",NULL);
	if (tmpbuf==NULL||ortp_file_exist(tmpbuf)!=0) {
		if (tmpbuf) ms_warning("%s does not exist",tmpbuf);
885
		tmpbuf = get_default_local_ring(lc);
aymeric's avatar
aymeric committed
886 887
	}
	linphone_core_set_ring(lc,tmpbuf);
888

889
	tmpbuf=PACKAGE_SOUND_DIR "/" REMOTE_RING;
aymeric's avatar
aymeric committed
890
	tmpbuf=lp_config_get_string(lc->config,"sound","remote_ring",tmpbuf);
Jehan Monnier's avatar
Jehan Monnier committed
891
	if (ortp_file_exist(tmpbuf)==-1){
892
		tmpbuf=PACKAGE_SOUND_DIR "/" REMOTE_RING;
aymeric's avatar
aymeric committed
893 894 895
	}
	if (strstr(tmpbuf,".wav")==NULL){
		/* it currently uses old sound files, so replace them */
896
		tmpbuf=PACKAGE_SOUND_DIR "/" REMOTE_RING;
aymeric's avatar
aymeric committed
897
	}
898
	linphone_core_set_ringback(lc,tmpbuf);
899

900
	linphone_core_set_play_file(lc,lp_config_get_string(lc->config,"sound","hold_music", get_default_onhold_music(lc)));
aymeric's avatar
aymeric committed
901
	lc->sound_conf.latency=0;
902
#ifndef __ios
Simon Morlat's avatar
Simon Morlat committed
903
	tmp=TRUE;
904
#else
Simon Morlat's avatar
Simon Morlat committed
905
	tmp=FALSE; /* on iOS we have builtin echo cancellation.*/
906
#endif
Simon Morlat's avatar
Simon Morlat committed
907
	tmp=lp_config_get_int(lc->config,"sound","echocancellation",tmp);
908
	linphone_core_enable_echo_cancellation(lc,tmp);
smorlat's avatar
smorlat committed
909 910
	linphone_core_enable_echo_limiter(lc,
		lp_config_get_int(lc->config,"sound","echolimiter",0));
Simon Morlat's avatar