mirror of
https://gitlab.linphone.org/BC/public/linphone-iphone.git
synced 2026-01-28 00:29:21 +00:00
Use a transaction for message migration. On 10000 messages, we have a 20x speedup.
This commit is contained in:
parent
dbbea2a41c
commit
1a89e8a174
1 changed files with 450 additions and 446 deletions
|
|
@ -1,446 +1,450 @@
|
|||
/*
|
||||
message_storage.c
|
||||
Copyright (C) 2012 Belledonne Communications, Grenoble, France
|
||||
|
||||
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
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "private.h"
|
||||
#include "linphonecore.h"
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
static inline char *my_ctime_r(const time_t *t, char *buf){
|
||||
strcpy(buf,ctime(t));
|
||||
return buf;
|
||||
}
|
||||
|
||||
#else
|
||||
#define my_ctime_r ctime_r
|
||||
#endif
|
||||
|
||||
#ifdef MSG_STORAGE_ENABLED
|
||||
|
||||
#include "sqlite3.h"
|
||||
|
||||
static inline LinphoneChatMessage* get_transient_message(LinphoneChatRoom* cr, unsigned int storage_id){
|
||||
MSList* transients = cr->transient_messages;
|
||||
LinphoneChatMessage* chat;
|
||||
while( transients ){
|
||||
chat = (LinphoneChatMessage*)transients->data;
|
||||
if(chat->storage_id == storage_id){
|
||||
return linphone_chat_message_ref(chat);
|
||||
}
|
||||
transients = transients->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void create_chat_message(char **argv, void *data){
|
||||
LinphoneChatRoom *cr = (LinphoneChatRoom *)data;
|
||||
LinphoneAddress *from;
|
||||
|
||||
unsigned int storage_id = atoi(argv[0]);
|
||||
|
||||
// check if the message exists in the transient list, in which case we should return that one.
|
||||
LinphoneChatMessage* new_message = get_transient_message(cr, storage_id);
|
||||
if( new_message == NULL ){
|
||||
new_message = linphone_chat_room_create_message(cr, argv[4]);
|
||||
|
||||
if(atoi(argv[3])==LinphoneChatMessageIncoming){
|
||||
new_message->dir=LinphoneChatMessageIncoming;
|
||||
from=linphone_address_new(argv[2]);
|
||||
} else {
|
||||
new_message->dir=LinphoneChatMessageOutgoing;
|
||||
from=linphone_address_new(argv[1]);
|
||||
}
|
||||
linphone_chat_message_set_from(new_message,from);
|
||||
linphone_address_destroy(from);
|
||||
|
||||
if( argv[9] != NULL ){
|
||||
new_message->time = (time_t)atol(argv[9]);
|
||||
} else {
|
||||
new_message->time = time(NULL);
|
||||
}
|
||||
|
||||
new_message->is_read=atoi(argv[6]);
|
||||
new_message->state=atoi(argv[7]);
|
||||
new_message->storage_id=storage_id;
|
||||
new_message->external_body_url=argv[8]?ms_strdup(argv[8]):NULL;
|
||||
}
|
||||
cr->messages_hist=ms_list_prepend(cr->messages_hist,new_message);
|
||||
}
|
||||
|
||||
// Called when fetching all conversations from database
|
||||
static int callback_all(void *data, int argc, char **argv, char **colName){
|
||||
LinphoneCore* lc = (LinphoneCore*) data;
|
||||
char* address = argv[0];
|
||||
linphone_core_get_or_create_chat_room(lc, address);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int callback(void *data, int argc, char **argv, char **colName){
|
||||
create_chat_message(argv,data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void linphone_sql_request_message(sqlite3 *db,const char *stmt,LinphoneChatRoom *cr){
|
||||
char* errmsg=NULL;
|
||||
int ret;
|
||||
ret=sqlite3_exec(db,stmt,callback,cr,&errmsg);
|
||||
if(ret != SQLITE_OK) {
|
||||
ms_error("Error in creation: %s.\n", errmsg);
|
||||
sqlite3_free(errmsg);
|
||||
}
|
||||
}
|
||||
|
||||
void linphone_sql_request(sqlite3* db,const char *stmt){
|
||||
char* errmsg=NULL;
|
||||
int ret;
|
||||
ret=sqlite3_exec(db,stmt,NULL,NULL,&errmsg);
|
||||
if(ret != SQLITE_OK) {
|
||||
ms_error("linphone_sql_request: error sqlite3_exec(): %s.\n", errmsg);
|
||||
sqlite3_free(errmsg);
|
||||
}
|
||||
}
|
||||
|
||||
// Process the request to fetch all chat contacts
|
||||
void linphone_sql_request_all(sqlite3* db,const char *stmt, LinphoneCore* lc){
|
||||
char* errmsg=NULL;
|
||||
int ret;
|
||||
ret=sqlite3_exec(db,stmt,callback_all,lc,&errmsg);
|
||||
if(ret != SQLITE_OK) {
|
||||
ms_error("linphone_sql_request_all: error sqlite3_exec(): %s.\n", errmsg);
|
||||
sqlite3_free(errmsg);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int linphone_chat_message_store(LinphoneChatMessage *msg){
|
||||
LinphoneCore *lc=linphone_chat_room_get_lc(msg->chat_room);
|
||||
int id=0;
|
||||
|
||||
if (lc->db){
|
||||
char *peer=linphone_address_as_string_uri_only(linphone_chat_room_get_peer_address(msg->chat_room));
|
||||
char *local_contact=linphone_address_as_string_uri_only(linphone_chat_message_get_local_address(msg));
|
||||
char *buf=sqlite3_mprintf("INSERT INTO history VALUES(NULL,%Q,%Q,%i,%Q,%Q,%i,%i,%Q,%i);",
|
||||
local_contact,
|
||||
peer,
|
||||
msg->dir,
|
||||
msg->message,
|
||||
"-1", /* use UTC field now */
|
||||
msg->is_read,
|
||||
msg->state,
|
||||
msg->external_body_url,
|
||||
msg->time);
|
||||
linphone_sql_request(lc->db,buf);
|
||||
sqlite3_free(buf);
|
||||
ms_free(local_contact);
|
||||
ms_free(peer);
|
||||
id = (unsigned int) sqlite3_last_insert_rowid (lc->db);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
void linphone_chat_message_store_state(LinphoneChatMessage *msg){
|
||||
LinphoneCore *lc=msg->chat_room->lc;
|
||||
if (lc->db){
|
||||
char *buf=sqlite3_mprintf("UPDATE history SET status=%i WHERE (message = %Q OR url = %Q) AND utc = %i;",
|
||||
msg->state,msg->message,msg->external_body_url,msg->time);
|
||||
linphone_sql_request(lc->db,buf);
|
||||
sqlite3_free(buf);
|
||||
}
|
||||
|
||||
if( msg->state == LinphoneChatMessageStateDelivered
|
||||
|| msg->state == LinphoneChatMessageStateNotDelivered ){
|
||||
// message is not transient anymore, we can remove it from our transient list:
|
||||
msg->chat_room->transient_messages = ms_list_remove(msg->chat_room->transient_messages, msg);
|
||||
linphone_chat_message_unref(msg);
|
||||
}
|
||||
}
|
||||
|
||||
void linphone_chat_room_mark_as_read(LinphoneChatRoom *cr){
|
||||
LinphoneCore *lc=linphone_chat_room_get_lc(cr);
|
||||
int read=1;
|
||||
|
||||
if (lc->db==NULL) return ;
|
||||
|
||||
char *peer=linphone_address_as_string_uri_only(linphone_chat_room_get_peer_address(cr));
|
||||
char *buf=sqlite3_mprintf("UPDATE history SET read=%i WHERE remoteContact = %Q;",
|
||||
read,peer);
|
||||
linphone_sql_request(lc->db,buf);
|
||||
sqlite3_free(buf);
|
||||
ms_free(peer);
|
||||
}
|
||||
|
||||
void linphone_chat_room_update_url(LinphoneChatRoom *cr, LinphoneChatMessage *msg) {
|
||||
LinphoneCore *lc=linphone_chat_room_get_lc(cr);
|
||||
|
||||
if (lc->db==NULL) return ;
|
||||
|
||||
char *buf=sqlite3_mprintf("UPDATE history SET url=%Q WHERE id=%i;",msg->external_body_url,msg->storage_id);
|
||||
linphone_sql_request(lc->db,buf);
|
||||
sqlite3_free(buf);
|
||||
}
|
||||
|
||||
int linphone_chat_room_get_unread_messages_count(LinphoneChatRoom *cr){
|
||||
LinphoneCore *lc=linphone_chat_room_get_lc(cr);
|
||||
int numrows=0;
|
||||
|
||||
if (lc->db==NULL) return 0;
|
||||
|
||||
char *peer=linphone_address_as_string_uri_only(linphone_chat_room_get_peer_address(cr));
|
||||
char *buf=sqlite3_mprintf("SELECT count(*) FROM history WHERE remoteContact = %Q AND read = 0;",peer);
|
||||
sqlite3_stmt *selectStatement;
|
||||
int returnValue = sqlite3_prepare_v2(lc->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);
|
||||
ms_free(peer);
|
||||
return numrows;
|
||||
}
|
||||
|
||||
void linphone_chat_room_delete_message(LinphoneChatRoom *cr, LinphoneChatMessage *msg) {
|
||||
LinphoneCore *lc=cr->lc;
|
||||
|
||||
if (lc->db==NULL) return ;
|
||||
|
||||
char *buf=sqlite3_mprintf("DELETE FROM history WHERE id = %i;", msg->storage_id);
|
||||
linphone_sql_request(lc->db,buf);
|
||||
sqlite3_free(buf);
|
||||
}
|
||||
|
||||
void linphone_chat_room_delete_history(LinphoneChatRoom *cr){
|
||||
LinphoneCore *lc=cr->lc;
|
||||
|
||||
if (lc->db==NULL) return ;
|
||||
|
||||
char *peer=linphone_address_as_string_uri_only(linphone_chat_room_get_peer_address(cr));
|
||||
char *buf=sqlite3_mprintf("DELETE FROM history WHERE remoteContact = %Q;",peer);
|
||||
linphone_sql_request(lc->db,buf);
|
||||
sqlite3_free(buf);
|
||||
ms_free(peer);
|
||||
}
|
||||
|
||||
MSList *linphone_chat_room_get_history(LinphoneChatRoom *cr,int nb_message){
|
||||
LinphoneCore *lc=linphone_chat_room_get_lc(cr);
|
||||
MSList *ret;
|
||||
char *buf;
|
||||
char *peer;
|
||||
uint64_t begin,end;
|
||||
|
||||
if (lc->db==NULL) return NULL;
|
||||
peer=linphone_address_as_string_uri_only(linphone_chat_room_get_peer_address(cr));
|
||||
cr->messages_hist = NULL;
|
||||
if (nb_message > 0)
|
||||
buf=sqlite3_mprintf("SELECT * FROM history WHERE remoteContact = %Q ORDER BY id DESC LIMIT %i ;",peer,nb_message);
|
||||
else
|
||||
buf=sqlite3_mprintf("SELECT * FROM history WHERE remoteContact = %Q ORDER BY id DESC;",peer);
|
||||
begin=ortp_get_cur_time_ms();
|
||||
linphone_sql_request_message(lc->db,buf,cr);
|
||||
end=ortp_get_cur_time_ms();
|
||||
ms_message("linphone_chat_room_get_history(): completed in %i ms",(int)(end-begin));
|
||||
sqlite3_free(buf);
|
||||
ret=cr->messages_hist;
|
||||
cr->messages_hist=NULL;
|
||||
ms_free(peer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void linphone_close_storage(sqlite3* db){
|
||||
sqlite3_close(db);
|
||||
}
|
||||
|
||||
void linphone_create_table(sqlite3* db){
|
||||
char* errmsg=NULL;
|
||||
int ret;
|
||||
ret=sqlite3_exec(db,"CREATE TABLE IF NOT EXISTS history ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||
"localContact TEXT NOT NULL,"
|
||||
"remoteContact TEXT NOT NULL,"
|
||||
"direction INTEGER,"
|
||||
"message TEXT,"
|
||||
"time TEXT NOT NULL,"
|
||||
"read INTEGER,"
|
||||
"status INTEGER"
|
||||
");",
|
||||
0,0,&errmsg);
|
||||
if(ret != SQLITE_OK) {
|
||||
ms_error("Error in creation: %s.\n", errmsg);
|
||||
sqlite3_free(errmsg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static const char *days[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
|
||||
static const char *months[]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
|
||||
static time_t parse_time_from_db( const char* time ){
|
||||
/* messages used to be stored in the DB by using string-based time */
|
||||
struct tm ret={0};
|
||||
char tmp1[80]={0};
|
||||
char tmp2[80]={0};
|
||||
int i,j;
|
||||
time_t parsed = 0;
|
||||
|
||||
if( sscanf(time,"%3c %3c%d%d:%d:%d %d",tmp1,tmp2,&ret.tm_mday,
|
||||
&ret.tm_hour,&ret.tm_min,&ret.tm_sec,&ret.tm_year) == 7 ){
|
||||
ret.tm_year-=1900;
|
||||
for(i=0;i<7;i++) {
|
||||
if(strcmp(tmp1,days[i])==0) ret.tm_wday=i;
|
||||
}
|
||||
for(j=0;j<12;j++) {
|
||||
if(strcmp(tmp2,months[j])==0) ret.tm_mon=j;
|
||||
}
|
||||
ret.tm_isdst=-1;
|
||||
parsed = mktime(&ret);
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
|
||||
|
||||
static int migrate_messages(void* data,int argc, char** argv, char** column_names) {
|
||||
time_t new_time = parse_time_from_db(argv[1]);
|
||||
if( new_time ){
|
||||
/* replace 'time' by -1 and set 'utc' to the timestamp */
|
||||
char *buf = sqlite3_mprintf("UPDATE history SET utc=%i,time='-1' WHERE id=%i", new_time, atoi(argv[0]));
|
||||
if( buf) {
|
||||
linphone_sql_request((sqlite3*)data, buf);
|
||||
sqlite3_free(buf);
|
||||
}
|
||||
} else {
|
||||
ms_warning("Cannot parse time %s from id %s", argv[1], argv[0]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void linphone_migrate_timestamps(sqlite3* db){
|
||||
int ret;
|
||||
char* errmsg = NULL;
|
||||
uint64_t begin=ortp_get_cur_time_ms();
|
||||
|
||||
ret = sqlite3_exec(db,"SELECT id,time,direction FROM history WHERE time != '-1'", migrate_messages, db, &errmsg);
|
||||
if( ret != SQLITE_OK ){
|
||||
ms_warning("Error migrating outgoing messages: %s.\n", errmsg);
|
||||
sqlite3_free(errmsg);
|
||||
} else {
|
||||
uint64_t end=ortp_get_cur_time_ms();
|
||||
ms_message("Migrated message timestamps to UTC in %i ms",(int)(end-begin));
|
||||
}
|
||||
}
|
||||
|
||||
void linphone_update_table(sqlite3* db) {
|
||||
char* errmsg=NULL;
|
||||
int ret;
|
||||
|
||||
// for image url storage
|
||||
ret=sqlite3_exec(db,"ALTER TABLE history ADD COLUMN url TEXT;",NULL,NULL,&errmsg);
|
||||
if(ret != SQLITE_OK) {
|
||||
ms_message("Table already up to date: %s.", errmsg);
|
||||
sqlite3_free(errmsg);
|
||||
} else {
|
||||
ms_debug("Table updated successfully for URL.");
|
||||
}
|
||||
|
||||
// for UTC timestamp storage
|
||||
ret = sqlite3_exec(db, "ALTER TABLE history ADD COLUMN utc INTEGER;", NULL,NULL,&errmsg);
|
||||
if( ret != SQLITE_OK ){
|
||||
ms_message("Table already up to date: %s.", errmsg);
|
||||
sqlite3_free(errmsg);
|
||||
} else {
|
||||
ms_debug("Table updated successfully for UTC.");
|
||||
// migrate from old text-based timestamps to unix time-based timestamps
|
||||
linphone_migrate_timestamps(db);
|
||||
}
|
||||
}
|
||||
|
||||
void linphone_message_storage_init_chat_rooms(LinphoneCore *lc) {
|
||||
char *buf;
|
||||
|
||||
if (lc->db==NULL) return;
|
||||
buf=sqlite3_mprintf("SELECT remoteContact FROM history GROUP BY remoteContact;");
|
||||
linphone_sql_request_all(lc->db,buf,lc);
|
||||
sqlite3_free(buf);
|
||||
}
|
||||
|
||||
void linphone_core_message_storage_init(LinphoneCore *lc){
|
||||
int ret;
|
||||
const char *errmsg;
|
||||
sqlite3 *db;
|
||||
|
||||
linphone_core_message_storage_close(lc);
|
||||
|
||||
ret=sqlite3_open(lc->chat_db_file,&db);
|
||||
if(ret != SQLITE_OK) {
|
||||
errmsg=sqlite3_errmsg(db);
|
||||
ms_error("Error in the opening: %s.\n", errmsg);
|
||||
sqlite3_close(db);
|
||||
}
|
||||
linphone_create_table(db);
|
||||
linphone_update_table(db);
|
||||
lc->db=db;
|
||||
|
||||
// Create a chatroom for each contact in the chat history
|
||||
linphone_message_storage_init_chat_rooms(lc);
|
||||
}
|
||||
|
||||
void linphone_core_message_storage_close(LinphoneCore *lc){
|
||||
if (lc->db){
|
||||
sqlite3_close(lc->db);
|
||||
lc->db=NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
unsigned int linphone_chat_message_store(LinphoneChatMessage *cr){
|
||||
return 0;
|
||||
}
|
||||
|
||||
void linphone_chat_message_store_state(LinphoneChatMessage *cr){
|
||||
}
|
||||
|
||||
void linphone_chat_room_mark_as_read(LinphoneChatRoom *cr){
|
||||
}
|
||||
|
||||
MSList *linphone_chat_room_get_history(LinphoneChatRoom *cr,int nb_message){
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void linphone_chat_room_delete_message(LinphoneChatRoom *cr, LinphoneChatMessage *msg) {
|
||||
}
|
||||
|
||||
void linphone_chat_room_delete_history(LinphoneChatRoom *cr){
|
||||
}
|
||||
|
||||
void linphone_message_storage_init_chat_rooms(LinphoneCore *lc) {
|
||||
}
|
||||
|
||||
void linphone_core_message_storage_init(LinphoneCore *lc){
|
||||
}
|
||||
|
||||
void linphone_core_message_storage_close(LinphoneCore *lc){
|
||||
}
|
||||
|
||||
void linphone_chat_room_update_url(LinphoneChatRoom *cr, LinphoneChatMessage *msg) {
|
||||
}
|
||||
|
||||
int linphone_chat_room_get_unread_messages_count(LinphoneChatRoom *cr){
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
/*
|
||||
message_storage.c
|
||||
Copyright (C) 2012 Belledonne Communications, Grenoble, France
|
||||
|
||||
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
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "private.h"
|
||||
#include "linphonecore.h"
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
static inline char *my_ctime_r(const time_t *t, char *buf){
|
||||
strcpy(buf,ctime(t));
|
||||
return buf;
|
||||
}
|
||||
|
||||
#else
|
||||
#define my_ctime_r ctime_r
|
||||
#endif
|
||||
|
||||
#ifdef MSG_STORAGE_ENABLED
|
||||
|
||||
#include "sqlite3.h"
|
||||
|
||||
static inline LinphoneChatMessage* get_transient_message(LinphoneChatRoom* cr, unsigned int storage_id){
|
||||
MSList* transients = cr->transient_messages;
|
||||
LinphoneChatMessage* chat;
|
||||
while( transients ){
|
||||
chat = (LinphoneChatMessage*)transients->data;
|
||||
if(chat->storage_id == storage_id){
|
||||
return linphone_chat_message_ref(chat);
|
||||
}
|
||||
transients = transients->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void create_chat_message(char **argv, void *data){
|
||||
LinphoneChatRoom *cr = (LinphoneChatRoom *)data;
|
||||
LinphoneAddress *from;
|
||||
|
||||
unsigned int storage_id = atoi(argv[0]);
|
||||
|
||||
// check if the message exists in the transient list, in which case we should return that one.
|
||||
LinphoneChatMessage* new_message = get_transient_message(cr, storage_id);
|
||||
if( new_message == NULL ){
|
||||
new_message = linphone_chat_room_create_message(cr, argv[4]);
|
||||
|
||||
if(atoi(argv[3])==LinphoneChatMessageIncoming){
|
||||
new_message->dir=LinphoneChatMessageIncoming;
|
||||
from=linphone_address_new(argv[2]);
|
||||
} else {
|
||||
new_message->dir=LinphoneChatMessageOutgoing;
|
||||
from=linphone_address_new(argv[1]);
|
||||
}
|
||||
linphone_chat_message_set_from(new_message,from);
|
||||
linphone_address_destroy(from);
|
||||
|
||||
if( argv[9] != NULL ){
|
||||
new_message->time = (time_t)atol(argv[9]);
|
||||
} else {
|
||||
new_message->time = time(NULL);
|
||||
}
|
||||
|
||||
new_message->is_read=atoi(argv[6]);
|
||||
new_message->state=atoi(argv[7]);
|
||||
new_message->storage_id=storage_id;
|
||||
new_message->external_body_url=argv[8]?ms_strdup(argv[8]):NULL;
|
||||
}
|
||||
cr->messages_hist=ms_list_prepend(cr->messages_hist,new_message);
|
||||
}
|
||||
|
||||
// Called when fetching all conversations from database
|
||||
static int callback_all(void *data, int argc, char **argv, char **colName){
|
||||
LinphoneCore* lc = (LinphoneCore*) data;
|
||||
char* address = argv[0];
|
||||
linphone_core_get_or_create_chat_room(lc, address);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int callback(void *data, int argc, char **argv, char **colName){
|
||||
create_chat_message(argv,data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void linphone_sql_request_message(sqlite3 *db,const char *stmt,LinphoneChatRoom *cr){
|
||||
char* errmsg=NULL;
|
||||
int ret;
|
||||
ret=sqlite3_exec(db,stmt,callback,cr,&errmsg);
|
||||
if(ret != SQLITE_OK) {
|
||||
ms_error("Error in creation: %s.\n", errmsg);
|
||||
sqlite3_free(errmsg);
|
||||
}
|
||||
}
|
||||
|
||||
void linphone_sql_request(sqlite3* db,const char *stmt){
|
||||
char* errmsg=NULL;
|
||||
int ret;
|
||||
ret=sqlite3_exec(db,stmt,NULL,NULL,&errmsg);
|
||||
if(ret != SQLITE_OK) {
|
||||
ms_error("linphone_sql_request: error sqlite3_exec(): %s.\n", errmsg);
|
||||
sqlite3_free(errmsg);
|
||||
}
|
||||
}
|
||||
|
||||
// Process the request to fetch all chat contacts
|
||||
void linphone_sql_request_all(sqlite3* db,const char *stmt, LinphoneCore* lc){
|
||||
char* errmsg=NULL;
|
||||
int ret;
|
||||
ret=sqlite3_exec(db,stmt,callback_all,lc,&errmsg);
|
||||
if(ret != SQLITE_OK) {
|
||||
ms_error("linphone_sql_request_all: error sqlite3_exec(): %s.\n", errmsg);
|
||||
sqlite3_free(errmsg);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int linphone_chat_message_store(LinphoneChatMessage *msg){
|
||||
LinphoneCore *lc=linphone_chat_room_get_lc(msg->chat_room);
|
||||
int id=0;
|
||||
|
||||
if (lc->db){
|
||||
char *peer=linphone_address_as_string_uri_only(linphone_chat_room_get_peer_address(msg->chat_room));
|
||||
char *local_contact=linphone_address_as_string_uri_only(linphone_chat_message_get_local_address(msg));
|
||||
char *buf=sqlite3_mprintf("INSERT INTO history VALUES(NULL,%Q,%Q,%i,%Q,%Q,%i,%i,%Q,%i);",
|
||||
local_contact,
|
||||
peer,
|
||||
msg->dir,
|
||||
msg->message,
|
||||
"-1", /* use UTC field now */
|
||||
msg->is_read,
|
||||
msg->state,
|
||||
msg->external_body_url,
|
||||
msg->time);
|
||||
linphone_sql_request(lc->db,buf);
|
||||
sqlite3_free(buf);
|
||||
ms_free(local_contact);
|
||||
ms_free(peer);
|
||||
id = (unsigned int) sqlite3_last_insert_rowid (lc->db);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
void linphone_chat_message_store_state(LinphoneChatMessage *msg){
|
||||
LinphoneCore *lc=msg->chat_room->lc;
|
||||
if (lc->db){
|
||||
char *buf=sqlite3_mprintf("UPDATE history SET status=%i WHERE (message = %Q OR url = %Q) AND utc = %i;",
|
||||
msg->state,msg->message,msg->external_body_url,msg->time);
|
||||
linphone_sql_request(lc->db,buf);
|
||||
sqlite3_free(buf);
|
||||
}
|
||||
|
||||
if( msg->state == LinphoneChatMessageStateDelivered
|
||||
|| msg->state == LinphoneChatMessageStateNotDelivered ){
|
||||
// message is not transient anymore, we can remove it from our transient list:
|
||||
msg->chat_room->transient_messages = ms_list_remove(msg->chat_room->transient_messages, msg);
|
||||
linphone_chat_message_unref(msg);
|
||||
}
|
||||
}
|
||||
|
||||
void linphone_chat_room_mark_as_read(LinphoneChatRoom *cr){
|
||||
LinphoneCore *lc=linphone_chat_room_get_lc(cr);
|
||||
int read=1;
|
||||
|
||||
if (lc->db==NULL) return ;
|
||||
|
||||
char *peer=linphone_address_as_string_uri_only(linphone_chat_room_get_peer_address(cr));
|
||||
char *buf=sqlite3_mprintf("UPDATE history SET read=%i WHERE remoteContact = %Q;",
|
||||
read,peer);
|
||||
linphone_sql_request(lc->db,buf);
|
||||
sqlite3_free(buf);
|
||||
ms_free(peer);
|
||||
}
|
||||
|
||||
void linphone_chat_room_update_url(LinphoneChatRoom *cr, LinphoneChatMessage *msg) {
|
||||
LinphoneCore *lc=linphone_chat_room_get_lc(cr);
|
||||
|
||||
if (lc->db==NULL) return ;
|
||||
|
||||
char *buf=sqlite3_mprintf("UPDATE history SET url=%Q WHERE id=%i;",msg->external_body_url,msg->storage_id);
|
||||
linphone_sql_request(lc->db,buf);
|
||||
sqlite3_free(buf);
|
||||
}
|
||||
|
||||
int linphone_chat_room_get_unread_messages_count(LinphoneChatRoom *cr){
|
||||
LinphoneCore *lc=linphone_chat_room_get_lc(cr);
|
||||
int numrows=0;
|
||||
|
||||
if (lc->db==NULL) return 0;
|
||||
|
||||
char *peer=linphone_address_as_string_uri_only(linphone_chat_room_get_peer_address(cr));
|
||||
char *buf=sqlite3_mprintf("SELECT count(*) FROM history WHERE remoteContact = %Q AND read = 0;",peer);
|
||||
sqlite3_stmt *selectStatement;
|
||||
int returnValue = sqlite3_prepare_v2(lc->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);
|
||||
ms_free(peer);
|
||||
return numrows;
|
||||
}
|
||||
|
||||
void linphone_chat_room_delete_message(LinphoneChatRoom *cr, LinphoneChatMessage *msg) {
|
||||
LinphoneCore *lc=cr->lc;
|
||||
|
||||
if (lc->db==NULL) return ;
|
||||
|
||||
char *buf=sqlite3_mprintf("DELETE FROM history WHERE id = %i;", msg->storage_id);
|
||||
linphone_sql_request(lc->db,buf);
|
||||
sqlite3_free(buf);
|
||||
}
|
||||
|
||||
void linphone_chat_room_delete_history(LinphoneChatRoom *cr){
|
||||
LinphoneCore *lc=cr->lc;
|
||||
|
||||
if (lc->db==NULL) return ;
|
||||
|
||||
char *peer=linphone_address_as_string_uri_only(linphone_chat_room_get_peer_address(cr));
|
||||
char *buf=sqlite3_mprintf("DELETE FROM history WHERE remoteContact = %Q;",peer);
|
||||
linphone_sql_request(lc->db,buf);
|
||||
sqlite3_free(buf);
|
||||
ms_free(peer);
|
||||
}
|
||||
|
||||
MSList *linphone_chat_room_get_history(LinphoneChatRoom *cr,int nb_message){
|
||||
LinphoneCore *lc=linphone_chat_room_get_lc(cr);
|
||||
MSList *ret;
|
||||
char *buf;
|
||||
char *peer;
|
||||
uint64_t begin,end;
|
||||
|
||||
if (lc->db==NULL) return NULL;
|
||||
peer=linphone_address_as_string_uri_only(linphone_chat_room_get_peer_address(cr));
|
||||
cr->messages_hist = NULL;
|
||||
if (nb_message > 0)
|
||||
buf=sqlite3_mprintf("SELECT * FROM history WHERE remoteContact = %Q ORDER BY id DESC LIMIT %i ;",peer,nb_message);
|
||||
else
|
||||
buf=sqlite3_mprintf("SELECT * FROM history WHERE remoteContact = %Q ORDER BY id DESC;",peer);
|
||||
begin=ortp_get_cur_time_ms();
|
||||
linphone_sql_request_message(lc->db,buf,cr);
|
||||
end=ortp_get_cur_time_ms();
|
||||
ms_message("linphone_chat_room_get_history(): completed in %i ms",(int)(end-begin));
|
||||
sqlite3_free(buf);
|
||||
ret=cr->messages_hist;
|
||||
cr->messages_hist=NULL;
|
||||
ms_free(peer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void linphone_close_storage(sqlite3* db){
|
||||
sqlite3_close(db);
|
||||
}
|
||||
|
||||
void linphone_create_table(sqlite3* db){
|
||||
char* errmsg=NULL;
|
||||
int ret;
|
||||
ret=sqlite3_exec(db,"CREATE TABLE IF NOT EXISTS history ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||
"localContact TEXT NOT NULL,"
|
||||
"remoteContact TEXT NOT NULL,"
|
||||
"direction INTEGER,"
|
||||
"message TEXT,"
|
||||
"time TEXT NOT NULL,"
|
||||
"read INTEGER,"
|
||||
"status INTEGER"
|
||||
");",
|
||||
0,0,&errmsg);
|
||||
if(ret != SQLITE_OK) {
|
||||
ms_error("Error in creation: %s.\n", errmsg);
|
||||
sqlite3_free(errmsg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static const char *days[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
|
||||
static const char *months[]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
|
||||
static time_t parse_time_from_db( const char* time ){
|
||||
/* messages used to be stored in the DB by using string-based time */
|
||||
struct tm ret={0};
|
||||
char tmp1[80]={0};
|
||||
char tmp2[80]={0};
|
||||
int i,j;
|
||||
time_t parsed = 0;
|
||||
|
||||
if( sscanf(time,"%3c %3c%d%d:%d:%d %d",tmp1,tmp2,&ret.tm_mday,
|
||||
&ret.tm_hour,&ret.tm_min,&ret.tm_sec,&ret.tm_year) == 7 ){
|
||||
ret.tm_year-=1900;
|
||||
for(i=0;i<7;i++) {
|
||||
if(strcmp(tmp1,days[i])==0) ret.tm_wday=i;
|
||||
}
|
||||
for(j=0;j<12;j++) {
|
||||
if(strcmp(tmp2,months[j])==0) ret.tm_mon=j;
|
||||
}
|
||||
ret.tm_isdst=-1;
|
||||
parsed = mktime(&ret);
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
|
||||
|
||||
static int migrate_messages(void* data,int argc, char** argv, char** column_names) {
|
||||
time_t new_time = parse_time_from_db(argv[1]);
|
||||
if( new_time ){
|
||||
/* replace 'time' by -1 and set 'utc' to the timestamp */
|
||||
char *buf = sqlite3_mprintf("UPDATE history SET utc=%i,time='-1' WHERE id=%i", new_time, atoi(argv[0]));
|
||||
if( buf) {
|
||||
linphone_sql_request((sqlite3*)data, buf);
|
||||
sqlite3_free(buf);
|
||||
}
|
||||
} else {
|
||||
ms_warning("Cannot parse time %s from id %s", argv[1], argv[0]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void linphone_migrate_timestamps(sqlite3* db){
|
||||
int ret;
|
||||
char* errmsg = NULL;
|
||||
uint64_t begin=ortp_get_cur_time_ms();
|
||||
|
||||
linphone_sql_request(db,"BEGIN TRANSACTION");
|
||||
|
||||
ret = sqlite3_exec(db,"SELECT id,time,direction FROM history WHERE time != '-1'", migrate_messages, db, &errmsg);
|
||||
if( ret != SQLITE_OK ){
|
||||
ms_warning("Error migrating outgoing messages: %s.\n", errmsg);
|
||||
sqlite3_free(errmsg);
|
||||
linphone_sql_request(db, "ROLLBACK");
|
||||
} else {
|
||||
linphone_sql_request(db, "COMMIT");
|
||||
uint64_t end=ortp_get_cur_time_ms();
|
||||
ms_message("Migrated message timestamps to UTC in %i ms",(int)(end-begin));
|
||||
}
|
||||
}
|
||||
|
||||
void linphone_update_table(sqlite3* db) {
|
||||
char* errmsg=NULL;
|
||||
int ret;
|
||||
|
||||
// for image url storage
|
||||
ret=sqlite3_exec(db,"ALTER TABLE history ADD COLUMN url TEXT;",NULL,NULL,&errmsg);
|
||||
if(ret != SQLITE_OK) {
|
||||
ms_message("Table already up to date: %s.", errmsg);
|
||||
sqlite3_free(errmsg);
|
||||
} else {
|
||||
ms_debug("Table updated successfully for URL.");
|
||||
}
|
||||
|
||||
// for UTC timestamp storage
|
||||
ret = sqlite3_exec(db, "ALTER TABLE history ADD COLUMN utc INTEGER;", NULL,NULL,&errmsg);
|
||||
if( ret != SQLITE_OK ){
|
||||
ms_message("Table already up to date: %s.", errmsg);
|
||||
sqlite3_free(errmsg);
|
||||
} else {
|
||||
ms_debug("Table updated successfully for UTC.");
|
||||
// migrate from old text-based timestamps to unix time-based timestamps
|
||||
linphone_migrate_timestamps(db);
|
||||
}
|
||||
}
|
||||
|
||||
void linphone_message_storage_init_chat_rooms(LinphoneCore *lc) {
|
||||
char *buf;
|
||||
|
||||
if (lc->db==NULL) return;
|
||||
buf=sqlite3_mprintf("SELECT remoteContact FROM history GROUP BY remoteContact;");
|
||||
linphone_sql_request_all(lc->db,buf,lc);
|
||||
sqlite3_free(buf);
|
||||
}
|
||||
|
||||
void linphone_core_message_storage_init(LinphoneCore *lc){
|
||||
int ret;
|
||||
const char *errmsg;
|
||||
sqlite3 *db;
|
||||
|
||||
linphone_core_message_storage_close(lc);
|
||||
|
||||
ret=sqlite3_open(lc->chat_db_file,&db);
|
||||
if(ret != SQLITE_OK) {
|
||||
errmsg=sqlite3_errmsg(db);
|
||||
ms_error("Error in the opening: %s.\n", errmsg);
|
||||
sqlite3_close(db);
|
||||
}
|
||||
linphone_create_table(db);
|
||||
linphone_update_table(db);
|
||||
lc->db=db;
|
||||
|
||||
// Create a chatroom for each contact in the chat history
|
||||
linphone_message_storage_init_chat_rooms(lc);
|
||||
}
|
||||
|
||||
void linphone_core_message_storage_close(LinphoneCore *lc){
|
||||
if (lc->db){
|
||||
sqlite3_close(lc->db);
|
||||
lc->db=NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
unsigned int linphone_chat_message_store(LinphoneChatMessage *cr){
|
||||
return 0;
|
||||
}
|
||||
|
||||
void linphone_chat_message_store_state(LinphoneChatMessage *cr){
|
||||
}
|
||||
|
||||
void linphone_chat_room_mark_as_read(LinphoneChatRoom *cr){
|
||||
}
|
||||
|
||||
MSList *linphone_chat_room_get_history(LinphoneChatRoom *cr,int nb_message){
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void linphone_chat_room_delete_message(LinphoneChatRoom *cr, LinphoneChatMessage *msg) {
|
||||
}
|
||||
|
||||
void linphone_chat_room_delete_history(LinphoneChatRoom *cr){
|
||||
}
|
||||
|
||||
void linphone_message_storage_init_chat_rooms(LinphoneCore *lc) {
|
||||
}
|
||||
|
||||
void linphone_core_message_storage_init(LinphoneCore *lc){
|
||||
}
|
||||
|
||||
void linphone_core_message_storage_close(LinphoneCore *lc){
|
||||
}
|
||||
|
||||
void linphone_chat_room_update_url(LinphoneChatRoom *cr, LinphoneChatMessage *msg) {
|
||||
}
|
||||
|
||||
int linphone_chat_room_get_unread_messages_count(LinphoneChatRoom *cr){
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue