call_log.c 21.3 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
*/

20
#define _XOPEN_SOURCE 700 /*required for strptime of GNU libc*/
21

22
#include <time.h>
23 24
#include "private.h"

25
#ifdef SQLITE_STORAGE_ENABLED
26 27 28 29 30 31 32 33 34 35 36 37 38
#ifndef _WIN32
#if !defined(ANDROID) && !defined(__QNXNTO__)
#	include <langinfo.h>
#	include <iconv.h>
#	include <string.h>
#endif
#else
#include <Windows.h>
#endif

#define MAX_PATH_SIZE 1024
#include "sqlite3.h"
#endif
39 40 41 42 43

/*******************************************************************************
 * Internal functions                                                          *
 ******************************************************************************/

Ghislain MARY's avatar
Ghislain MARY committed
44 45 46 47 48 49
/*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){
50
#ifndef _WIN32
Ghislain MARY's avatar
Ghislain MARY committed
51 52 53 54 55 56 57 58
	struct tm tmtime={0};
	strptime(date,"%c",&tmtime);
	return mktime(&tmtime);
#else
	return 0;
#endif
}

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

	if (linphone_core_get_global_state (lc)==LinphoneGlobalStartup) return;
84 85
	
	if (lc->max_call_logs == LINPHONE_MAX_CALL_HISTORY_UNLIMITED) return;
86 87 88 89 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 118 119 120 121 122

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

void call_logs_read_from_config_file(LinphoneCore *lc){
	char logsection[32];
	int i;
	const char *tmp;
	uint64_t sec;
	LpConfig *cfg=lc->config;
	for(i=0;;++i){
		snprintf(logsection,sizeof(logsection),"call_log_%i",i);
		if (lp_config_has_section(cfg,logsection)){
123 124
			LinphoneCallLog *cl;
			LinphoneAddress *from=NULL,*to=NULL;
125
			tmp=lp_config_get_string(cfg,logsection,"from",NULL);
126
			if (tmp) from=linphone_address_new(tmp);
127
			tmp=lp_config_get_string(cfg,logsection,"to",NULL);
128 129 130 131 132
			if (tmp) to=linphone_address_new(tmp);
			if (!from || !to)
				continue;
			cl=linphone_call_log_new(lp_config_get_int(cfg,logsection,"dir",0),from,to);
			cl->status=lp_config_get_int(cfg,logsection,"status",0);
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
			sec=lp_config_get_int64(cfg,logsection,"start_date_time",0);
			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);
			cl->video_enabled=lp_config_get_int(cfg,logsection,"video_enabled",0);
			tmp=lp_config_get_string(cfg,logsection,"call_id",NULL);
			if (tmp) cl->call_id=ms_strdup(tmp);
152
			lc->call_logs=bctbx_list_append(lc->call_logs,cl);
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
		}else break;
	}
}


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

const char *linphone_call_log_get_call_id(const LinphoneCallLog *cl){
	return cl->call_id;
}

LinphoneCallDir linphone_call_log_get_dir(LinphoneCallLog *cl){
	return cl->dir;
}

int linphone_call_log_get_duration(LinphoneCallLog *cl){
	return cl->duration;
}

LinphoneAddress *linphone_call_log_get_from_address(LinphoneCallLog *cl){
	return cl->from;
}

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

float linphone_call_log_get_quality(LinphoneCallLog *cl){
	return cl->quality;
}

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

LinphoneAddress *linphone_call_log_get_remote_address(LinphoneCallLog *cl){
	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;
}

time_t linphone_call_log_get_start_date(LinphoneCallLog *cl){
	return cl->start_date_time;
}

LinphoneCallStatus linphone_call_log_get_status(LinphoneCallLog *cl){
	return cl->status;
}

LinphoneAddress *linphone_call_log_get_to_address(LinphoneCallLog *cl){
	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);
}

char * linphone_call_log_to_str(LinphoneCallLog *cl){
	char *status;
	char *tmp;
	char *from=linphone_address_as_string (cl->from);
	char *to=linphone_address_as_string (cl->to);
	switch(cl->status){
		case LinphoneCallAborted:
			status=_("aborted");
			break;
		case LinphoneCallSuccess:
			status=_("completed");
			break;
		case LinphoneCallMissed:
			status=_("missed");
			break;
		default:
234
			status=_("unknown");
235
	}
236
	tmp=ms_strdup_printf(_("%s at %s\nFrom: %s\nTo: %s\nStatus: %s\nDuration: %i mn %i sec\n"),
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
			(cl->dir==LinphoneCallIncoming) ? _("Incoming call") : _("Outgoing call"),
			cl->start_date,
			from,
			to,
			status,
			cl->duration/60,
			cl->duration%60);
	ms_free(from);
	ms_free(to);
	return tmp;
}

bool_t linphone_call_log_video_enabled(LinphoneCallLog *cl) {
	return cl->video_enabled;
}

253 254 255 256
bool_t linphone_call_log_was_conference(LinphoneCallLog *cl) {
	return cl->was_conference;
}

257 258 259 260 261

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

262 263
void *linphone_call_log_get_user_data(const LinphoneCallLog *cl) {
	return cl->user_data;
264 265
}

266 267
void linphone_call_log_set_user_data(LinphoneCallLog *cl, void *ud) {
	cl->user_data = ud;
268 269
}

270 271 272 273 274 275 276 277
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);
}
278 279 280 281 282

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

283
static void _linphone_call_log_destroy(LinphoneCallLog *cl) {
Simon Morlat's avatar
Simon Morlat committed
284 285
	if (cl->from!=NULL) linphone_address_unref(cl->from);
	if (cl->to!=NULL) linphone_address_unref(cl->to);
286 287 288 289
	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]);
290
	if (cl->reporting.reports[LINPHONE_CALL_STATS_TEXT]!=NULL) linphone_reporting_destroy(cl->reporting.reports[LINPHONE_CALL_STATS_TEXT]);
291 292
}

293
LinphoneCallLog * linphone_call_log_new(LinphoneCallDir dir, LinphoneAddress *from, LinphoneAddress *to) {
294
	LinphoneCallLog *cl=belle_sip_object_new(LinphoneCallLog);
295
	cl->dir=dir;
296 297 298 299 300 301
	cl->start_date_time=time(NULL);
	set_call_log_date(cl,cl->start_date_time);
	cl->from=from;
	cl->to=to;
	cl->status=LinphoneCallAborted; /*default status*/
	cl->quality=-1;
302
	cl->storage_id=0;
303 304 305

	cl->reporting.reports[LINPHONE_CALL_STATS_AUDIO]=linphone_reporting_new();
	cl->reporting.reports[LINPHONE_CALL_STATS_VIDEO]=linphone_reporting_new();
306
	cl->reporting.reports[LINPHONE_CALL_STATS_TEXT]=linphone_reporting_new();
307
	cl->connected_date_time=0;
308 309
	return cl;
}
310 311 312 313 314 315 316 317 318

/* 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,
319
	(belle_sip_object_destroy_t)_linphone_call_log_destroy,
320 321 322 323
	NULL, // clone
	NULL, // marshal
	FALSE
);
324 325 326 327 328 329


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

330
#ifdef SQLITE_STORAGE_ENABLED
331

332
static void linphone_create_table(sqlite3* db) {
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353
	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);
	}
}

354
static void linphone_update_call_log_table(sqlite3* db) {
355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373
	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.");
		}
	}
}

374 375 376 377
void linphone_core_call_log_storage_init(LinphoneCore *lc) {
	int ret;
	const char *errmsg;
	sqlite3 *db;
378

379
	linphone_core_call_log_storage_close(lc);
380 381 382 383 384 385 386 387 388 389

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

	linphone_create_table(db);
390
	linphone_update_call_log_table(db);
391
	lc->logs_db = db;
392

393
	// Load the existing call logs
394
	linphone_core_get_call_history(lc);
395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414
}

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

/* 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
415 416
 * | 10 | call_id
 * | 11 | refkey
417 418
 */
static int create_call_log(void *data, int argc, char **argv, char **colName) {
419
	bctbx_list_t **list = (bctbx_list_t **)data;
420 421 422 423
	LinphoneAddress *from;
	LinphoneAddress *to;
	LinphoneCallDir dir;
	LinphoneCallLog *log;
424

425
	unsigned int storage_id = (unsigned int)atoi(argv[0]);
426 427
	from = linphone_address_new(argv[1]);
	to = linphone_address_new(argv[2]);
428 429 430
	
	if (from == NULL || to == NULL) goto error;
	
431 432
	dir = (LinphoneCallDir) atoi(argv[3]);
	log = linphone_call_log_new(dir, from, to);
433

434 435 436
	log->storage_id = storage_id;
	log->duration = atoi(argv[4]);
	log->start_date_time = (time_t)atol(argv[5]);
437
	set_call_log_date(log,log->start_date_time);
438 439 440
	log->connected_date_time = (time_t)atol(argv[6]);
	log->status = (LinphoneCallStatus) atoi(argv[7]);
	log->video_enabled = atoi(argv[8]) == 1;
441
	log->quality = (float)atof(argv[9]);
442

443 444 445 446 447 448 449 450
	if (argc > 10) {
		if (argv[10] != NULL) {
			log->call_id = ms_strdup(argv[10]);
		}
		if (argv[10] != NULL) {
			log->refkey = ms_strdup(argv[11]);
		}
	}
451

452
	*list = bctbx_list_append(*list, log);
453 454 455 456
	return 0;
	
error:
	if (from){
Simon Morlat's avatar
Simon Morlat committed
457
		linphone_address_unref(from);
458 459
	}
	if (to){
Simon Morlat's avatar
Simon Morlat committed
460
		linphone_address_unref(to);
461 462
	}
	ms_error("Bad call log at storage_id %u", storage_id);
463 464 465
	return 0;
}

466
static void linphone_sql_request_call_log(sqlite3 *db, const char *stmt, bctbx_list_t **list) {
467 468
	char* errmsg = NULL;
	int ret;
469
	ret = sqlite3_exec(db, stmt, create_call_log, list, &errmsg);
470
	if (ret != SQLITE_OK) {
471
		ms_error("linphone_sql_request: statement %s -> error sqlite3_exec(): %s.", stmt, errmsg);
472 473 474 475
		sqlite3_free(errmsg);
	}
}

476
static int linphone_sql_request_generic(sqlite3* db, const char *stmt) {
477 478 479 480 481 482 483 484 485 486
	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;
}

487
void linphone_core_store_call_log(LinphoneCore *lc, LinphoneCallLog *log) {
488 489 490
	if (lc && lc->logs_db){
		char *from, *to;
		char *buf;
491

492 493
		from = linphone_address_as_string(log->from);
		to = linphone_address_as_string(log->to);
494
		buf = sqlite3_mprintf("INSERT INTO call_history VALUES(NULL,%Q,%Q,%i,%i,%lld,%lld,%i,%i,%f,%Q,%Q);",
495 496 497 498 499 500 501 502
						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,
503 504 505
						log->quality,
						log->call_id,
						log->refkey
506 507 508 509 510
					);
		linphone_sql_request_generic(lc->logs_db, buf);
		sqlite3_free(buf);
		ms_free(from);
		ms_free(to);
511

512
		log->storage_id = (unsigned int)sqlite3_last_insert_rowid(lc->logs_db);
513
	}
514

515
	if (lc) {
516
		lc->call_logs = bctbx_list_prepend(lc->call_logs, linphone_call_log_ref(log));
517 518 519
	}
}

520
static void copy_user_data_from_existing_log(bctbx_list_t *existing_logs, LinphoneCallLog *log) {
521 522 523 524 525 526
	while (existing_logs) {
		LinphoneCallLog *existing_log = (LinphoneCallLog *)existing_logs->data;
		if (existing_log->storage_id == log->storage_id) {
			log->user_data = existing_log->user_data;
			break;
		}
527
		existing_logs = bctbx_list_next(existing_logs);
528 529 530
	}
}

531
static void copy_user_data_from_existing_logs(bctbx_list_t *existing_logs, bctbx_list_t *new_logs) {
532 533 534
	while (new_logs) {
		LinphoneCallLog *new_log = (LinphoneCallLog *)new_logs->data;
		copy_user_data_from_existing_log(existing_logs, new_log);
535
		new_logs = bctbx_list_next(new_logs);
536 537 538
	}
}

539
const bctbx_list_t *linphone_core_get_call_history(LinphoneCore *lc) {
540 541
	char *buf;
	uint64_t begin,end;
542
	bctbx_list_t *result = NULL;
543 544 545

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

546 547 548 549 550
	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");
	}
551 552

	begin = ortp_get_cur_time_ms();
553
	linphone_sql_request_call_log(lc->logs_db, buf, &result);
554 555 556
	end = ortp_get_cur_time_ms();
	ms_message("%s(): completed in %i ms",__FUNCTION__, (int)(end-begin));
	sqlite3_free(buf);
557

558
	if (lc->call_logs) {
559
		copy_user_data_from_existing_logs(lc->call_logs, result);
560
	}
561

562
	lc->call_logs = bctbx_list_free_with_data(lc->call_logs, (void (*)(void*))linphone_call_log_unref);
563
	lc->call_logs = result;
564

565 566 567
	return lc->call_logs;
}

568
void linphone_core_delete_call_history(LinphoneCore *lc) {
569 570 571 572 573 574 575 576 577
	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);
}

578
void linphone_core_delete_call_log(LinphoneCore *lc, LinphoneCallLog *log) {
579 580 581 582
	char *buf;

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

583
	buf = sqlite3_mprintf("DELETE FROM call_history WHERE id = %u", log->storage_id);
584 585 586 587
	linphone_sql_request_generic(lc->logs_db, buf);
	sqlite3_free(buf);
}

588
int linphone_core_get_call_history_size(LinphoneCore *lc) {
589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608
	int numrows = 0;
	char *buf;
	sqlite3_stmt *selectStatement;
	int returnValue;

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

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

609
bctbx_list_t * linphone_core_get_call_history_for_address(LinphoneCore *lc, const LinphoneAddress *addr) {
610 611 612
	char *buf;
	char *sipAddress;
	uint64_t begin,end;
613
	bctbx_list_t *result = NULL;
614 615

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

617 618
	/*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);
619
	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
620 621

	begin = ortp_get_cur_time_ms();
622
	linphone_sql_request_call_log(lc->logs_db, buf, &result);
623 624 625 626
	end = ortp_get_cur_time_ms();
	ms_message("%s(): completed in %i ms",__FUNCTION__, (int)(end-begin));
	sqlite3_free(buf);
	ms_free(sipAddress);
627

628
	if (lc->call_logs) {
629
		copy_user_data_from_existing_logs(lc->call_logs, result);
630
	}
631

632 633 634
	return result;
}

635 636 637
LinphoneCallLog * linphone_core_get_last_outgoing_call_log(LinphoneCore *lc) {
	char *buf;
	uint64_t begin,end;
638
	bctbx_list_t *list = NULL;
639 640 641
	LinphoneCallLog* result = NULL;

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

643 644 645 646 647 648 649 650
	/*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");

	begin = ortp_get_cur_time_ms();
	linphone_sql_request_call_log(lc->logs_db, buf, &list);
	end = ortp_get_cur_time_ms();
	ms_message("%s(): completed in %i ms",__FUNCTION__, (int)(end-begin));
	sqlite3_free(buf);
651

652 653 654
	if (list) {
		result = (LinphoneCallLog*)list->data;
	}
655

656
	if (lc->call_logs && result) {
657
		copy_user_data_from_existing_log(lc->call_logs, result);
658
	}
659

660 661 662
	return result;
}

663 664 665
LinphoneCallLog * linphone_core_find_call_log_from_call_id(LinphoneCore *lc, const char *call_id) {
	char *buf;
	uint64_t begin,end;
666
	bctbx_list_t *list = NULL;
667 668 669
	LinphoneCallLog* result = NULL;

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

671 672 673 674 675 676 677 678
	/*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);

	begin = ortp_get_cur_time_ms();
	linphone_sql_request_call_log(lc->logs_db, buf, &list);
	end = ortp_get_cur_time_ms();
	ms_message("%s(): completed in %i ms",__FUNCTION__, (int)(end-begin));
	sqlite3_free(buf);
679

680 681 682
	if (list) {
		result = (LinphoneCallLog*)list->data;
	}
683

684 685 686
	if (lc->call_logs && result) {
		copy_user_data_from_existing_log(lc->call_logs, result);
	}
687

688 689 690
	return result;
}

691 692
#else

693
void linphone_core_call_log_storage_init(LinphoneCore *lc) {
694 695 696 697 698
}

void linphone_core_call_log_storage_close(LinphoneCore *lc) {
}

699
void linphone_core_store_call_log(LinphoneCore *lc, LinphoneCallLog *log) {
700 701
}

702
const bctbx_list_t *linphone_core_get_call_history(LinphoneCore *lc) {
703 704 705
	return NULL;
}

706
void linphone_core_delete_call_history(LinphoneCore *lc) {
707 708
}

709
void linphone_core_delete_call_log(LinphoneCore *lc, LinphoneCallLog *log) {
710 711
}

712
int linphone_core_get_call_history_size(LinphoneCore *lc) {
713 714 715
	return 0;
}

716
bctbx_list_t * linphone_core_get_call_history_for_address(LinphoneCore *lc, const LinphoneAddress *addr) {
717 718 719
	return NULL;
}

720
LinphoneCallLog * linphone_core_get_last_outgoing_call_log(LinphoneCore *lc) {
721 722 723
	return NULL;
}

724 725 726 727
LinphoneCallLog * linphone_core_find_call_log_from_call_id(LinphoneCore *lc, const char *call_id) {
	return NULL;
}

728
#endif