call_log.c 22.7 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

Ronan's avatar
Ronan committed
26
#ifdef SQLITE_STORAGE_ENABLED
27
	#if !defined(_WIN32) && !defined(__ANDROID__) && !defined(__QNXNTO__)
Ronan's avatar
Ronan committed
28 29 30 31
		#include <langinfo.h>
		#include <iconv.h>
		#include <string.h>
	#endif
32

33
	#include "sqlite3.h"
Ronan's avatar
Ronan committed
34 35

	#define MAX_PATH_SIZE 1024
36
#endif
37

38 39
#include "c-wrapper/c-wrapper.h"

40 41 42
// TODO: From coreapi. Remove me later.
#include "private.h"

43 44 45 46 47
typedef struct _CallLogStorageResult {
	LinphoneCore *core;
	bctbx_list_t *result;
} CallLogStorageResult;

48 49 50 51
/*******************************************************************************
 * Internal functions                                                          *
 ******************************************************************************/

Ghislain MARY's avatar
Ghislain MARY committed
52 53 54 55 56 57
/*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){
58
#ifndef _WIN32
Ghislain MARY's avatar
Ghislain MARY committed
59 60 61 62 63 64 65 66
	struct tm tmtime={0};
	strptime(date,"%c",&tmtime);
	return mktime(&tmtime);
#else
	return 0;
#endif
}

67 68
static void set_call_log_date(LinphoneCallLog *cl, time_t start_time){
	struct tm loctime;
69
#ifdef _WIN32
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
#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){
85
	bctbx_list_t *elem;
86
	char logsection[32];
Simon Morlat's avatar
Simon Morlat committed
87
	int i;
88 89 90 91
	char *tmp;
	LpConfig *cfg=lc->config;

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

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

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

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

130 131 132
	for(i=0;;++i){
		snprintf(logsection,sizeof(logsection),"call_log_%i",i);
		if (lp_config_has_section(cfg,logsection)){
133 134
			LinphoneCallLog *cl;
			LinphoneAddress *from=NULL,*to=NULL;
135
			tmp=lp_config_get_string(cfg,logsection,"from",NULL);
136
			if (tmp) from=linphone_address_new(tmp);
137
			tmp=lp_config_get_string(cfg,logsection,"to",NULL);
138 139 140
			if (tmp) to=linphone_address_new(tmp);
			if (!from || !to)
				continue;
141 142
			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
143
			sec=(uint64_t)lp_config_get_int64(cfg,logsection,"start_date_time",0);
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
			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));
					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);
159
			cl->video_enabled=!!lp_config_get_int(cfg,logsection,"video_enabled",0);
160 161
			tmp=lp_config_get_string(cfg,logsection,"call_id",NULL);
			if (tmp) cl->call_id=ms_strdup(tmp);
162
			call_logs=bctbx_list_append(call_logs,cl);
163 164
		}else break;
	}
165
	return call_logs;
166 167 168 169 170 171 172
}


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

173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
LinphoneCallLog *linphone_core_create_call_log(LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to, LinphoneCallDir dir, 
		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;
}

190 191 192 193
const char *linphone_call_log_get_call_id(const LinphoneCallLog *cl){
	return cl->call_id;
}

Ronan's avatar
Ronan committed
194
LinphoneCallDir linphone_call_log_get_dir(const LinphoneCallLog *cl){
195 196 197
	return cl->dir;
}

Ronan's avatar
Ronan committed
198
int linphone_call_log_get_duration(const LinphoneCallLog *cl){
199 200 201
	return cl->duration;
}

Ronan's avatar
Ronan committed
202
const LinphoneAddress *linphone_call_log_get_from_address(const LinphoneCallLog *cl){
203 204 205 206 207 208 209
	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
210
float linphone_call_log_get_quality(const LinphoneCallLog *cl){
211 212 213 214 215 216 217
	return cl->quality;
}

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

218 219 220 221 222
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){
223 224 225 226 227 228 229
	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
230
time_t linphone_call_log_get_start_date(const LinphoneCallLog *cl){
231 232 233
	return cl->start_date_time;
}

Ronan's avatar
Ronan committed
234
LinphoneCallStatus linphone_call_log_get_status(const LinphoneCallLog *cl){
235 236 237
	return cl->status;
}

Ronan's avatar
Ronan committed
238
const LinphoneAddress *linphone_call_log_get_to_address(const LinphoneCallLog *cl){
239 240 241 242 243 244 245 246 247 248 249
	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
250
char * linphone_call_log_to_str(const LinphoneCallLog *cl){
251
	const char *status;
252 253 254 255 256
	char *tmp;
	char *from=linphone_address_as_string (cl->from);
	char *to=linphone_address_as_string (cl->to);
	switch(cl->status){
		case LinphoneCallAborted:
257
			status="aborted";
258 259
			break;
		case LinphoneCallSuccess:
260
			status="completed";
261 262
			break;
		case LinphoneCallMissed:
263
			status="missed";
264
			break;
265
		case LinphoneCallAcceptedElsewhere:
266
			status="answered elsewhere";
267 268
			break;
		case LinphoneCallDeclinedElsewhere:
269
			status="declined elsewhere";
270
			break;
271
		default:
272
			status="unknown";
273
	}
274 275
	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",
276 277 278 279 280 281 282 283 284 285 286
			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
287
bool_t linphone_call_log_video_enabled(const LinphoneCallLog *cl) {
288 289 290
	return cl->video_enabled;
}

Ronan's avatar
Ronan committed
291
bool_t linphone_call_log_was_conference(const LinphoneCallLog *cl) {
292 293 294
	return cl->was_conference;
}

Ronan's avatar
Ronan committed
295
const LinphoneErrorInfo *linphone_call_log_get_error_info(const LinphoneCallLog *cl){
296 297 298
	return cl->error_info;
}

299 300 301 302 303

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

304 305
void *linphone_call_log_get_user_data(const LinphoneCallLog *cl) {
	return cl->user_data;
306 307
}

308 309
void linphone_call_log_set_user_data(LinphoneCallLog *cl, void *ud) {
	cl->user_data = ud;
310 311
}

312 313 314 315 316 317 318 319
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);
}
320 321 322 323 324

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

325
static void _linphone_call_log_destroy(LinphoneCallLog *cl) {
Simon Morlat's avatar
Simon Morlat committed
326 327
	if (cl->from!=NULL) linphone_address_unref(cl->from);
	if (cl->to!=NULL) linphone_address_unref(cl->to);
328 329 330 331
	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]);
332
	if (cl->reporting.reports[LINPHONE_CALL_STATS_TEXT]!=NULL) linphone_reporting_destroy(cl->reporting.reports[LINPHONE_CALL_STATS_TEXT]);
333
	if (cl->error_info) linphone_error_info_unref(cl->error_info);
334 335
}

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

343
	cl->to=to;
344

345 346
	cl->status=LinphoneCallAborted; /*default status*/
	cl->quality=-1;
347
	cl->storage_id=0;
348 349 350

	cl->reporting.reports[LINPHONE_CALL_STATS_AUDIO]=linphone_reporting_new();
	cl->reporting.reports[LINPHONE_CALL_STATS_VIDEO]=linphone_reporting_new();
351
	cl->reporting.reports[LINPHONE_CALL_STATS_TEXT]=linphone_reporting_new();
352
	cl->connected_date_time=0;
353 354
	return cl;
}
355

356

357 358 359 360 361 362 363 364
/* 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,
365
	(belle_sip_object_destroy_t)_linphone_call_log_destroy,
366 367 368 369
	NULL, // clone
	NULL, // marshal
	FALSE
);
370 371 372 373 374 375


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

376
#ifdef SQLITE_STORAGE_ENABLED
377

378
static void linphone_create_call_log_table(sqlite3* db) {
379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399
	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);
	}
}

400
static void linphone_update_call_log_table(sqlite3* db) {
401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419
	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.");
		}
	}
}

420 421 422 423
void linphone_core_call_log_storage_init(LinphoneCore *lc) {
	int ret;
	const char *errmsg;
	sqlite3 *db;
424

425
	linphone_core_call_log_storage_close(lc);
426 427 428 429 430 431 432 433 434

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

435
	linphone_create_call_log_table(db);
436
	linphone_update_call_log_table(db);
437
	lc->logs_db = db;
438

439
	// Load the existing call logs
440
	linphone_core_get_call_history(lc);
441 442 443 444 445 446 447 448 449
}

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

450 451 452
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)) {
453
		LinphoneCallLog *call_log = reinterpret_cast<LinphoneCallLog *>(bctbx_list_get_data(item));
454 455 456 457 458
		if (call_log->storage_id == storage_id) return call_log;
	}
	return NULL;
}

459 460 461 462 463 464 465 466 467 468 469
/* 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
470 471
 * | 10 | call_id
 * | 11 | refkey
472 473
 */
static int create_call_log(void *data, int argc, char **argv, char **colName) {
474
	CallLogStorageResult *clsres = (CallLogStorageResult *)data;
475 476 477 478
	LinphoneAddress *from;
	LinphoneAddress *to;
	LinphoneCallDir dir;
	LinphoneCallLog *log;
479

480
	unsigned int storage_id = (unsigned int)atoi(argv[0]);
481 482 483 484 485 486 487

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

488 489
	from = linphone_address_new(argv[1]);
	to = linphone_address_new(argv[2]);
490

491
	if (from == NULL || to == NULL) goto error;
492

493 494
	dir = (LinphoneCallDir) atoi(argv[3]);
	log = linphone_call_log_new(dir, from, to);
495

496 497 498
	log->storage_id = storage_id;
	log->duration = atoi(argv[4]);
	log->start_date_time = (time_t)atol(argv[5]);
499
	set_call_log_date(log,log->start_date_time);
500 501 502
	log->connected_date_time = (time_t)atol(argv[6]);
	log->status = (LinphoneCallStatus) atoi(argv[7]);
	log->video_enabled = atoi(argv[8]) == 1;
503
	log->quality = (float)atof(argv[9]);
504

505 506 507 508 509 510 511 512
	if (argc > 10) {
		if (argv[10] != NULL) {
			log->call_id = ms_strdup(argv[10]);
		}
		if (argv[10] != NULL) {
			log->refkey = ms_strdup(argv[11]);
		}
	}
513

514
	clsres->result = bctbx_list_append(clsres->result, log);
515
	return 0;
516

517 518
error:
	if (from){
Simon Morlat's avatar
Simon Morlat committed
519
		linphone_address_unref(from);
520 521
	}
	if (to){
Simon Morlat's avatar
Simon Morlat committed
522
		linphone_address_unref(to);
523 524
	}
	ms_error("Bad call log at storage_id %u", storage_id);
525 526 527
	return 0;
}

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

538
static int linphone_sql_request_generic(sqlite3* db, const char *stmt) {
539 540 541 542 543 544 545 546 547 548
	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;
}

549
void linphone_core_store_call_log(LinphoneCore *lc, LinphoneCallLog *log) {
550 551 552
	if (lc && lc->logs_db){
		char *from, *to;
		char *buf;
553

554 555
		from = linphone_address_as_string(log->from);
		to = linphone_address_as_string(log->to);
556
		buf = sqlite3_mprintf("INSERT INTO call_history VALUES(NULL,%Q,%Q,%i,%i,%lld,%lld,%i,%i,%f,%Q,%Q);",
557 558 559 560 561 562 563 564
						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,
565 566 567
						log->quality,
						log->call_id,
						log->refkey
568 569 570 571 572
					);
		linphone_sql_request_generic(lc->logs_db, buf);
		sqlite3_free(buf);
		ms_free(from);
		ms_free(to);
573

574
		log->storage_id = (unsigned int)sqlite3_last_insert_rowid(lc->logs_db);
575
	}
576

577
	if (lc) {
578
		lc->call_logs = bctbx_list_prepend(lc->call_logs, linphone_call_log_ref(log));
579 580 581
	}
}

582
const bctbx_list_t *linphone_core_get_call_history(LinphoneCore *lc) {
583 584
	char *buf;
	uint64_t begin,end;
585
	CallLogStorageResult clsres;
586 587

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

590 591 592 593 594
	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");
	}
595

596 597
	clsres.core = lc;
	clsres.result = NULL;
598
	begin = ortp_get_cur_time_ms();
599
	linphone_sql_request_call_log(lc->logs_db, buf, &clsres);
600 601 602
	end = ortp_get_cur_time_ms();
	ms_message("%s(): completed in %i ms",__FUNCTION__, (int)(end-begin));
	sqlite3_free(buf);
603

604
	lc->call_logs = clsres.result;
605 606 607
	return lc->call_logs;
}

608
void linphone_core_delete_call_history(LinphoneCore *lc) {
609 610 611 612 613 614 615 616 617
	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);
}

618
void linphone_core_delete_call_log(LinphoneCore *lc, LinphoneCallLog *log) {
619 620 621 622
	char *buf;

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

623
	buf = sqlite3_mprintf("DELETE FROM call_history WHERE id = %u", log->storage_id);
624 625 626 627
	linphone_sql_request_generic(lc->logs_db, buf);
	sqlite3_free(buf);
}

628
int linphone_core_get_call_history_size(LinphoneCore *lc) {
629 630 631 632 633
	int numrows = 0;
	char *buf;
	sqlite3_stmt *selectStatement;
	int returnValue;

634 635 636 637
	if (!lc)
		return 0;
	if (!lc->logs_db)
		return (int)bctbx_list_size(lc->call_logs);
638 639 640 641 642 643 644 645 646 647 648 649 650 651

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

652
bctbx_list_t * linphone_core_get_call_history_for_address(LinphoneCore *lc, const LinphoneAddress *addr) {
653 654 655
	char *buf;
	char *sipAddress;
	uint64_t begin,end;
656
	CallLogStorageResult clsres;
657 658

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

660 661
	/*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);
662
	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
663

664 665
	clsres.core = lc;
	clsres.result = NULL;
666
	begin = ortp_get_cur_time_ms();
667
	linphone_sql_request_call_log(lc->logs_db, buf, &clsres);
668 669 670 671
	end = ortp_get_cur_time_ms();
	ms_message("%s(): completed in %i ms",__FUNCTION__, (int)(end-begin));
	sqlite3_free(buf);
	ms_free(sipAddress);
672

673
	return clsres.result;
674 675
}

676 677 678
LinphoneCallLog * linphone_core_get_last_outgoing_call_log(LinphoneCore *lc) {
	char *buf;
	uint64_t begin,end;
679 680
	CallLogStorageResult clsres;
	LinphoneCallLog *result = NULL;
681 682

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

684 685 686
	/*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");

687 688
	clsres.core = lc;
	clsres.result = NULL;
689
	begin = ortp_get_cur_time_ms();
690
	linphone_sql_request_call_log(lc->logs_db, buf, &clsres);
691 692 693
	end = ortp_get_cur_time_ms();
	ms_message("%s(): completed in %i ms",__FUNCTION__, (int)(end-begin));
	sqlite3_free(buf);
694

695 696
	if (clsres.result != NULL) {
		result = (LinphoneCallLog *)bctbx_list_get_data(clsres.result);
697
	}
698

699 700 701
	return result;
}

702 703 704
LinphoneCallLog * linphone_core_find_call_log_from_call_id(LinphoneCore *lc, const char *call_id) {
	char *buf;
	uint64_t begin,end;
705
	CallLogStorageResult clsres;
706 707 708
	LinphoneCallLog* result = NULL;

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

710 711 712
	/*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);

713 714
	clsres.core = lc;
	clsres.result = NULL;
715
	begin = ortp_get_cur_time_ms();
716
	linphone_sql_request_call_log(lc->logs_db, buf, &clsres);
717 718 719
	end = ortp_get_cur_time_ms();
	ms_message("%s(): completed in %i ms",__FUNCTION__, (int)(end-begin));
	sqlite3_free(buf);
720

721 722
	if (clsres.result != NULL) {
		result = (LinphoneCallLog *)bctbx_list_get_data(clsres.result);
723
	}
724

725 726 727
	return result;
}

728 729
#else

730
void linphone_core_call_log_storage_init(LinphoneCore *lc) {
731 732 733 734 735
}

void linphone_core_call_log_storage_close(LinphoneCore *lc) {
}

736
void linphone_core_store_call_log(LinphoneCore *lc, LinphoneCallLog *log) {
737 738
}

739
const bctbx_list_t *linphone_core_get_call_history(LinphoneCore *lc) {
740 741 742
	return NULL;
}

743
void linphone_core_delete_call_history(LinphoneCore *lc) {
744 745
}

746
void linphone_core_delete_call_log(LinphoneCore *lc, LinphoneCallLog *log) {
747 748
}

749
int linphone_core_get_call_history_size(LinphoneCore *lc) {
750 751 752
	return 0;
}

753
bctbx_list_t * linphone_core_get_call_history_for_address(LinphoneCore *lc, const LinphoneAddress *addr) {
754 755 756
	return NULL;
}

757
LinphoneCallLog * linphone_core_get_last_outgoing_call_log(LinphoneCore *lc) {
758 759 760
	return NULL;
}

761 762 763 764
LinphoneCallLog * linphone_core_find_call_log_from_call_id(LinphoneCore *lc, const char *call_id) {
	return NULL;
}

765
#endif