2014-10-02 03:03:10 +08:00
/*
Copyright ( C ) 2014 by Project Tox < https : //tox.im>
This file is part of qTox , a Qt - based graphical interface for Tox .
This program is libre 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 3 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 COPYING file for more details .
*/
# include "historykeeper.h"
# include "misc/settings.h"
2014-10-10 23:45:58 +08:00
# include "core.h"
2014-10-02 03:03:10 +08:00
# include <QSqlError>
# include <QFile>
# include <QDir>
# include <QSqlQuery>
# include <QVariant>
# include <QDebug>
2014-10-10 23:45:58 +08:00
# include <QTemporaryFile>
2014-10-02 03:03:10 +08:00
2014-10-15 22:46:36 +08:00
# include "misc/db/plaindb.h"
# include "misc/db/encrypteddb.h"
2014-10-02 03:03:10 +08:00
static HistoryKeeper * historyInstance = nullptr ;
HistoryKeeper * HistoryKeeper : : getInstance ( )
{
if ( historyInstance = = nullptr )
{
2014-10-19 16:48:10 +08:00
QList < QString > initLst ;
initLst . push_back ( QString ( " CREATE TABLE IF NOT EXISTS history (id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp INTEGER NOT NULL, " ) +
2014-11-09 15:13:17 +08:00
QString ( " chat_id INTEGER NOT NULL, sender INTEGER NOT NULL, sent_ok BOOLEAN NOT NULL DEFAULT TRUE, message TEXT NOT NULL); " ) ) ;
2014-10-19 16:48:10 +08:00
initLst . push_back ( QString ( " CREATE TABLE IF NOT EXISTS aliases (id INTEGER PRIMARY KEY AUTOINCREMENT, user_id TEXT UNIQUE NOT NULL); " ) ) ;
initLst . push_back ( QString ( " CREATE TABLE IF NOT EXISTS chats (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE NOT NULL, ctype INTEGER NOT NULL); " ) ) ;
2014-10-02 03:03:10 +08:00
QString path ( " :memory: " ) ;
2014-10-15 22:46:36 +08:00
GenericDdInterface * dbIntf ;
2014-10-02 03:03:10 +08:00
if ( Settings : : getInstance ( ) . getEnableLogging ( ) )
{
2014-10-15 22:46:36 +08:00
bool encrypted = Settings : : getInstance ( ) . getEncryptLogs ( ) ;
if ( encrypted )
{
2014-10-19 01:38:47 +08:00
path = getHistoryPath ( ) ;
2014-10-19 16:48:10 +08:00
dbIntf = new EncryptedDb ( path , initLst ) ;
2014-10-15 22:46:36 +08:00
historyInstance = new HistoryKeeper ( dbIntf ) ;
return historyInstance ;
} else {
2014-10-19 01:38:47 +08:00
path = getHistoryPath ( ) ;
2014-10-15 22:46:36 +08:00
}
2014-10-02 03:03:10 +08:00
}
2014-10-19 16:48:10 +08:00
dbIntf = new PlainDb ( path , initLst ) ;
2014-10-15 22:46:36 +08:00
historyInstance = new HistoryKeeper ( dbIntf ) ;
2014-10-02 03:03:10 +08:00
}
return historyInstance ;
}
2014-10-19 01:38:47 +08:00
bool HistoryKeeper : : checkPassword ( )
{
if ( Settings : : getInstance ( ) . getEnableLogging ( ) )
{
if ( Settings : : getInstance ( ) . getEncryptLogs ( ) )
{
2014-10-19 01:52:43 +08:00
QString dbpath = getHistoryPath ( ) ;
2014-10-19 01:38:47 +08:00
return EncryptedDb : : check ( dbpath ) ;
} else {
return true ;
}
} else {
return true ;
}
}
2014-10-15 22:46:36 +08:00
HistoryKeeper : : HistoryKeeper ( GenericDdInterface * db_ ) :
db ( db_ )
2014-10-02 03:03:10 +08:00
{
2014-10-11 17:53:25 +08:00
/*
DB format
chats :
* name - > id map
id - - auto - incrementing number
name - - chat ' s name ( for user to user conversation it is opposite user public key )
ctype - - chat type , reserved for group chats
alisases :
* user_id - > id map
id - - auto - incrementing number
name - - user ' s public key
history :
id - - auto - incrementing number
timestamp
chat_id - - current chat ID ( resolves from chats table )
sender - - sender ' s ID ( resolves from aliases table )
message
*/
2014-10-02 03:03:10 +08:00
updateChatsID ( ) ;
updateAliases ( ) ;
}
HistoryKeeper : : ~ HistoryKeeper ( )
{
2014-10-15 22:46:36 +08:00
delete db ;
2014-10-02 03:03:10 +08:00
}
2014-10-14 22:36:17 +08:00
void HistoryKeeper : : addChatEntry ( const QString & chat , const QString & message , const QString & sender , const QDateTime & dt )
2014-10-02 03:03:10 +08:00
{
int chat_id = getChatID ( chat , ctSingle ) . first ;
int sender_id = getAliasID ( sender ) ;
2014-10-22 22:14:42 +08:00
db - > exec ( QString ( " INSERT INTO history (timestamp, chat_id, sender, message) " ) +
QString ( " VALUES (%1, %2, %3, '%4'); " )
. arg ( dt . toMSecsSinceEpoch ( ) ) . arg ( chat_id ) . arg ( sender_id ) . arg ( wrapMessage ( message ) ) ) ;
2014-10-02 03:03:10 +08:00
}
2014-10-22 22:14:42 +08:00
QList < HistoryKeeper : : HistMessage > HistoryKeeper : : getChatHistory ( HistoryKeeper : : ChatType ct , const QString & chat ,
const QDateTime & time_from , const QDateTime & time_to )
2014-10-02 03:03:10 +08:00
{
QList < HistMessage > res ;
2014-10-14 13:49:27 +08:00
qint64 time64_from = time_from . toMSecsSinceEpoch ( ) ;
qint64 time64_to = time_to . toMSecsSinceEpoch ( ) ;
2014-10-02 03:03:10 +08:00
int chat_id = getChatID ( chat , ct ) . first ;
QSqlQuery dbAnswer ;
if ( ct = = ctSingle )
{
2014-10-15 22:46:36 +08:00
dbAnswer = db - > exec ( QString ( " SELECT timestamp, user_id, message FROM history INNER JOIN aliases ON history.sender = aliases.id " ) +
2014-10-22 22:14:42 +08:00
QString ( " AND timestamp BETWEEN %1 AND %2 AND chat_id = %3; " )
. arg ( time64_from ) . arg ( time64_to ) . arg ( chat_id ) ) ;
2014-10-02 03:03:10 +08:00
} else {
2014-10-10 23:45:58 +08:00
// no groupchats yet
2014-10-02 03:03:10 +08:00
}
while ( dbAnswer . next ( ) )
{
QString sender = dbAnswer . value ( 1 ) . toString ( ) ;
QString message = unWrapMessage ( dbAnswer . value ( 2 ) . toString ( ) ) ;
2014-10-14 13:49:27 +08:00
qint64 timeInt = dbAnswer . value ( 0 ) . toLongLong ( ) ;
QDateTime time = QDateTime : : fromMSecsSinceEpoch ( timeInt ) ;
2014-10-02 03:03:10 +08:00
2014-10-14 22:36:17 +08:00
res . push_back ( { sender , message , time } ) ;
2014-10-02 03:03:10 +08:00
}
return res ;
}
QString HistoryKeeper : : wrapMessage ( const QString & str )
{
QString wrappedMessage ( str ) ;
wrappedMessage . replace ( " ' " , " '' " ) ;
return wrappedMessage ;
}
QString HistoryKeeper : : unWrapMessage ( const QString & str )
{
QString unWrappedMessage ( str ) ;
unWrappedMessage . replace ( " '' " , " ' " ) ;
return unWrappedMessage ;
}
void HistoryKeeper : : updateChatsID ( )
{
2014-10-15 22:46:36 +08:00
auto dbAnswer = db - > exec ( QString ( " SELECT * FROM chats; " ) ) ;
2014-10-02 03:03:10 +08:00
chats . clear ( ) ;
while ( dbAnswer . next ( ) )
{
QString name = dbAnswer . value ( 1 ) . toString ( ) ;
int id = dbAnswer . value ( 0 ) . toInt ( ) ;
2014-10-14 21:43:03 +08:00
ChatType ctype = convertToChatType ( dbAnswer . value ( 2 ) . toInt ( ) ) ;
2014-10-02 03:03:10 +08:00
chats [ name ] = { id , ctype } ;
}
}
void HistoryKeeper : : updateAliases ( )
{
2014-10-15 22:46:36 +08:00
auto dbAnswer = db - > exec ( QString ( " SELECT * FROM aliases; " ) ) ;
2014-10-02 03:03:10 +08:00
aliases . clear ( ) ;
while ( dbAnswer . next ( ) )
{
QString user_id = dbAnswer . value ( 1 ) . toString ( ) ;
int id = dbAnswer . value ( 0 ) . toInt ( ) ;
aliases [ user_id ] = id ;
}
}
QPair < int , HistoryKeeper : : ChatType > HistoryKeeper : : getChatID ( const QString & id_str , ChatType ct )
{
auto it = chats . find ( id_str ) ;
if ( it ! = chats . end ( ) )
return it . value ( ) ;
2014-10-15 22:46:36 +08:00
db - > exec ( QString ( " INSERT INTO chats (name, ctype) VALUES ('%1', '%2'); " ) . arg ( id_str ) . arg ( ct ) ) ;
2014-10-02 03:03:10 +08:00
updateChatsID ( ) ;
return getChatID ( id_str , ct ) ;
}
int HistoryKeeper : : getAliasID ( const QString & id_str )
{
auto it = aliases . find ( id_str ) ;
if ( it ! = aliases . end ( ) )
return it . value ( ) ;
2014-10-15 22:46:36 +08:00
db - > exec ( QString ( " INSERT INTO aliases (user_id) VALUES ('%1'); " ) . arg ( id_str ) ) ;
2014-10-02 03:03:10 +08:00
updateAliases ( ) ;
return getAliasID ( id_str ) ;
}
void HistoryKeeper : : resetInstance ( )
{
if ( historyInstance = = nullptr )
return ;
delete historyInstance ;
historyInstance = nullptr ;
}
2014-10-14 13:49:27 +08:00
void HistoryKeeper : : addGroupChatEntry ( const QString & chat , const QString & message , const QString & sender , const QDateTime & dt )
2014-10-02 03:03:10 +08:00
{
Q_UNUSED ( chat )
Q_UNUSED ( message )
Q_UNUSED ( sender )
2014-10-14 13:49:27 +08:00
Q_UNUSED ( dt )
2014-10-10 23:45:58 +08:00
// no groupchats yet
}
2014-10-14 21:43:03 +08:00
HistoryKeeper : : ChatType HistoryKeeper : : convertToChatType ( int ct )
{
if ( ct < 0 | | ct > 1 )
return ctSingle ;
return static_cast < ChatType > ( ct ) ;
}
2014-10-19 01:38:47 +08:00
QString HistoryKeeper : : getHistoryPath ( )
{
QDir baseDir ( Settings : : getInstance ( ) . getSettingsDirPath ( ) ) ;
QString currentProfile = Settings : : getInstance ( ) . getCurrentProfile ( ) ;
if ( Settings : : getInstance ( ) . getEncryptLogs ( ) )
return baseDir . filePath ( currentProfile + " .qtox_history.encrypted " ) ;
else
return baseDir . filePath ( currentProfile + " .qtox_history " ) ;
}
void HistoryKeeper : : renameHistory ( QString from , QString to )
{
resetInstance ( ) ;
QFile fileEnc ( QDir ( Settings : : getInstance ( ) . getSettingsDirPath ( ) ) . filePath ( from + " .qtox_history.encrypted " ) ) ;
if ( fileEnc . exists ( ) )
fileEnc . rename ( QDir ( Settings : : getInstance ( ) . getSettingsDirPath ( ) ) . filePath ( to + " .qtox_history.encrypted " ) ) ;
QFile filePlain ( QDir ( Settings : : getInstance ( ) . getSettingsDirPath ( ) ) . filePath ( from + " .qtox_history " ) ) ;
if ( filePlain . exists ( ) )
filePlain . rename ( QDir ( Settings : : getInstance ( ) . getSettingsDirPath ( ) ) . filePath ( to + " .qtox_history " ) ) ;
}