1 /*
2 linphone
3 Copyright (C) 2010-2014  Belledonne Communications SARL
4 
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 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 #include "private.h"
24 
25 #ifdef SQLITE_STORAGE_ENABLED
26 #ifndef _WIN32
27 #if !defined(__ANDROID__) && !defined(__QNXNTO__)
28 #	include <langinfo.h>
29 #	include <iconv.h>
30 #	include <string.h>
31 #endif
32 #else
33 #include <Windows.h>
34 #endif
35 
36 #define MAX_PATH_SIZE 1024
37 #include "sqlite3.h"
38 #endif
39 
40 typedef struct _CallLogStorageResult {
41 	LinphoneCore *core;
42 	bctbx_list_t *result;
43 } CallLogStorageResult;
44 
45 /*******************************************************************************
46  * Internal functions                                                          *
47  ******************************************************************************/
48 
49 /*prevent a gcc bug with %c*/
my_strftime(char * s,size_t max,const char * fmt,const struct tm * tm)50 static size_t my_strftime(char *s, size_t max, const char  *fmt,  const struct tm *tm){
51 	return strftime(s, max, fmt, tm);
52 }
53 
string_to_time(const char * date)54 static time_t string_to_time(const char *date){
55 #ifndef _WIN32
56 	struct tm tmtime={0};
57 	strptime(date,"%c",&tmtime);
58 	return mktime(&tmtime);
59 #else
60 	return 0;
61 #endif
62 }
63 
set_call_log_date(LinphoneCallLog * cl,time_t start_time)64 static void set_call_log_date(LinphoneCallLog *cl, time_t start_time){
65 	struct tm loctime;
66 #ifdef _WIN32
67 #if !defined(_WIN32_WCE)
68 	loctime=*localtime(&start_time);
69 	/*FIXME*/
70 #endif /*_WIN32_WCE*/
71 #else
72 	localtime_r(&start_time,&loctime);
73 #endif
74 	my_strftime(cl->start_date,sizeof(cl->start_date),"%c",&loctime);
75 }
76 
77 /*******************************************************************************
78  * Private functions                                                           *
79  ******************************************************************************/
80 
call_logs_write_to_config_file(LinphoneCore * lc)81 void call_logs_write_to_config_file(LinphoneCore *lc){
82 	bctbx_list_t *elem;
83 	char logsection[32];
84 	int i;
85 	char *tmp;
86 	LpConfig *cfg=lc->config;
87 
88 	if (linphone_core_get_global_state (lc)==LinphoneGlobalStartup) return;
89 
90 	if (lc->max_call_logs == LINPHONE_MAX_CALL_HISTORY_UNLIMITED) return;
91 
92 	for(i=0,elem=lc->call_logs;elem!=NULL;elem=elem->next,++i){
93 		LinphoneCallLog *cl=(LinphoneCallLog*)elem->data;
94 		snprintf(logsection,sizeof(logsection),"call_log_%i",i);
95 		lp_config_clean_section(cfg,logsection);
96 		lp_config_set_int(cfg,logsection,"dir",cl->dir);
97 		lp_config_set_int(cfg,logsection,"status",cl->status);
98 		tmp=linphone_address_as_string(cl->from);
99 		lp_config_set_string(cfg,logsection,"from",tmp);
100 		ms_free(tmp);
101 		tmp=linphone_address_as_string(cl->to);
102 		lp_config_set_string(cfg,logsection,"to",tmp);
103 		ms_free(tmp);
104 		if (cl->start_date_time)
105 			lp_config_set_int64(cfg,logsection,"start_date_time",(int64_t)cl->start_date_time);
106 		else lp_config_set_string(cfg,logsection,"start_date",cl->start_date);
107 		lp_config_set_int(cfg,logsection,"duration",cl->duration);
108 		if (cl->refkey) lp_config_set_string(cfg,logsection,"refkey",cl->refkey);
109 		lp_config_set_float(cfg,logsection,"quality",cl->quality);
110 		lp_config_set_int(cfg,logsection,"video_enabled", cl->video_enabled);
111 		lp_config_set_string(cfg,logsection,"call_id",cl->call_id);
112 	}
113 	for(;i<lc->max_call_logs;++i){
114 		snprintf(logsection,sizeof(logsection),"call_log_%i",i);
115 		lp_config_clean_section(cfg,logsection);
116 	}
117 }
118 
call_logs_read_from_config_file(LinphoneCore * lc)119 bctbx_list_t * call_logs_read_from_config_file(LinphoneCore *lc){
120 	char logsection[32];
121 	int i;
122 	const char *tmp;
123 	uint64_t sec;
124 	LpConfig *cfg=lc->config;
125 	bctbx_list_t *call_logs = NULL;
126 
127 	for(i=0;;++i){
128 		snprintf(logsection,sizeof(logsection),"call_log_%i",i);
129 		if (lp_config_has_section(cfg,logsection)){
130 			LinphoneCallLog *cl;
131 			LinphoneAddress *from=NULL,*to=NULL;
132 			tmp=lp_config_get_string(cfg,logsection,"from",NULL);
133 			if (tmp) from=linphone_address_new(tmp);
134 			tmp=lp_config_get_string(cfg,logsection,"to",NULL);
135 			if (tmp) to=linphone_address_new(tmp);
136 			if (!from || !to)
137 				continue;
138 			cl=linphone_call_log_new(lp_config_get_int(cfg,logsection,"dir",0),from,to);
139 			cl->status=lp_config_get_int(cfg,logsection,"status",0);
140 			sec=lp_config_get_int64(cfg,logsection,"start_date_time",0);
141 			if (sec) {
142 				/*new call log format with date expressed in seconds */
143 				cl->start_date_time=(time_t)sec;
144 				set_call_log_date(cl,cl->start_date_time);
145 			}else{
146 				tmp=lp_config_get_string(cfg,logsection,"start_date",NULL);
147 				if (tmp) {
148 					strncpy(cl->start_date,tmp,sizeof(cl->start_date));
149 					cl->start_date_time=string_to_time(cl->start_date);
150 				}
151 			}
152 			cl->duration=lp_config_get_int(cfg,logsection,"duration",0);
153 			tmp=lp_config_get_string(cfg,logsection,"refkey",NULL);
154 			if (tmp) cl->refkey=ms_strdup(tmp);
155 			cl->quality=lp_config_get_float(cfg,logsection,"quality",-1);
156 			cl->video_enabled=lp_config_get_int(cfg,logsection,"video_enabled",0);
157 			tmp=lp_config_get_string(cfg,logsection,"call_id",NULL);
158 			if (tmp) cl->call_id=ms_strdup(tmp);
159 			call_logs=bctbx_list_append(call_logs,cl);
160 		}else break;
161 	}
162 	return call_logs;
163 }
164 
165 
166 /*******************************************************************************
167  * Public functions                                                            *
168  ******************************************************************************/
169 
linphone_call_log_get_call_id(const LinphoneCallLog * cl)170 const char *linphone_call_log_get_call_id(const LinphoneCallLog *cl){
171 	return cl->call_id;
172 }
173 
linphone_call_log_get_dir(LinphoneCallLog * cl)174 LinphoneCallDir linphone_call_log_get_dir(LinphoneCallLog *cl){
175 	return cl->dir;
176 }
177 
linphone_call_log_get_duration(LinphoneCallLog * cl)178 int linphone_call_log_get_duration(LinphoneCallLog *cl){
179 	return cl->duration;
180 }
181 
linphone_call_log_get_from_address(LinphoneCallLog * cl)182 LinphoneAddress *linphone_call_log_get_from_address(LinphoneCallLog *cl){
183 	return cl->from;
184 }
185 
linphone_call_log_get_local_stats(const LinphoneCallLog * cl)186 const rtp_stats_t *linphone_call_log_get_local_stats(const LinphoneCallLog *cl){
187 	return &cl->local_stats;
188 }
189 
linphone_call_log_get_quality(LinphoneCallLog * cl)190 float linphone_call_log_get_quality(LinphoneCallLog *cl){
191 	return cl->quality;
192 }
193 
linphone_call_log_get_ref_key(const LinphoneCallLog * cl)194 const char *linphone_call_log_get_ref_key(const LinphoneCallLog *cl){
195 	return cl->refkey;
196 }
197 
linphone_call_log_get_remote_address(LinphoneCallLog * cl)198 LinphoneAddress *linphone_call_log_get_remote_address(LinphoneCallLog *cl){
199 	return (cl->dir == LinphoneCallIncoming) ? cl->from : cl->to;
200 }
201 
linphone_call_log_get_remote_stats(const LinphoneCallLog * cl)202 const rtp_stats_t *linphone_call_log_get_remote_stats(const LinphoneCallLog *cl){
203 	return &cl->remote_stats;
204 }
205 
linphone_call_log_get_start_date(LinphoneCallLog * cl)206 time_t linphone_call_log_get_start_date(LinphoneCallLog *cl){
207 	return cl->start_date_time;
208 }
209 
linphone_call_log_get_status(LinphoneCallLog * cl)210 LinphoneCallStatus linphone_call_log_get_status(LinphoneCallLog *cl){
211 	return cl->status;
212 }
213 
linphone_call_log_get_to_address(LinphoneCallLog * cl)214 LinphoneAddress *linphone_call_log_get_to_address(LinphoneCallLog *cl){
215 	return cl->to;
216 }
217 
linphone_call_log_set_ref_key(LinphoneCallLog * cl,const char * refkey)218 void linphone_call_log_set_ref_key(LinphoneCallLog *cl, const char *refkey){
219 	if (cl->refkey!=NULL){
220 		ms_free(cl->refkey);
221 		cl->refkey=NULL;
222 	}
223 	if (refkey) cl->refkey=ms_strdup(refkey);
224 }
225 
linphone_call_log_to_str(LinphoneCallLog * cl)226 char * linphone_call_log_to_str(LinphoneCallLog *cl){
227 	char *status;
228 	char *tmp;
229 	char *from=linphone_address_as_string (cl->from);
230 	char *to=linphone_address_as_string (cl->to);
231 	switch(cl->status){
232 		case LinphoneCallAborted:
233 			status=_("aborted");
234 			break;
235 		case LinphoneCallSuccess:
236 			status=_("completed");
237 			break;
238 		case LinphoneCallMissed:
239 			status=_("missed");
240 			break;
241 		default:
242 			status=_("unknown");
243 	}
244 	tmp=ms_strdup_printf(_("%s at %s\nFrom: %s\nTo: %s\nStatus: %s\nDuration: %i mn %i sec\n"),
245 			(cl->dir==LinphoneCallIncoming) ? _("Incoming call") : _("Outgoing call"),
246 			cl->start_date,
247 			from,
248 			to,
249 			status,
250 			cl->duration/60,
251 			cl->duration%60);
252 	ms_free(from);
253 	ms_free(to);
254 	return tmp;
255 }
256 
linphone_call_log_video_enabled(LinphoneCallLog * cl)257 bool_t linphone_call_log_video_enabled(LinphoneCallLog *cl) {
258 	return cl->video_enabled;
259 }
260 
linphone_call_log_was_conference(LinphoneCallLog * cl)261 bool_t linphone_call_log_was_conference(LinphoneCallLog *cl) {
262 	return cl->was_conference;
263 }
264 
linphone_call_log_get_error_info(LinphoneCallLog * cl)265 const LinphoneErrorInfo *linphone_call_log_get_error_info(LinphoneCallLog *cl){
266 	return cl->error_info;
267 }
268 
269 
270 /*******************************************************************************
271  * Reference and user data handling functions                                  *
272  ******************************************************************************/
273 
linphone_call_log_get_user_data(const LinphoneCallLog * cl)274 void *linphone_call_log_get_user_data(const LinphoneCallLog *cl) {
275 	return cl->user_data;
276 }
277 
linphone_call_log_set_user_data(LinphoneCallLog * cl,void * ud)278 void linphone_call_log_set_user_data(LinphoneCallLog *cl, void *ud) {
279 	cl->user_data = ud;
280 }
281 
linphone_call_log_ref(LinphoneCallLog * cl)282 LinphoneCallLog * linphone_call_log_ref(LinphoneCallLog *cl) {
283 	belle_sip_object_ref(cl);
284 	return cl;
285 }
286 
linphone_call_log_unref(LinphoneCallLog * cl)287 void linphone_call_log_unref(LinphoneCallLog *cl) {
288 	belle_sip_object_unref(cl);
289 }
290 
291 /*******************************************************************************
292  * Constructor and destructor functions                                        *
293  ******************************************************************************/
294 
_linphone_call_log_destroy(LinphoneCallLog * cl)295 static void _linphone_call_log_destroy(LinphoneCallLog *cl) {
296 	if (cl->from!=NULL) linphone_address_unref(cl->from);
297 	if (cl->to!=NULL) linphone_address_unref(cl->to);
298 	if (cl->refkey!=NULL) ms_free(cl->refkey);
299 	if (cl->call_id) ms_free(cl->call_id);
300 	if (cl->reporting.reports[LINPHONE_CALL_STATS_AUDIO]!=NULL) linphone_reporting_destroy(cl->reporting.reports[LINPHONE_CALL_STATS_AUDIO]);
301 	if (cl->reporting.reports[LINPHONE_CALL_STATS_VIDEO]!=NULL) linphone_reporting_destroy(cl->reporting.reports[LINPHONE_CALL_STATS_VIDEO]);
302 	if (cl->reporting.reports[LINPHONE_CALL_STATS_TEXT]!=NULL) linphone_reporting_destroy(cl->reporting.reports[LINPHONE_CALL_STATS_TEXT]);
303 	if (cl->error_info) linphone_error_info_unref(cl->error_info);
304 }
305 
linphone_call_log_new(LinphoneCallDir dir,LinphoneAddress * from,LinphoneAddress * to)306 LinphoneCallLog * linphone_call_log_new(LinphoneCallDir dir, LinphoneAddress *from, LinphoneAddress *to) {
307 	LinphoneCallLog *cl=belle_sip_object_new(LinphoneCallLog);
308 	cl->dir=dir;
309 	cl->start_date_time=time(NULL);
310 	set_call_log_date(cl,cl->start_date_time);
311 	cl->from=from;
312 	cl->to=to;
313 	cl->status=LinphoneCallAborted; /*default status*/
314 	cl->quality=-1;
315 	cl->storage_id=0;
316 
317 	cl->reporting.reports[LINPHONE_CALL_STATS_AUDIO]=linphone_reporting_new();
318 	cl->reporting.reports[LINPHONE_CALL_STATS_VIDEO]=linphone_reporting_new();
319 	cl->reporting.reports[LINPHONE_CALL_STATS_TEXT]=linphone_reporting_new();
320 	cl->connected_date_time=0;
321 	return cl;
322 }
323 
324 
325 /* DEPRECATED */
linphone_call_log_destroy(LinphoneCallLog * cl)326 void linphone_call_log_destroy(LinphoneCallLog *cl) {
327 	belle_sip_object_unref(cl);
328 }
329 
330 BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneCallLog);
331 
332 BELLE_SIP_INSTANCIATE_VPTR(LinphoneCallLog, belle_sip_object_t,
333 	(belle_sip_object_destroy_t)_linphone_call_log_destroy,
334 	NULL, // clone
335 	NULL, // marshal
336 	FALSE
337 );
338 
339 
340 /*******************************************************************************
341  * SQL storage related functions                                               *
342  ******************************************************************************/
343 
344 #ifdef SQLITE_STORAGE_ENABLED
345 
linphone_create_call_log_table(sqlite3 * db)346 static void linphone_create_call_log_table(sqlite3* db) {
347 	char* errmsg=NULL;
348 	int ret;
349 	ret=sqlite3_exec(db,"CREATE TABLE IF NOT EXISTS call_history ("
350 							 "id             INTEGER PRIMARY KEY AUTOINCREMENT,"
351 							 "caller         TEXT NOT NULL," // Can't name a field "from"...
352 							 "callee         TEXT NOT NULL,"
353 							 "direction      INTEGER,"
354 							 "duration       INTEGER,"
355 							 "start_time     TEXT NOT NULL,"
356 							 "connected_time TEXT NOT NULL,"
357 							 "status         INTEGER,"
358 							 "videoEnabled   INTEGER,"
359 							 "quality        REAL"
360 						");",
361 			0,0,&errmsg);
362 	if(ret != SQLITE_OK) {
363 		ms_error("Error in creation: %s.\n", errmsg);
364 		sqlite3_free(errmsg);
365 	}
366 }
367 
linphone_update_call_log_table(sqlite3 * db)368 static void linphone_update_call_log_table(sqlite3* db) {
369 	char* errmsg=NULL;
370 	int ret;
371 
372 	// for image url storage
373 	ret=sqlite3_exec(db,"ALTER TABLE call_history ADD COLUMN call_id TEXT;",NULL,NULL,&errmsg);
374 	if(ret != SQLITE_OK) {
375 		ms_message("Table already up to date: %s.", errmsg);
376 		sqlite3_free(errmsg);
377 	} else {
378 		ret=sqlite3_exec(db,"ALTER TABLE call_history ADD COLUMN refkey TEXT;",NULL,NULL,&errmsg);
379 		if(ret != SQLITE_OK) {
380 			ms_message("Table already up to date: %s.", errmsg);
381 			sqlite3_free(errmsg);
382 		} else {
383 			ms_debug("Table call_history updated successfully for call_id and refkey.");
384 		}
385 	}
386 }
387 
linphone_core_call_log_storage_init(LinphoneCore * lc)388 void linphone_core_call_log_storage_init(LinphoneCore *lc) {
389 	int ret;
390 	const char *errmsg;
391 	sqlite3 *db;
392 
393 	linphone_core_call_log_storage_close(lc);
394 
395 	ret=_linphone_sqlite3_open(lc->logs_db_file, &db);
396 	if(ret != SQLITE_OK) {
397 		errmsg = sqlite3_errmsg(db);
398 		ms_error("Error in the opening: %s.\n", errmsg);
399 		sqlite3_close(db);
400 		return;
401 	}
402 
403 	linphone_create_call_log_table(db);
404 	linphone_update_call_log_table(db);
405 	lc->logs_db = db;
406 
407 	// Load the existing call logs
408 	linphone_core_get_call_history(lc);
409 }
410 
linphone_core_call_log_storage_close(LinphoneCore * lc)411 void linphone_core_call_log_storage_close(LinphoneCore *lc) {
412 	if (lc->logs_db){
413 		sqlite3_close(lc->logs_db);
414 		lc->logs_db = NULL;
415 	}
416 }
417 
find_call_log_by_storage_id(bctbx_list_t * call_logs,unsigned int storage_id)418 static LinphoneCallLog * find_call_log_by_storage_id(bctbx_list_t *call_logs, unsigned int storage_id) {
419 	bctbx_list_t *item;
420 	for (item = call_logs; item != NULL; item = bctbx_list_next(item)) {
421 		LinphoneCallLog *call_log = bctbx_list_get_data(item);
422 		if (call_log->storage_id == storage_id) return call_log;
423 	}
424 	return NULL;
425 }
426 
427 /* DB layout:
428  * | 0  | storage_id
429  * | 1  | from
430  * | 2  | to
431  * | 3  | direction flag
432  * | 4  | duration
433  * | 5  | start date time (time_t)
434  * | 6  | connected date time (time_t)
435  * | 7  | status
436  * | 8  | video enabled (1 or 0)
437  * | 9  | quality
438  * | 10 | call_id
439  * | 11 | refkey
440  */
create_call_log(void * data,int argc,char ** argv,char ** colName)441 static int create_call_log(void *data, int argc, char **argv, char **colName) {
442 	CallLogStorageResult *clsres = (CallLogStorageResult *)data;
443 	LinphoneAddress *from;
444 	LinphoneAddress *to;
445 	LinphoneCallDir dir;
446 	LinphoneCallLog *log;
447 
448 	unsigned int storage_id = (unsigned int)atoi(argv[0]);
449 
450 	log = find_call_log_by_storage_id(clsres->core->call_logs, storage_id);
451 	if (log != NULL) {
452 		clsres->result = bctbx_list_append(clsres->result, linphone_call_log_ref(log));
453 		return 0;
454 	}
455 
456 	from = linphone_address_new(argv[1]);
457 	to = linphone_address_new(argv[2]);
458 
459 	if (from == NULL || to == NULL) goto error;
460 
461 	dir = (LinphoneCallDir) atoi(argv[3]);
462 	log = linphone_call_log_new(dir, from, to);
463 
464 	log->storage_id = storage_id;
465 	log->duration = atoi(argv[4]);
466 	log->start_date_time = (time_t)atol(argv[5]);
467 	set_call_log_date(log,log->start_date_time);
468 	log->connected_date_time = (time_t)atol(argv[6]);
469 	log->status = (LinphoneCallStatus) atoi(argv[7]);
470 	log->video_enabled = atoi(argv[8]) == 1;
471 	log->quality = (float)atof(argv[9]);
472 
473 	if (argc > 10) {
474 		if (argv[10] != NULL) {
475 			log->call_id = ms_strdup(argv[10]);
476 		}
477 		if (argv[10] != NULL) {
478 			log->refkey = ms_strdup(argv[11]);
479 		}
480 	}
481 
482 	clsres->result = bctbx_list_append(clsres->result, log);
483 	return 0;
484 
485 error:
486 	if (from){
487 		linphone_address_unref(from);
488 	}
489 	if (to){
490 		linphone_address_unref(to);
491 	}
492 	ms_error("Bad call log at storage_id %u", storage_id);
493 	return 0;
494 }
495 
linphone_sql_request_call_log(sqlite3 * db,const char * stmt,CallLogStorageResult * clsres)496 static void linphone_sql_request_call_log(sqlite3 *db, const char *stmt, CallLogStorageResult *clsres) {
497 	char* errmsg = NULL;
498 	int ret;
499 	ret = sqlite3_exec(db, stmt, create_call_log, clsres, &errmsg);
500 	if (ret != SQLITE_OK) {
501 		ms_error("linphone_sql_request: statement %s -> error sqlite3_exec(): %s.", stmt, errmsg);
502 		sqlite3_free(errmsg);
503 	}
504 }
505 
linphone_sql_request_generic(sqlite3 * db,const char * stmt)506 static int linphone_sql_request_generic(sqlite3* db, const char *stmt) {
507 	char* errmsg = NULL;
508 	int ret;
509 	ret = sqlite3_exec(db, stmt, NULL, NULL, &errmsg);
510 	if (ret != SQLITE_OK) {
511 		ms_error("linphone_sql_request: statement %s -> error sqlite3_exec(): %s.", stmt, errmsg);
512 		sqlite3_free(errmsg);
513 	}
514 	return ret;
515 }
516 
linphone_core_store_call_log(LinphoneCore * lc,LinphoneCallLog * log)517 void linphone_core_store_call_log(LinphoneCore *lc, LinphoneCallLog *log) {
518 	if (lc && lc->logs_db){
519 		char *from, *to;
520 		char *buf;
521 
522 		from = linphone_address_as_string(log->from);
523 		to = linphone_address_as_string(log->to);
524 		buf = sqlite3_mprintf("INSERT INTO call_history VALUES(NULL,%Q,%Q,%i,%i,%lld,%lld,%i,%i,%f,%Q,%Q);",
525 						from,
526 						to,
527 						log->dir,
528 						log->duration,
529 						(int64_t)log->start_date_time,
530 						(int64_t)log->connected_date_time,
531 						log->status,
532 						log->video_enabled ? 1 : 0,
533 						log->quality,
534 						log->call_id,
535 						log->refkey
536 					);
537 		linphone_sql_request_generic(lc->logs_db, buf);
538 		sqlite3_free(buf);
539 		ms_free(from);
540 		ms_free(to);
541 
542 		log->storage_id = (unsigned int)sqlite3_last_insert_rowid(lc->logs_db);
543 	}
544 
545 	if (lc) {
546 		lc->call_logs = bctbx_list_prepend(lc->call_logs, linphone_call_log_ref(log));
547 	}
548 }
549 
linphone_core_get_call_history(LinphoneCore * lc)550 const bctbx_list_t *linphone_core_get_call_history(LinphoneCore *lc) {
551 	char *buf;
552 	uint64_t begin,end;
553 	CallLogStorageResult clsres;
554 
555 	if (!lc || lc->logs_db == NULL) return NULL;
556     if (lc->call_logs != NULL) return lc->call_logs;
557 
558 	if (lc->max_call_logs != LINPHONE_MAX_CALL_HISTORY_UNLIMITED){
559 		buf = sqlite3_mprintf("SELECT * FROM call_history ORDER BY id DESC LIMIT %i", lc->max_call_logs);
560 	}else{
561 		buf = sqlite3_mprintf("SELECT * FROM call_history ORDER BY id DESC");
562 	}
563 
564 	clsres.core = lc;
565 	clsres.result = NULL;
566 	begin = ortp_get_cur_time_ms();
567 	linphone_sql_request_call_log(lc->logs_db, buf, &clsres);
568 	end = ortp_get_cur_time_ms();
569 	ms_message("%s(): completed in %i ms",__FUNCTION__, (int)(end-begin));
570 	sqlite3_free(buf);
571 
572 	lc->call_logs = clsres.result;
573 	return lc->call_logs;
574 }
575 
linphone_core_delete_call_history(LinphoneCore * lc)576 void linphone_core_delete_call_history(LinphoneCore *lc) {
577 	char *buf;
578 
579 	if (!lc || lc->logs_db == NULL) return ;
580 
581 	buf = sqlite3_mprintf("DELETE FROM call_history");
582 	linphone_sql_request_generic(lc->logs_db, buf);
583 	sqlite3_free(buf);
584 }
585 
linphone_core_delete_call_log(LinphoneCore * lc,LinphoneCallLog * log)586 void linphone_core_delete_call_log(LinphoneCore *lc, LinphoneCallLog *log) {
587 	char *buf;
588 
589 	if (!lc || lc->logs_db == NULL) return ;
590 
591 	buf = sqlite3_mprintf("DELETE FROM call_history WHERE id = %u", log->storage_id);
592 	linphone_sql_request_generic(lc->logs_db, buf);
593 	sqlite3_free(buf);
594 }
595 
linphone_core_get_call_history_size(LinphoneCore * lc)596 int linphone_core_get_call_history_size(LinphoneCore *lc) {
597 	int numrows = 0;
598 	char *buf;
599 	sqlite3_stmt *selectStatement;
600 	int returnValue;
601 
602 	if (!lc || lc->logs_db == NULL) return 0;
603 
604 	buf = sqlite3_mprintf("SELECT count(*) FROM call_history");
605 	returnValue = sqlite3_prepare_v2(lc->logs_db, buf, -1, &selectStatement, NULL);
606 	if (returnValue == SQLITE_OK){
607 		if(sqlite3_step(selectStatement) == SQLITE_ROW){
608 			numrows = sqlite3_column_int(selectStatement, 0);
609 		}
610 	}
611 	sqlite3_finalize(selectStatement);
612 	sqlite3_free(buf);
613 
614 	return numrows;
615 }
616 
linphone_core_get_call_history_for_address(LinphoneCore * lc,const LinphoneAddress * addr)617 bctbx_list_t * linphone_core_get_call_history_for_address(LinphoneCore *lc, const LinphoneAddress *addr) {
618 	char *buf;
619 	char *sipAddress;
620 	uint64_t begin,end;
621 	CallLogStorageResult clsres;
622 
623 	if (!lc || lc->logs_db == NULL || addr == NULL) return NULL;
624 
625 	/*since we want to append query parameters depending on arguments given, we use malloc instead of sqlite3_mprintf*/
626 	sipAddress = linphone_address_as_string_uri_only(addr);
627 	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
628 
629 	clsres.core = lc;
630 	clsres.result = NULL;
631 	begin = ortp_get_cur_time_ms();
632 	linphone_sql_request_call_log(lc->logs_db, buf, &clsres);
633 	end = ortp_get_cur_time_ms();
634 	ms_message("%s(): completed in %i ms",__FUNCTION__, (int)(end-begin));
635 	sqlite3_free(buf);
636 	ms_free(sipAddress);
637 
638 	return clsres.result;
639 }
640 
linphone_core_get_last_outgoing_call_log(LinphoneCore * lc)641 LinphoneCallLog * linphone_core_get_last_outgoing_call_log(LinphoneCore *lc) {
642 	char *buf;
643 	uint64_t begin,end;
644 	CallLogStorageResult clsres;
645 	LinphoneCallLog *result = NULL;
646 
647 	if (!lc || lc->logs_db == NULL) return NULL;
648 
649 	/*since we want to append query parameters depending on arguments given, we use malloc instead of sqlite3_mprintf*/
650 	buf = sqlite3_mprintf("SELECT * FROM call_history WHERE direction = 0 ORDER BY id DESC LIMIT 1");
651 
652 	clsres.core = lc;
653 	clsres.result = NULL;
654 	begin = ortp_get_cur_time_ms();
655 	linphone_sql_request_call_log(lc->logs_db, buf, &clsres);
656 	end = ortp_get_cur_time_ms();
657 	ms_message("%s(): completed in %i ms",__FUNCTION__, (int)(end-begin));
658 	sqlite3_free(buf);
659 
660 	if (clsres.result != NULL) {
661 		result = (LinphoneCallLog *)bctbx_list_get_data(clsres.result);
662 	}
663 
664 	return result;
665 }
666 
linphone_core_find_call_log_from_call_id(LinphoneCore * lc,const char * call_id)667 LinphoneCallLog * linphone_core_find_call_log_from_call_id(LinphoneCore *lc, const char *call_id) {
668 	char *buf;
669 	uint64_t begin,end;
670 	CallLogStorageResult clsres;
671 	LinphoneCallLog* result = NULL;
672 
673 	if (!lc || lc->logs_db == NULL) return NULL;
674 
675 	/*since we want to append query parameters depending on arguments given, we use malloc instead of sqlite3_mprintf*/
676 	buf = sqlite3_mprintf("SELECT * FROM call_history WHERE call_id = '%q' ORDER BY id DESC LIMIT 1", call_id);
677 
678 	clsres.core = lc;
679 	clsres.result = NULL;
680 	begin = ortp_get_cur_time_ms();
681 	linphone_sql_request_call_log(lc->logs_db, buf, &clsres);
682 	end = ortp_get_cur_time_ms();
683 	ms_message("%s(): completed in %i ms",__FUNCTION__, (int)(end-begin));
684 	sqlite3_free(buf);
685 
686 	if (clsres.result != NULL) {
687 		result = (LinphoneCallLog *)bctbx_list_get_data(clsres.result);
688 	}
689 
690 	return result;
691 }
692 
693 #else
694 
linphone_core_call_log_storage_init(LinphoneCore * lc)695 void linphone_core_call_log_storage_init(LinphoneCore *lc) {
696 }
697 
linphone_core_call_log_storage_close(LinphoneCore * lc)698 void linphone_core_call_log_storage_close(LinphoneCore *lc) {
699 }
700 
linphone_core_store_call_log(LinphoneCore * lc,LinphoneCallLog * log)701 void linphone_core_store_call_log(LinphoneCore *lc, LinphoneCallLog *log) {
702 }
703 
linphone_core_get_call_history(LinphoneCore * lc)704 const bctbx_list_t *linphone_core_get_call_history(LinphoneCore *lc) {
705 	return NULL;
706 }
707 
linphone_core_delete_call_history(LinphoneCore * lc)708 void linphone_core_delete_call_history(LinphoneCore *lc) {
709 }
710 
linphone_core_delete_call_log(LinphoneCore * lc,LinphoneCallLog * log)711 void linphone_core_delete_call_log(LinphoneCore *lc, LinphoneCallLog *log) {
712 }
713 
linphone_core_get_call_history_size(LinphoneCore * lc)714 int linphone_core_get_call_history_size(LinphoneCore *lc) {
715 	return 0;
716 }
717 
linphone_core_get_call_history_for_address(LinphoneCore * lc,const LinphoneAddress * addr)718 bctbx_list_t * linphone_core_get_call_history_for_address(LinphoneCore *lc, const LinphoneAddress *addr) {
719 	return NULL;
720 }
721 
linphone_core_get_last_outgoing_call_log(LinphoneCore * lc)722 LinphoneCallLog * linphone_core_get_last_outgoing_call_log(LinphoneCore *lc) {
723 	return NULL;
724 }
725 
linphone_core_find_call_log_from_call_id(LinphoneCore * lc,const char * call_id)726 LinphoneCallLog * linphone_core_find_call_log_from_call_id(LinphoneCore *lc, const char *call_id) {
727 	return NULL;
728 }
729 
730 #endif
731