call_log.c 23.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/*
linphone
Copyright (C) 2010-2014  Belledonne Communications SARL

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
17
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 19
*/

jehan's avatar
jehan committed
20
#ifndef __APPLE__ /*XOPEN_SOURCE icompilation issue  with xcode9 https://github.com/eclipse/omr/pull/1721*/
21
#define _XOPEN_SOURCE 700 /*required for strptime of GNU libc*/
jehan's avatar
jehan committed
22
#endif
23

24
#include <time.h>
25

26 27 28 29 30
#if !defined(_WIN32) && !defined(__ANDROID__) && !defined(__QNXNTO__)
	#include <langinfo.h>
	#include <iconv.h>
	#include <string.h>
#endif // if !defined(_WIN32) && !defined(__ANDROID__) && !defined(__QNXNTO__)
31

32
#define MAX_PATH_SIZE 1024
33

34 35
#include "c-wrapper/c-wrapper.h"

36 37 38
// TODO: From coreapi. Remove me later.
#include "private.h"

39 40 41 42 43
typedef struct _CallLogStorageResult {
	LinphoneCore *core;
	bctbx_list_t *result;
} CallLogStorageResult;

44 45 46 47
/*******************************************************************************
 * Internal functions                                                          *
 ******************************************************************************/

Ghislain MARY's avatar
Ghislain MARY committed
48 49 50 51 52 53
/*prevent a gcc bug with %c*/
static size_t my_strftime(char *s, size_t max, const char  *fmt,  const struct tm *tm){
	return strftime(s, max, fmt, tm);
}

static time_t string_to_time(const char *date){
54
#ifndef _WIN32
Ghislain MARY's avatar
Ghislain MARY committed
55 56 57 58 59 60 61 62
	struct tm tmtime={0};
	strptime(date,"%c",&tmtime);
	return mktime(&tmtime);
#else
	return 0;
#endif
}

63 64
static void set_call_log_date(LinphoneCallLog *cl, time_t start_time){
	struct tm loctime;
65
#ifdef _WIN32
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
#if !defined(_WIN32_WCE)
	loctime=*localtime(&start_time);
	/*FIXME*/
#endif /*_WIN32_WCE*/
#else
	localtime_r(&start_time,&loctime);
#endif
	my_strftime(cl->start_date,sizeof(cl->start_date),"%c",&loctime);
}

/*******************************************************************************
 * Private functions                                                           *
 ******************************************************************************/

void call_logs_write_to_config_file(LinphoneCore *lc){
81
	bctbx_list_t *elem;
82
	char logsection[32];
Simon Morlat's avatar
Simon Morlat committed
83
	int i;
84 85 86 87
	char *tmp;
	LpConfig *cfg=lc->config;

	if (linphone_core_get_global_state (lc)==LinphoneGlobalStartup) return;
88

89
	if (lc->max_call_logs == LINPHONE_MAX_CALL_HISTORY_UNLIMITED) return;
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117

	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_clean_section(cfg,logsection);
		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);
		if (cl->start_date_time)
			lp_config_set_int64(cfg,logsection,"start_date_time",(int64_t)cl->start_date_time);
		else 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);
		lp_config_set_float(cfg,logsection,"quality",cl->quality);
		lp_config_set_int(cfg,logsection,"video_enabled", cl->video_enabled);
		lp_config_set_string(cfg,logsection,"call_id",cl->call_id);
	}
	for(;i<lc->max_call_logs;++i){
		snprintf(logsection,sizeof(logsection),"call_log_%i",i);
		lp_config_clean_section(cfg,logsection);
	}
}

118
bctbx_list_t * linphone_core_read_call_logs_from_config_file(LinphoneCore *lc){
119 120 121 122 123
	char logsection[32];
	int i;
	const char *tmp;
	uint64_t sec;
	LpConfig *cfg=lc->config;
124
	bctbx_list_t *call_logs = NULL;
125

126 127 128
	for(i=0;;++i){
		snprintf(logsection,sizeof(logsection),"call_log_%i",i);
		if (lp_config_has_section(cfg,logsection)){
129 130
			LinphoneCallLog *cl;
			LinphoneAddress *from=NULL,*to=NULL;
131
			tmp=lp_config_get_string(cfg,logsection,"from",NULL);
132
			if (tmp) from=linphone_address_new(tmp);
133
			tmp=lp_config_get_string(cfg,logsection,"to",NULL);
134 135 136
			if (tmp) to=linphone_address_new(tmp);
			if (!from || !to)
				continue;
137 138
			cl=linphone_call_log_new(static_cast<LinphoneCallDir>(lp_config_get_int(cfg,logsection,"dir",0)),from,to);
			cl->status=static_cast<LinphoneCallStatus>(lp_config_get_int(cfg,logsection,"status",0));
Benjamin REIS's avatar
Benjamin REIS committed
139
			sec=(uint64_t)lp_config_get_int64(cfg,logsection,"start_date_time",0);
140 141 142 143 144 145 146 147
			if (sec) {
				/*new call log format with date expressed in seconds */
				cl->start_date_time=(time_t)sec;
				set_call_log_date(cl,cl->start_date_time);
			}else{
				tmp=lp_config_get_string(cfg,logsection,"start_date",NULL);
				if (tmp) {
					strncpy(cl->start_date,tmp,sizeof(cl->start_date));
148
					cl->start_date[sizeof(cl->start_date) - 1] = '\0';
149 150 151 152 153 154 155
					cl->start_date_time=string_to_time(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);
			cl->quality=lp_config_get_float(cfg,logsection,"quality",-1);
156
			cl->video_enabled=!!lp_config_get_int(cfg,logsection,"video_enabled",0);
157 158
			tmp=lp_config_get_string(cfg,logsection,"call_id",NULL);
			if (tmp) cl->call_id=ms_strdup(tmp);
159
			call_logs=bctbx_list_append(call_logs,cl);
160 161
		}else break;
	}
162
	return call_logs;
163 164 165 166 167 168 169
}


/*******************************************************************************
 * Public functions                                                            *
 ******************************************************************************/

170
LinphoneCallLog *linphone_core_create_call_log(LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to, LinphoneCallDir dir,
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
		int duration, time_t start_time, time_t connected_time, LinphoneCallStatus status, bool_t video_enabled, float quality) {
	LinphoneCallLog *log = linphone_call_log_new(dir, linphone_address_ref(from), linphone_address_ref(to));

	log->duration = duration;
	log->start_date_time = start_time;
	set_call_log_date(log,log->start_date_time);
	log->connected_date_time = connected_time;
	log->status = status;
	log->video_enabled = video_enabled;
	log->quality = quality;

	linphone_core_store_call_log(lc, log);

	return log;
}

187 188 189 190
const char *linphone_call_log_get_call_id(const LinphoneCallLog *cl){
	return cl->call_id;
}

Ronan's avatar
Ronan committed
191
LinphoneCallDir linphone_call_log_get_dir(const LinphoneCallLog *cl){
192 193 194
	return cl->dir;
}

Ronan's avatar
Ronan committed
195
int linphone_call_log_get_duration(const LinphoneCallLog *cl){
196 197 198
	return cl->duration;
}

Ronan's avatar
Ronan committed
199
const LinphoneAddress *linphone_call_log_get_from_address(const LinphoneCallLog *cl){
200 201 202 203 204 205 206
	return cl->from;
}

const rtp_stats_t *linphone_call_log_get_local_stats(const LinphoneCallLog *cl){
	return &cl->local_stats;
}

Ronan's avatar
Ronan committed
207
float linphone_call_log_get_quality(const LinphoneCallLog *cl){
208 209 210 211 212 213 214
	return cl->quality;
}

const char *linphone_call_log_get_ref_key(const LinphoneCallLog *cl){
	return cl->refkey;
}

215 216 217 218 219
const LinphoneAddress *linphone_call_log_get_local_address(const LinphoneCallLog *cl) {
	return (cl->dir == LinphoneCallIncoming) ? cl->to : cl->from;
}

const LinphoneAddress *linphone_call_log_get_remote_address(const LinphoneCallLog *cl){
220 221 222 223 224 225 226
	return (cl->dir == LinphoneCallIncoming) ? cl->from : cl->to;
}

const rtp_stats_t *linphone_call_log_get_remote_stats(const LinphoneCallLog *cl){
	return &cl->remote_stats;
}

Ronan's avatar
Ronan committed
227
time_t linphone_call_log_get_start_date(const LinphoneCallLog *cl){
228 229 230
	return cl->start_date_time;
}

Ronan's avatar
Ronan committed
231
LinphoneCallStatus linphone_call_log_get_status(const LinphoneCallLog *cl){
232 233 234
	return cl->status;
}

Ronan's avatar
Ronan committed
235
const LinphoneAddress *linphone_call_log_get_to_address(const LinphoneCallLog *cl){
236 237 238 239 240 241 242 243 244 245 246
	return cl->to;
}

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

Ronan's avatar
Ronan committed
247
char * linphone_call_log_to_str(const LinphoneCallLog *cl){
248
	const char *status;
249 250 251 252 253
	char *tmp;
	char *from=linphone_address_as_string (cl->from);
	char *to=linphone_address_as_string (cl->to);
	switch(cl->status){
		case LinphoneCallAborted:
254
			status="aborted";
255 256
			break;
		case LinphoneCallSuccess:
257
			status="completed";
258 259
			break;
		case LinphoneCallMissed:
260
			status="missed";
261
			break;
262
		case LinphoneCallAcceptedElsewhere:
263
			status="answered elsewhere";
264 265
			break;
		case LinphoneCallDeclinedElsewhere:
266
			status="declined elsewhere";
267
			break;
268
		default:
269
			status="unknown";
270
	}
271 272
	tmp=ms_strdup_printf("%s at %s\nFrom: %s\nTo: %s\nStatus: %s\nDuration: %i mn %i sec\n",
			(cl->dir==LinphoneCallIncoming) ? "Incoming call" : "Outgoing call",
273 274 275 276 277 278 279 280 281 282 283
			cl->start_date,
			from,
			to,
			status,
			cl->duration/60,
			cl->duration%60);
	ms_free(from);
	ms_free(to);
	return tmp;
}

Ronan's avatar
Ronan committed
284
bool_t linphone_call_log_video_enabled(const LinphoneCallLog *cl) {
285 286 287
	return cl->video_enabled;
}

Ronan's avatar
Ronan committed
288
bool_t linphone_call_log_was_conference(const LinphoneCallLog *cl) {
289 290 291
	return cl->was_conference;
}

Ronan's avatar
Ronan committed
292
const LinphoneErrorInfo *linphone_call_log_get_error_info(const LinphoneCallLog *cl){
293 294 295
	return cl->error_info;
}

296 297 298 299 300

/*******************************************************************************
 * Reference and user data handling functions                                  *
 ******************************************************************************/

301 302
void *linphone_call_log_get_user_data(const LinphoneCallLog *cl) {
	return cl->user_data;
303 304
}

305 306
void linphone_call_log_set_user_data(LinphoneCallLog *cl, void *ud) {
	cl->user_data = ud;
307 308
}

309 310 311 312 313 314 315 316
LinphoneCallLog * linphone_call_log_ref(LinphoneCallLog *cl) {
	belle_sip_object_ref(cl);
	return cl;
}

void linphone_call_log_unref(LinphoneCallLog *cl) {
	belle_sip_object_unref(cl);
}
317 318 319 320 321

/*******************************************************************************
 * Constructor and destructor functions                                        *
 ******************************************************************************/

322
static void _linphone_call_log_destroy(LinphoneCallLog *cl) {
Simon Morlat's avatar
Simon Morlat committed
323 324
	if (cl->from!=NULL) linphone_address_unref(cl->from);
	if (cl->to!=NULL) linphone_address_unref(cl->to);
325 326 327 328
	if (cl->refkey!=NULL) ms_free(cl->refkey);
	if (cl->call_id) ms_free(cl->call_id);
	if (cl->reporting.reports[LINPHONE_CALL_STATS_AUDIO]!=NULL) linphone_reporting_destroy(cl->reporting.reports[LINPHONE_CALL_STATS_AUDIO]);
	if (cl->reporting.reports[LINPHONE_CALL_STATS_VIDEO]!=NULL) linphone_reporting_destroy(cl->reporting.reports[LINPHONE_CALL_STATS_VIDEO]);
329
	if (cl->reporting.reports[LINPHONE_CALL_STATS_TEXT]!=NULL) linphone_reporting_destroy(cl->reporting.reports[LINPHONE_CALL_STATS_TEXT]);
330
	if (cl->error_info) linphone_error_info_unref(cl->error_info);
331 332
}

333
LinphoneCallLog * linphone_call_log_new(LinphoneCallDir dir, LinphoneAddress *from, LinphoneAddress *to) {
334
	LinphoneCallLog *cl=belle_sip_object_new(LinphoneCallLog);
335
	cl->dir=dir;
336 337 338
	cl->start_date_time=time(NULL);
	set_call_log_date(cl,cl->start_date_time);
	cl->from=from;
339

340
	cl->to=to;
341

342 343
	cl->status=LinphoneCallAborted; /*default status*/
	cl->quality=-1;
344
	cl->storage_id=0;
345 346 347

	cl->reporting.reports[LINPHONE_CALL_STATS_AUDIO]=linphone_reporting_new();
	cl->reporting.reports[LINPHONE_CALL_STATS_VIDEO]=linphone_reporting_new();
348
	cl->reporting.reports[LINPHONE_CALL_STATS_TEXT]=linphone_reporting_new();
349
	cl->connected_date_time=0;
350 351
	return cl;
}
352

353

354 355 356 357 358 359 360 361
/* DEPRECATED */
void linphone_call_log_destroy(LinphoneCallLog *cl) {
	belle_sip_object_unref(cl);
}

BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneCallLog);

BELLE_SIP_INSTANCIATE_VPTR(LinphoneCallLog, belle_sip_object_t,
362
	(belle_sip_object_destroy_t)_linphone_call_log_destroy,
363 364 365 366
	NULL, // clone
	NULL, // marshal
	FALSE
);
367 368 369 370 371 372


/*******************************************************************************
 * SQL storage related functions                                               *
 ******************************************************************************/

373
static void linphone_create_call_log_table(sqlite3* db) {
374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394
	char* errmsg=NULL;
	int ret;
	ret=sqlite3_exec(db,"CREATE TABLE IF NOT EXISTS call_history ("
							 "id             INTEGER PRIMARY KEY AUTOINCREMENT,"
							 "caller         TEXT NOT NULL," // Can't name a field "from"...
							 "callee         TEXT NOT NULL,"
							 "direction      INTEGER,"
							 "duration       INTEGER,"
							 "start_time     TEXT NOT NULL,"
							 "connected_time TEXT NOT NULL,"
							 "status         INTEGER,"
							 "videoEnabled   INTEGER,"
							 "quality        REAL"
						");",
			0,0,&errmsg);
	if(ret != SQLITE_OK) {
		ms_error("Error in creation: %s.\n", errmsg);
		sqlite3_free(errmsg);
	}
}

395
static void linphone_update_call_log_table(sqlite3* db) {
396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414
	char* errmsg=NULL;
	int ret;

	// for image url storage
	ret=sqlite3_exec(db,"ALTER TABLE call_history ADD COLUMN call_id TEXT;",NULL,NULL,&errmsg);
	if(ret != SQLITE_OK) {
		ms_message("Table already up to date: %s.", errmsg);
		sqlite3_free(errmsg);
	} else {
		ret=sqlite3_exec(db,"ALTER TABLE call_history ADD COLUMN refkey TEXT;",NULL,NULL,&errmsg);
		if(ret != SQLITE_OK) {
			ms_message("Table already up to date: %s.", errmsg);
			sqlite3_free(errmsg);
		} else {
			ms_debug("Table call_history updated successfully for call_id and refkey.");
		}
	}
}

415 416 417 418
void linphone_core_call_log_storage_init(LinphoneCore *lc) {
	int ret;
	const char *errmsg;
	sqlite3 *db;
419

420
	linphone_core_call_log_storage_close(lc);
421 422 423 424 425 426 427 428 429

	ret=_linphone_sqlite3_open(lc->logs_db_file, &db);
	if(ret != SQLITE_OK) {
		errmsg = sqlite3_errmsg(db);
		ms_error("Error in the opening: %s.\n", errmsg);
		sqlite3_close(db);
		return;
	}

430
	linphone_create_call_log_table(db);
431
	linphone_update_call_log_table(db);
432
	lc->logs_db = db;
433

434
	// Load the existing call logs
435
	linphone_core_get_call_history(lc);
436 437 438 439 440 441 442 443 444
}

void linphone_core_call_log_storage_close(LinphoneCore *lc) {
	if (lc->logs_db){
		sqlite3_close(lc->logs_db);
		lc->logs_db = NULL;
	}
}

445 446 447
static LinphoneCallLog * find_call_log_by_storage_id(bctbx_list_t *call_logs, unsigned int storage_id) {
	bctbx_list_t *item;
	for (item = call_logs; item != NULL; item = bctbx_list_next(item)) {
448
		LinphoneCallLog *call_log = reinterpret_cast<LinphoneCallLog *>(bctbx_list_get_data(item));
449 450 451 452 453
		if (call_log->storage_id == storage_id) return call_log;
	}
	return NULL;
}

454 455 456 457 458 459 460 461 462 463 464
/* DB layout:
 * | 0  | storage_id
 * | 1  | from
 * | 2  | to
 * | 3  | direction flag
 * | 4  | duration
 * | 5  | start date time (time_t)
 * | 6  | connected date time (time_t)
 * | 7  | status
 * | 8  | video enabled (1 or 0)
 * | 9  | quality
465 466
 * | 10 | call_id
 * | 11 | refkey
467 468
 */
static int create_call_log(void *data, int argc, char **argv, char **colName) {
469
	CallLogStorageResult *clsres = (CallLogStorageResult *)data;
470 471 472 473
	LinphoneAddress *from;
	LinphoneAddress *to;
	LinphoneCallDir dir;
	LinphoneCallLog *log;
474

475
	unsigned int storage_id = (unsigned int)atoi(argv[0]);
476 477 478 479 480 481 482

	log = find_call_log_by_storage_id(clsres->core->call_logs, storage_id);
	if (log != NULL) {
		clsres->result = bctbx_list_append(clsres->result, linphone_call_log_ref(log));
		return 0;
	}

483 484
	from = linphone_address_new(argv[1]);
	to = linphone_address_new(argv[2]);
485

486
	if (from == NULL || to == NULL) goto error;
487

488 489
	dir = (LinphoneCallDir) atoi(argv[3]);
	log = linphone_call_log_new(dir, from, to);
490

491 492 493
	log->storage_id = storage_id;
	log->duration = atoi(argv[4]);
	log->start_date_time = (time_t)atol(argv[5]);
494
	set_call_log_date(log,log->start_date_time);
495 496 497
	log->connected_date_time = (time_t)atol(argv[6]);
	log->status = (LinphoneCallStatus) atoi(argv[7]);
	log->video_enabled = atoi(argv[8]) == 1;
498
	log->quality = (float)atof(argv[9]);
499

500 501 502 503 504 505 506 507
	if (argc > 10) {
		if (argv[10] != NULL) {
			log->call_id = ms_strdup(argv[10]);
		}
		if (argv[10] != NULL) {
			log->refkey = ms_strdup(argv[11]);
		}
	}
508

509
	clsres->result = bctbx_list_append(clsres->result, log);
510
	return 0;
511

512 513
error:
	if (from){
Simon Morlat's avatar
Simon Morlat committed
514
		linphone_address_unref(from);
515 516
	}
	if (to){
Simon Morlat's avatar
Simon Morlat committed
517
		linphone_address_unref(to);
518 519
	}
	ms_error("Bad call log at storage_id %u", storage_id);
520 521 522
	return 0;
}

523
static void linphone_sql_request_call_log(sqlite3 *db, const char *stmt, CallLogStorageResult *clsres) {
524 525
	char* errmsg = NULL;
	int ret;
526
	ret = sqlite3_exec(db, stmt, create_call_log, clsres, &errmsg);
527
	if (ret != SQLITE_OK) {
528
		ms_error("linphone_sql_request: statement %s -> error sqlite3_exec(): %s.", stmt, errmsg);
529 530 531 532
		sqlite3_free(errmsg);
	}
}

533
static int linphone_sql_request_generic(sqlite3* db, const char *stmt) {
534 535 536 537 538 539 540 541 542 543
	char* errmsg = NULL;
	int ret;
	ret = sqlite3_exec(db, stmt, NULL, NULL, &errmsg);
	if (ret != SQLITE_OK) {
		ms_error("linphone_sql_request: statement %s -> error sqlite3_exec(): %s.", stmt, errmsg);
		sqlite3_free(errmsg);
	}
	return ret;
}

544
void linphone_core_store_call_log(LinphoneCore *lc, LinphoneCallLog *log) {
545 546 547
	if (lc && lc->logs_db){
		char *from, *to;
		char *buf;
548

549 550
		from = linphone_address_as_string(log->from);
		to = linphone_address_as_string(log->to);
551
		buf = sqlite3_mprintf("INSERT INTO call_history VALUES(NULL,%Q,%Q,%i,%i,%lld,%lld,%i,%i,%f,%Q,%Q);",
552 553 554 555 556 557 558 559
						from,
						to,
						log->dir,
						log->duration,
						(int64_t)log->start_date_time,
						(int64_t)log->connected_date_time,
						log->status,
						log->video_enabled ? 1 : 0,
560 561 562
						log->quality,
						log->call_id,
						log->refkey
563 564 565 566 567
					);
		linphone_sql_request_generic(lc->logs_db, buf);
		sqlite3_free(buf);
		ms_free(from);
		ms_free(to);
568

569
		log->storage_id = (unsigned int)sqlite3_last_insert_rowid(lc->logs_db);
570
	}
571

572
	if (lc) {
573
		lc->call_logs = bctbx_list_prepend(lc->call_logs, linphone_call_log_ref(log));
574 575 576
	}
}

577
const bctbx_list_t *linphone_core_get_call_history(LinphoneCore *lc) {
578 579
	char *buf;
	uint64_t begin,end;
580
	CallLogStorageResult clsres;
581 582

	if (!lc || lc->logs_db == NULL) return NULL;
583
		if (lc->call_logs != NULL) return lc->call_logs;
584

585 586 587 588 589
	if (lc->max_call_logs != LINPHONE_MAX_CALL_HISTORY_UNLIMITED){
		buf = sqlite3_mprintf("SELECT * FROM call_history ORDER BY id DESC LIMIT %i", lc->max_call_logs);
	}else{
		buf = sqlite3_mprintf("SELECT * FROM call_history ORDER BY id DESC");
	}
590

591 592
	clsres.core = lc;
	clsres.result = NULL;
593
	begin = ortp_get_cur_time_ms();
594
	linphone_sql_request_call_log(lc->logs_db, buf, &clsres);
595 596 597
	end = ortp_get_cur_time_ms();
	ms_message("%s(): completed in %i ms",__FUNCTION__, (int)(end-begin));
	sqlite3_free(buf);
598

599
	lc->call_logs = clsres.result;
600 601 602
	return lc->call_logs;
}

603
void linphone_core_delete_call_history(LinphoneCore *lc) {
604 605 606 607 608 609 610 611 612
	char *buf;

	if (!lc || lc->logs_db == NULL) return ;

	buf = sqlite3_mprintf("DELETE FROM call_history");
	linphone_sql_request_generic(lc->logs_db, buf);
	sqlite3_free(buf);
}

613
void linphone_core_delete_call_log(LinphoneCore *lc, LinphoneCallLog *log) {
614 615 616 617
	char *buf;

	if (!lc || lc->logs_db == NULL) return ;

618
	buf = sqlite3_mprintf("DELETE FROM call_history WHERE id = %u", log->storage_id);
619 620 621 622
	linphone_sql_request_generic(lc->logs_db, buf);
	sqlite3_free(buf);
}

623
int linphone_core_get_call_history_size(LinphoneCore *lc) {
624 625 626 627 628
	int numrows = 0;
	char *buf;
	sqlite3_stmt *selectStatement;
	int returnValue;

629 630 631 632
	if (!lc)
		return 0;
	if (!lc->logs_db)
		return (int)bctbx_list_size(lc->call_logs);
633 634 635 636 637 638 639 640 641 642 643 644 645 646

	buf = sqlite3_mprintf("SELECT count(*) FROM call_history");
	returnValue = sqlite3_prepare_v2(lc->logs_db, buf, -1, &selectStatement, NULL);
	if (returnValue == SQLITE_OK){
		if(sqlite3_step(selectStatement) == SQLITE_ROW){
			numrows = sqlite3_column_int(selectStatement, 0);
		}
	}
	sqlite3_finalize(selectStatement);
	sqlite3_free(buf);

	return numrows;
}

647
bctbx_list_t * linphone_core_get_call_history_for_address(LinphoneCore *lc, const LinphoneAddress *addr) {
648 649 650
	char *buf;
	char *sipAddress;
	uint64_t begin,end;
651
	CallLogStorageResult clsres;
652 653

	if (!lc || lc->logs_db == NULL || addr == NULL) return NULL;
654

655 656
	/*since we want to append query parameters depending on arguments given, we use malloc instead of sqlite3_mprintf*/
	sipAddress = linphone_address_as_string_uri_only(addr);
657
	buf = sqlite3_mprintf("SELECT * FROM call_history WHERE caller LIKE '%%%q%%' OR callee LIKE '%%%q%%' ORDER BY id DESC", sipAddress, sipAddress); // The '%%%q%%' takes care of the eventual presence of a display name
658

659 660
	clsres.core = lc;
	clsres.result = NULL;
661
	begin = ortp_get_cur_time_ms();
662
	linphone_sql_request_call_log(lc->logs_db, buf, &clsres);
663 664 665 666
	end = ortp_get_cur_time_ms();
	ms_message("%s(): completed in %i ms",__FUNCTION__, (int)(end-begin));
	sqlite3_free(buf);
	ms_free(sipAddress);
667

668
	return clsres.result;
669 670
}

671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709
bctbx_list_t *linphone_core_get_call_history_2(
	LinphoneCore *lc,
	const LinphoneAddress *peer_addr,
	const LinphoneAddress *local_addr
) {
	char *buf;
	char *peer_addr_str;
	char *local_addr_str;
	uint64_t begin, end;
	CallLogStorageResult clsres;

	if (!lc || !lc->logs_db || !peer_addr || !local_addr) return NULL;

	peer_addr_str = bctbx_strdup(L_GET_CPP_PTR_FROM_C_OBJECT(peer_addr)->asStringUriOnly().c_str());
	local_addr_str = bctbx_strdup(L_GET_CPP_PTR_FROM_C_OBJECT(local_addr)->asStringUriOnly().c_str());
	buf = sqlite3_mprintf(
		"SELECT * FROM call_history WHERE "
		"(caller LIKE '%%%q%%' AND callee LIKE '%%%q%%' AND direction = 0) OR "
		"(caller LIKE '%%%q%%' AND callee LIKE '%%%q%%' AND direction = 1) "
		"ORDER BY id DESC",
		local_addr_str,
		peer_addr_str,
		peer_addr_str,
		local_addr_str
	);

	clsres.core = lc;
	clsres.result = NULL;
	begin = ortp_get_cur_time_ms();
	linphone_sql_request_call_log(lc->logs_db, buf, &clsres);
	end = ortp_get_cur_time_ms();
	bctbx_message("%s(): completed in %i ms", __FUNCTION__, (int)(end - begin));
	sqlite3_free(buf);
	bctbx_free(peer_addr_str);
	bctbx_free(local_addr_str);

	return clsres.result;
}

710 711 712
LinphoneCallLog * linphone_core_get_last_outgoing_call_log(LinphoneCore *lc) {
	char *buf;
	uint64_t begin,end;
713 714
	CallLogStorageResult clsres;
	LinphoneCallLog *result = NULL;
715 716

	if (!lc || lc->logs_db == NULL) return NULL;
717

718 719 720
	/*since we want to append query parameters depending on arguments given, we use malloc instead of sqlite3_mprintf*/
	buf = sqlite3_mprintf("SELECT * FROM call_history WHERE direction = 0 ORDER BY id DESC LIMIT 1");

721 722
	clsres.core = lc;
	clsres.result = NULL;
723
	begin = ortp_get_cur_time_ms();
724
	linphone_sql_request_call_log(lc->logs_db, buf, &clsres);
725 726 727
	end = ortp_get_cur_time_ms();
	ms_message("%s(): completed in %i ms",__FUNCTION__, (int)(end-begin));
	sqlite3_free(buf);
728

729 730
	if (clsres.result != NULL) {
		result = (LinphoneCallLog *)bctbx_list_get_data(clsres.result);
731
	}
732

733 734 735
	return result;
}

736 737 738
LinphoneCallLog * linphone_core_find_call_log_from_call_id(LinphoneCore *lc, const char *call_id) {
	char *buf;
	uint64_t begin,end;
739
	CallLogStorageResult clsres;
740 741 742
	LinphoneCallLog* result = NULL;

	if (!lc || lc->logs_db == NULL) return NULL;
743

744 745 746
	/*since we want to append query parameters depending on arguments given, we use malloc instead of sqlite3_mprintf*/
	buf = sqlite3_mprintf("SELECT * FROM call_history WHERE call_id = '%q' ORDER BY id DESC LIMIT 1", call_id);

747 748
	clsres.core = lc;
	clsres.result = NULL;
749
	begin = ortp_get_cur_time_ms();
750
	linphone_sql_request_call_log(lc->logs_db, buf, &clsres);
751 752 753
	end = ortp_get_cur_time_ms();
	ms_message("%s(): completed in %i ms",__FUNCTION__, (int)(end-begin));
	sqlite3_free(buf);
754

755 756
	if (clsres.result != NULL) {
		result = (LinphoneCallLog *)bctbx_list_get_data(clsres.result);
757
	}
758

759 760
	return result;
}