mirror of
https://github.com/qTox/qTox.git
synced 2024-03-22 14:00:36 +08:00
Merge branch 'pr423'
This commit is contained in:
commit
f1601f389f
|
@ -10,7 +10,7 @@ INSTALL_DIR=libs
|
|||
# just for convenience
|
||||
BASE_DIR=${SCRIPT_DIR}/${INSTALL_DIR}
|
||||
|
||||
SODIUM_VER=0.7.0
|
||||
SODIUM_VER=1.0.0
|
||||
|
||||
# directory names of cloned repositories
|
||||
SODIUM_DIR=libsodium-$SODIUM_VER
|
||||
|
|
35
qtox.pro
35
qtox.pro
|
@ -20,7 +20,7 @@
|
|||
# See the COPYING file for more details.
|
||||
|
||||
|
||||
QT += core gui network xml opengl
|
||||
QT += core gui network xml opengl sql
|
||||
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||
|
||||
TARGET = qtox
|
||||
|
@ -29,7 +29,12 @@ FORMS += \
|
|||
src/mainwindow.ui \
|
||||
src/widget/form/settings/generalsettings.ui \
|
||||
src/widget/form/settings/avsettings.ui \
|
||||
src/widget/form/settings/identitysettings.ui
|
||||
src/widget/form/settings/identitysettings.ui \
|
||||
src/widget/form/settings/privacysettings.ui \
|
||||
src/widget/form/loadhistorydialog.ui \
|
||||
src/widget/form/inputpassworddialog.ui \
|
||||
src/widget/form/setpassworddialog.ui
|
||||
|
||||
CONFIG += c++11
|
||||
|
||||
TRANSLATIONS = translations/de.ts \
|
||||
|
@ -55,28 +60,28 @@ contains(JENKINS,YES) {
|
|||
|
||||
# Rules for Windows, Mac OSX, and Linux
|
||||
win32 {
|
||||
LIBS += -liphlpapi -L$$PWD/libs/lib -ltoxav -ltoxcore -lvpx -lpthread
|
||||
LIBS += -liphlpapi -L$$PWD/libs/lib -ltoxav -ltoxcore -ltoxencryptsave -lvpx -lpthread
|
||||
LIBS += -L$$PWD/libs/lib -lopencv_core248 -lopencv_highgui248 -lopencv_imgproc248 -lOpenAL32 -lopus
|
||||
LIBS += -lz -lopengl32 -lole32 -loleaut32 -luuid -lvfw32 -ljpeg -ltiff -lpng -ljasper -lIlmImf -lHalf -lws2_32
|
||||
} else {
|
||||
macx {
|
||||
ICON = img/icons/qtox.icns
|
||||
LIBS += -L$$PWD/libs/lib/ -ltoxcore -ltoxav -lsodium -lvpx -framework OpenAL -lopencv_core -lopencv_highgui
|
||||
ICON = img/icons/qtox.icns
|
||||
LIBS += -L$$PWD/libs/lib/ -ltoxcore -ltoxav -ltoxencryptsave -lsodium -lvpx -framework OpenAL -lopencv_core -lopencv_highgui
|
||||
} else {
|
||||
# If we're building a package, static link libtox[core,av] and libsodium, since they are not provided by any package
|
||||
contains(STATICPKG, YES) {
|
||||
target.path = /usr/bin
|
||||
INSTALLS += target
|
||||
LIBS += -L$$PWD/libs/lib/ -lopus -lvpx -lopenal -Wl,-Bstatic -ltoxcore -ltoxav -lsodium -lopencv_highgui -lopencv_imgproc -lopencv_core -lz -Wl,-Bdynamic
|
||||
LIBS += -L$$PWD/libs/lib/ -lopus -lvpx -lopenal -Wl,-Bstatic -ltoxcore -ltoxav -ltoxencryptsave -lsodium -lopencv_highgui -lopencv_imgproc -lopencv_core -lz -Wl,-Bdynamic
|
||||
LIBS += -Wl,-Bstatic -ljpeg -ltiff -lpng -ljasper -lIlmImf -lIlmThread -lIex -ldc1394 -lraw1394 -lHalf -lz -llzma -ljbig
|
||||
LIBS += -Wl,-Bdynamic -lv4l1 -lv4l2 -lavformat -lavcodec -lavutil -lswscale -lusb-1.0
|
||||
|
||||
} else {
|
||||
LIBS += -L$$PWD/libs/lib/ -ltoxcore -ltoxav -lvpx -lopenal -lopencv_core -lopencv_highgui -lopencv_imgproc
|
||||
LIBS += -L$$PWD/libs/lib/ -ltoxcore -ltoxav -ltoxencryptsave -lvpx -lopenal -lopencv_core -lopencv_highgui -lopencv_imgproc
|
||||
}
|
||||
|
||||
contains(JENKINS, YES) {
|
||||
LIBS = ./libs/lib/libtoxav.a ./libs/lib/libvpx.a ./libs/lib/libopus.a ./libs/lib/libtoxcore.a ./libs/lib/libsodium.a -lopencv_core -lopencv_highgui -lopenal
|
||||
LIBS = ./libs/lib/libsodium.a ./libs/lib/libtoxcore.a ./libs/lib/libtoxav.a ./libs/lib/libtoxencryptsave.a ./libs/lib/libvpx.a ./libs/lib/libopus.a -lopencv_core -lopencv_highgui -lopenal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -140,6 +145,13 @@ HEADERS += src/widget/form/addfriendform.h \
|
|||
src/videosource.h \
|
||||
src/cameraworker.h \
|
||||
src/widget/videosurface.h \
|
||||
src/widget/form/loadhistorydialog.h \
|
||||
src/historykeeper.h \
|
||||
src/misc/db/genericddinterface.h \
|
||||
src/misc/db/plaindb.h \
|
||||
src/misc/db/encrypteddb.h \
|
||||
src/widget/form/inputpassworddialog.h \
|
||||
src/widget/form/setpassworddialog.h \
|
||||
src/widget/form/tabcompleter.h
|
||||
|
||||
SOURCES += \
|
||||
|
@ -189,5 +201,12 @@ SOURCES += \
|
|||
src/widget/maskablepixmapwidget.cpp \
|
||||
src/cameraworker.cpp \
|
||||
src/widget/videosurface.cpp \
|
||||
src/widget/form/loadhistorydialog.cpp \
|
||||
src/historykeeper.cpp \
|
||||
src/misc/db/genericddinterface.cpp \
|
||||
src/misc/db/plaindb.cpp \
|
||||
src/misc/db/encrypteddb.cpp \
|
||||
src/widget/form/inputpassworddialog.cpp \
|
||||
src/widget/form/setpassworddialog.cpp \
|
||||
src/netvideosource.cpp \
|
||||
src/widget/form/tabcompleter.cpp
|
||||
|
|
219
src/core.cpp
219
src/core.cpp
|
@ -19,8 +19,10 @@
|
|||
#include "misc/cstring.h"
|
||||
#include "misc/settings.h"
|
||||
#include "widget/widget.h"
|
||||
#include "historykeeper.h"
|
||||
|
||||
#include <tox/tox.h>
|
||||
#include <tox/toxencryptsave.h>
|
||||
|
||||
#include <ctime>
|
||||
#include <functional>
|
||||
|
@ -51,6 +53,9 @@ Core::Core(Camera* cam, QThread *coreThread, QString loadPath) :
|
|||
videobuf = new uint8_t[videobufsize];
|
||||
videoBusyness=0;
|
||||
|
||||
for (int i = 0; i < ptCounter; i++)
|
||||
pwsaltedkey[i] = nullptr;
|
||||
|
||||
toxTimer = new QTimer(this);
|
||||
toxTimer->setSingleShot(true);
|
||||
connect(toxTimer, &QTimer::timeout, this, &Core::process);
|
||||
|
@ -113,6 +118,9 @@ Core::~Core()
|
|||
alcCloseDevice(alOutDev);
|
||||
if (alInDev)
|
||||
alcCaptureCloseDevice(alInDev);
|
||||
|
||||
clearPassword(Core::ptMain);
|
||||
clearPassword(Core::ptHistory);
|
||||
}
|
||||
|
||||
Core* Core::getInstance()
|
||||
|
@ -1092,28 +1100,104 @@ bool Core::loadConfiguration(QString path)
|
|||
return true;
|
||||
}
|
||||
|
||||
if (Settings::getInstance().getEncryptTox())
|
||||
{
|
||||
while (!isPasswordSet(ptMain))
|
||||
{
|
||||
emit blockingGetPassword(tr("Tox datafile decryption password"), ptMain);
|
||||
if (!isPasswordSet(ptMain))
|
||||
QMessageBox::warning(nullptr, tr("Password error"), tr("Failed to setup password.\nEmpty password."));
|
||||
}
|
||||
}
|
||||
|
||||
qint64 fileSize = configurationFile.size();
|
||||
if (fileSize > 0) {
|
||||
QByteArray data = configurationFile.readAll();
|
||||
int error = tox_load(tox, reinterpret_cast<uint8_t *>(data.data()), data.size());
|
||||
if (error < 0)
|
||||
{
|
||||
qWarning() << "Core: tox_load failed with error "<<error;
|
||||
qWarning() << "Core: tox_load failed with error "<< error;
|
||||
}
|
||||
else if (error == 1) // Encrypted data save
|
||||
{
|
||||
qWarning() << "Core: Can not open encrypted tox save";
|
||||
if (QMessageBox::Ok != QMessageBox::warning(nullptr, tr("Encrypted profile"),
|
||||
tr("Your tox profile seems to be encrypted, qTox can't open it\nDo you want to erase this profile ?"),
|
||||
QMessageBox::Ok | QMessageBox::Cancel))
|
||||
do
|
||||
{
|
||||
qWarning() << "Core: Couldn't open encrypted save, giving up";
|
||||
configurationFile.close();
|
||||
return false;
|
||||
}
|
||||
while (!isPasswordSet(ptMain))
|
||||
{
|
||||
emit blockingGetPassword(tr("Tox datafile decryption password"), ptMain);
|
||||
if (!isPasswordSet(ptMain))
|
||||
QMessageBox::warning(nullptr, tr("Password error"), tr("Failed to setup password.\nEmpty password."));
|
||||
}
|
||||
|
||||
error = tox_encrypted_load(tox, reinterpret_cast<uint8_t *>(data.data()), data.size(),
|
||||
reinterpret_cast<uint8_t *>(barePassword[ptMain].data()), barePassword[ptMain].size());
|
||||
if (error != 0)
|
||||
{
|
||||
QMessageBox msgb;
|
||||
QPushButton *tryAgain = msgb.addButton(tr("Try Again"), QMessageBox::AcceptRole);
|
||||
QPushButton *cancel = msgb.addButton(tr("Change profile"), QMessageBox::RejectRole);
|
||||
QPushButton *wipe = msgb.addButton(tr("Reinit current profile"), QMessageBox::ActionRole);
|
||||
msgb.setDefaultButton(tryAgain);
|
||||
msgb.setWindowTitle(tr("Password error"));
|
||||
msgb.setText(tr("Wrong password has been entered"));
|
||||
// msgb.setInformativeText(tr(""));
|
||||
|
||||
msgb.exec();
|
||||
|
||||
if (msgb.clickedButton() == tryAgain)
|
||||
{
|
||||
clearPassword(ptMain);
|
||||
} else if (msgb.clickedButton() == cancel)
|
||||
{
|
||||
configurationFile.close();
|
||||
return false;
|
||||
} else if (msgb.clickedButton() == wipe)
|
||||
{
|
||||
clearPassword(ptMain);
|
||||
Settings::getInstance().setEncryptTox(false);
|
||||
error = 0;
|
||||
}
|
||||
} else {
|
||||
Settings::getInstance().setEncryptTox(true);
|
||||
}
|
||||
} while (error != 0);
|
||||
}
|
||||
}
|
||||
|
||||
// tox core is already decrypted
|
||||
if (Settings::getInstance().getEnableLogging() && Settings::getInstance().getEncryptLogs())
|
||||
{
|
||||
bool error = true;
|
||||
do
|
||||
{
|
||||
while (!isPasswordSet(ptHistory))
|
||||
{
|
||||
emit blockingGetPassword(tr("History Log decpytion password"), Core::ptHistory);
|
||||
if (!isPasswordSet(ptHistory))
|
||||
QMessageBox::warning(nullptr, tr("Password error"), tr("Failed to setup password.\nEmpty password."));
|
||||
}
|
||||
|
||||
if (!HistoryKeeper::checkPassword())
|
||||
{
|
||||
if (QMessageBox::Ok == QMessageBox::warning(nullptr, tr("Encrypted log"),
|
||||
tr("Your history encrypted with different password\nDo you want to try another password?"),
|
||||
QMessageBox::Ok | QMessageBox::Cancel))
|
||||
{
|
||||
error = true;
|
||||
clearPassword(ptHistory);
|
||||
} else {
|
||||
error = false;
|
||||
clearPassword(ptHistory);
|
||||
QMessageBox::warning(nullptr, tr("Loggin"), tr("Due to incorret password logging will be disabled"));
|
||||
Settings::getInstance().setEncryptLogs(false);
|
||||
Settings::getInstance().setEnableLogging(false);
|
||||
}
|
||||
} else {
|
||||
error = false;
|
||||
}
|
||||
} while (error);
|
||||
}
|
||||
|
||||
configurationFile.close();
|
||||
|
||||
// set GUI with user and statusmsg
|
||||
|
@ -1143,22 +1227,18 @@ void Core::saveConfiguration()
|
|||
}
|
||||
|
||||
QString profile = Settings::getInstance().getCurrentProfile();
|
||||
//qDebug() << "saveConf read profile: " << profile;
|
||||
|
||||
if (profile == "")
|
||||
{ // no profile active; this should only happen on startup, if at all
|
||||
profile = sanitize(getUsername());
|
||||
|
||||
if (profile == "") // happens on creation of a new Tox ID
|
||||
profile = getIDString();
|
||||
//qDebug() << "saveConf: read sanitized user as " << profile;
|
||||
|
||||
Settings::getInstance().setCurrentProfile(profile);
|
||||
}
|
||||
|
||||
QString path = dir + QDir::separator() + profile + TOX_EXT;
|
||||
QFileInfo info(path);
|
||||
// if (!info.exists()) // fall back to old school 'data'
|
||||
// { //path = dir + QDir::separator() + CONFIG_FILE_NAME;
|
||||
// qDebug() << "Core:" << path << " does not exist";
|
||||
// }
|
||||
QString path = directory.filePath(profile + TOX_EXT);
|
||||
|
||||
saveConfiguration(path);
|
||||
}
|
||||
|
@ -1180,10 +1260,36 @@ void Core::saveConfiguration(const QString& path)
|
|||
}
|
||||
|
||||
qDebug() << "Core: writing tox_save to " << path;
|
||||
uint32_t fileSize = tox_size(tox);
|
||||
|
||||
uint32_t fileSize;
|
||||
if (Settings::getInstance().getEncryptTox())
|
||||
fileSize = tox_encrypted_size(tox);
|
||||
else
|
||||
fileSize = tox_size(tox);
|
||||
|
||||
if (fileSize > 0 && fileSize <= INT32_MAX) {
|
||||
uint8_t *data = new uint8_t[fileSize];
|
||||
tox_save(tox, data);
|
||||
|
||||
if (Settings::getInstance().getEncryptTox())
|
||||
{
|
||||
if (!isPasswordSet(ptMain))
|
||||
{
|
||||
// probably zero chance event
|
||||
QMessageBox::warning(nullptr, tr("NO Password"), tr("Will be saved without encryption!"));
|
||||
tox_save(tox, data);
|
||||
} else {
|
||||
int ret = tox_encrypted_save(tox, data, reinterpret_cast<uint8_t *>(barePassword[ptMain].data()),
|
||||
barePassword[ptMain].size());
|
||||
if (ret == -1)
|
||||
{
|
||||
qCritical() << "Core::saveConfiguration: encryption of save file failed!!!";
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
tox_save(tox, data);
|
||||
}
|
||||
|
||||
configurationFile.write(reinterpret_cast<char *>(data), fileSize);
|
||||
configurationFile.commit();
|
||||
delete[] data;
|
||||
|
@ -1196,8 +1302,10 @@ void Core::switchConfiguration(const QString& profile)
|
|||
qDebug() << "Core: creating new Id";
|
||||
else
|
||||
qDebug() << "Core: switching from" << Settings::getInstance().getCurrentProfile() << "to" << profile;
|
||||
saveConfiguration();
|
||||
|
||||
saveConfiguration();
|
||||
clearPassword(ptMain);
|
||||
clearPassword(ptHistory);
|
||||
toxTimer->stop();
|
||||
|
||||
if (tox) {
|
||||
|
@ -1214,7 +1322,8 @@ void Core::switchConfiguration(const QString& profile)
|
|||
else
|
||||
loadPath = QDir(Settings::getSettingsDirPath()).filePath(profile + TOX_EXT);
|
||||
Settings::getInstance().setCurrentProfile(profile);
|
||||
|
||||
HistoryKeeper::getInstance()->resetInstance();
|
||||
|
||||
start();
|
||||
}
|
||||
|
||||
|
@ -1494,3 +1603,71 @@ QList<CString> Core::splitMessage(const QString &message)
|
|||
|
||||
return splittedMsgs;
|
||||
}
|
||||
|
||||
void Core::setPassword(QString& password, PasswordType passtype)
|
||||
{
|
||||
if (password.isEmpty())
|
||||
{
|
||||
clearPassword(passtype);
|
||||
return;
|
||||
}
|
||||
if (!pwsaltedkey[passtype])
|
||||
pwsaltedkey[passtype] = new uint8_t[tox_pass_key_length()];
|
||||
|
||||
CString str(password);
|
||||
tox_derive_key_from_pass(str.data(), str.size(), pwsaltedkey[passtype]);
|
||||
|
||||
barePassword[passtype].clear();
|
||||
barePassword[passtype].append(password);
|
||||
|
||||
password.clear();
|
||||
}
|
||||
|
||||
void Core::clearPassword(PasswordType passtype)
|
||||
{
|
||||
if (pwsaltedkey[passtype])
|
||||
{
|
||||
delete[] pwsaltedkey[passtype];
|
||||
pwsaltedkey[passtype] = nullptr;
|
||||
barePassword[passtype].clear();
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray Core::encryptData(const QByteArray& data, PasswordType passtype)
|
||||
{
|
||||
if (!pwsaltedkey[passtype])
|
||||
return QByteArray();
|
||||
uint8_t encrypted[data.size() + tox_pass_encryption_extra_length()];
|
||||
// if (tox_pass_key_encrypt(reinterpret_cast<const uint8_t*>(data.data()), data.size(), pwsaltedkey[passtype], encrypted) == -1)
|
||||
if (tox_pass_encrypt(reinterpret_cast<const uint8_t*>(data.data()), data.size(),
|
||||
reinterpret_cast<uint8_t*>(barePassword[passtype].data()), barePassword[passtype].size(), encrypted) == -1)
|
||||
{
|
||||
qWarning() << "Core::encryptData: encryption failed";
|
||||
return QByteArray();
|
||||
}
|
||||
return QByteArray(reinterpret_cast<char*>(encrypted), data.size() + tox_pass_encryption_extra_length());
|
||||
}
|
||||
|
||||
QByteArray Core::decryptData(const QByteArray& data, PasswordType passtype)
|
||||
{
|
||||
if (!pwsaltedkey[passtype])
|
||||
return QByteArray();
|
||||
int sz = data.size() - tox_pass_encryption_extra_length();
|
||||
uint8_t decrypted[sz];
|
||||
// if (tox_pass_key_decrypt(reinterpret_cast<const uint8_t*>(data.data()), data.size(), pwsaltedkey[passtype], decrypted) != sz)
|
||||
if (tox_pass_decrypt(reinterpret_cast<const uint8_t*>(data.data()), data.size(),
|
||||
reinterpret_cast<uint8_t*>(barePassword[passtype].data()), barePassword[passtype].size(), decrypted) != sz)
|
||||
{
|
||||
qWarning() << "Core::decryptData: decryption failed";
|
||||
return QByteArray();
|
||||
}
|
||||
return QByteArray(reinterpret_cast<char*>(decrypted), sz);
|
||||
}
|
||||
|
||||
bool Core::isPasswordSet(PasswordType passtype)
|
||||
{
|
||||
if (pwsaltedkey[passtype])
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
12
src/core.h
12
src/core.h
|
@ -35,6 +35,8 @@ class Core : public QObject
|
|||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum PasswordType {ptMain = 0, ptHistory, ptCounter};
|
||||
|
||||
explicit Core(Camera* cam, QThread* coreThread, QString initialLoadPath);
|
||||
static Core* getInstance(); ///< Returns the global widget's Core instance
|
||||
~Core();
|
||||
|
@ -66,6 +68,7 @@ public:
|
|||
void decreaseVideoBusyness();
|
||||
|
||||
bool anyActiveCalls();
|
||||
bool isPasswordSet(PasswordType passtype);
|
||||
|
||||
public slots:
|
||||
void start();
|
||||
|
@ -106,10 +109,16 @@ public slots:
|
|||
|
||||
void micMuteToggle(int callId);
|
||||
|
||||
void setPassword(QString& password, PasswordType passtype);
|
||||
void clearPassword(PasswordType passtype);
|
||||
QByteArray encryptData(const QByteArray& data, PasswordType passtype);
|
||||
QByteArray decryptData(const QByteArray& data, PasswordType passtype);
|
||||
|
||||
signals:
|
||||
void connected();
|
||||
void disconnected();
|
||||
void blockingClearContacts();
|
||||
void blockingGetPassword(QString info, int passtype);
|
||||
|
||||
void friendRequestReceived(const QString& userId, const QString& message);
|
||||
void friendMessageReceived(int friendId, const QString& message, bool isAction);
|
||||
|
@ -246,6 +255,9 @@ private:
|
|||
static QList<ToxFile> fileSendQueue, fileRecvQueue;
|
||||
static ToxCall calls[];
|
||||
|
||||
uint8_t* pwsaltedkey[PasswordType::ptCounter]; // use the pw's hash as the "pw"
|
||||
QByteArray barePassword[PasswordType::ptCounter]; // to be deleted after tox_pass_key_decrypt/tox_pass_key_encrypt fix
|
||||
|
||||
static const int videobufsize;
|
||||
static uint8_t* videobuf;
|
||||
static int videoBusyness; // Used to know when to drop frames
|
||||
|
|
277
src/historykeeper.cpp
Normal file
277
src/historykeeper.cpp
Normal file
|
@ -0,0 +1,277 @@
|
|||
/*
|
||||
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"
|
||||
#include "core.h"
|
||||
|
||||
#include <QSqlError>
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
#include <QSqlQuery>
|
||||
#include <QVariant>
|
||||
#include <QDebug>
|
||||
#include <QTemporaryFile>
|
||||
|
||||
#include "misc/db/plaindb.h"
|
||||
#include "misc/db/encrypteddb.h"
|
||||
|
||||
static HistoryKeeper *historyInstance = nullptr;
|
||||
|
||||
HistoryKeeper *HistoryKeeper::getInstance()
|
||||
{
|
||||
if (historyInstance == nullptr)
|
||||
{
|
||||
QList<QString> initLst;
|
||||
initLst.push_back(QString("CREATE TABLE IF NOT EXISTS history (id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp INTEGER NOT NULL, ") +
|
||||
QString("chat_id INTEGER NOT NULL, sender INTERGER NOT NULL, message TEXT NOT NULL);"));
|
||||
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);"));
|
||||
|
||||
QString path(":memory:");
|
||||
GenericDdInterface *dbIntf;
|
||||
|
||||
if (Settings::getInstance().getEnableLogging())
|
||||
{
|
||||
bool encrypted = Settings::getInstance().getEncryptLogs();
|
||||
|
||||
if (encrypted)
|
||||
{
|
||||
path = getHistoryPath();
|
||||
dbIntf = new EncryptedDb(path, initLst);
|
||||
|
||||
historyInstance = new HistoryKeeper(dbIntf);
|
||||
return historyInstance;
|
||||
} else {
|
||||
path = getHistoryPath();
|
||||
}
|
||||
}
|
||||
|
||||
dbIntf = new PlainDb(path, initLst);
|
||||
historyInstance = new HistoryKeeper(dbIntf);
|
||||
}
|
||||
|
||||
return historyInstance;
|
||||
}
|
||||
|
||||
bool HistoryKeeper::checkPassword()
|
||||
{
|
||||
if (Settings::getInstance().getEnableLogging())
|
||||
{
|
||||
if (Settings::getInstance().getEncryptLogs())
|
||||
{
|
||||
QString dbpath = getHistoryPath();
|
||||
return EncryptedDb::check(dbpath);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
HistoryKeeper::HistoryKeeper(GenericDdInterface *db_) :
|
||||
db(db_)
|
||||
{
|
||||
/*
|
||||
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
|
||||
*/
|
||||
|
||||
updateChatsID();
|
||||
updateAliases();
|
||||
}
|
||||
|
||||
HistoryKeeper::~HistoryKeeper()
|
||||
{
|
||||
delete db;
|
||||
}
|
||||
|
||||
void HistoryKeeper::addChatEntry(const QString& chat, const QString& message, const QString& sender, const QDateTime &dt)
|
||||
{
|
||||
int chat_id = getChatID(chat, ctSingle).first;
|
||||
int sender_id = getAliasID(sender);
|
||||
|
||||
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)));
|
||||
}
|
||||
|
||||
QList<HistoryKeeper::HistMessage> HistoryKeeper::getChatHistory(HistoryKeeper::ChatType ct, const QString &chat,
|
||||
const QDateTime &time_from, const QDateTime &time_to)
|
||||
{
|
||||
QList<HistMessage> res;
|
||||
|
||||
qint64 time64_from = time_from.toMSecsSinceEpoch();
|
||||
qint64 time64_to = time_to.toMSecsSinceEpoch();
|
||||
|
||||
int chat_id = getChatID(chat, ct).first;
|
||||
|
||||
QSqlQuery dbAnswer;
|
||||
if (ct == ctSingle)
|
||||
{
|
||||
dbAnswer = db->exec(QString("SELECT timestamp, user_id, message FROM history INNER JOIN aliases ON history.sender = aliases.id ") +
|
||||
QString("AND timestamp BETWEEN %1 AND %2 AND chat_id = %3;")
|
||||
.arg(time64_from).arg(time64_to).arg(chat_id));
|
||||
} else {
|
||||
// no groupchats yet
|
||||
}
|
||||
|
||||
while (dbAnswer.next())
|
||||
{
|
||||
QString sender = dbAnswer.value(1).toString();
|
||||
QString message = unWrapMessage(dbAnswer.value(2).toString());
|
||||
qint64 timeInt = dbAnswer.value(0).toLongLong();
|
||||
QDateTime time = QDateTime::fromMSecsSinceEpoch(timeInt);
|
||||
|
||||
res.push_back({sender,message,time});
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
auto dbAnswer = db->exec(QString("SELECT * FROM chats;"));
|
||||
|
||||
chats.clear();
|
||||
while (dbAnswer.next())
|
||||
{
|
||||
QString name = dbAnswer.value(1).toString();
|
||||
int id = dbAnswer.value(0).toInt();
|
||||
ChatType ctype = convertToChatType(dbAnswer.value(2).toInt());
|
||||
|
||||
chats[name] = {id, ctype};
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryKeeper::updateAliases()
|
||||
{
|
||||
auto dbAnswer = db->exec(QString("SELECT * FROM aliases;"));
|
||||
|
||||
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();
|
||||
|
||||
db->exec(QString("INSERT INTO chats (name, ctype) VALUES ('%1', '%2');").arg(id_str).arg(ct));
|
||||
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();
|
||||
|
||||
db->exec(QString("INSERT INTO aliases (user_id) VALUES ('%1');").arg(id_str));
|
||||
updateAliases();
|
||||
|
||||
return getAliasID(id_str);
|
||||
}
|
||||
|
||||
void HistoryKeeper::resetInstance()
|
||||
{
|
||||
if (historyInstance == nullptr)
|
||||
return;
|
||||
|
||||
delete historyInstance;
|
||||
historyInstance = nullptr;
|
||||
}
|
||||
|
||||
void HistoryKeeper::addGroupChatEntry(const QString &chat, const QString &message, const QString &sender, const QDateTime &dt)
|
||||
{
|
||||
Q_UNUSED(chat)
|
||||
Q_UNUSED(message)
|
||||
Q_UNUSED(sender)
|
||||
Q_UNUSED(dt)
|
||||
// no groupchats yet
|
||||
}
|
||||
|
||||
HistoryKeeper::ChatType HistoryKeeper::convertToChatType(int ct)
|
||||
{
|
||||
if (ct < 0 || ct > 1)
|
||||
return ctSingle;
|
||||
|
||||
return static_cast<ChatType>(ct);
|
||||
}
|
||||
|
||||
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"));
|
||||
}
|
71
src/historykeeper.h
Normal file
71
src/historykeeper.h
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
#ifndef HISTORYKEEPER_H
|
||||
#define HISTORYKEEPER_H
|
||||
|
||||
#include <QMap>
|
||||
#include <QList>
|
||||
#include <QDateTime>
|
||||
|
||||
class GenericDdInterface;
|
||||
|
||||
class HistoryKeeper
|
||||
{
|
||||
public:
|
||||
enum ChatType {ctSingle = 0, ctGroup};
|
||||
|
||||
struct HistMessage
|
||||
{
|
||||
QString sender;
|
||||
QString message;
|
||||
QDateTime timestamp;
|
||||
};
|
||||
|
||||
virtual ~HistoryKeeper();
|
||||
|
||||
static HistoryKeeper* getInstance();
|
||||
static void resetInstance();
|
||||
|
||||
static QString getHistoryPath();
|
||||
static bool checkPassword();
|
||||
static void renameHistory(QString from, QString to);
|
||||
|
||||
void addChatEntry(const QString& chat, const QString& message, const QString& sender, const QDateTime &dt);
|
||||
void addGroupChatEntry(const QString& chat, const QString& message, const QString& sender, const QDateTime &dt);
|
||||
QList<HistMessage> getChatHistory(ChatType ct, const QString &chat, const QDateTime &time_from, const QDateTime &time_to);
|
||||
|
||||
private:
|
||||
HistoryKeeper(GenericDdInterface *db_);
|
||||
HistoryKeeper(HistoryKeeper &hk) = delete;
|
||||
HistoryKeeper& operator=(const HistoryKeeper&) = delete;
|
||||
|
||||
void updateChatsID();
|
||||
void updateAliases();
|
||||
QPair<int, ChatType> getChatID(const QString &id_str, ChatType ct);
|
||||
int getAliasID(const QString &id_str);
|
||||
QString wrapMessage(const QString &str);
|
||||
QString unWrapMessage(const QString &str);
|
||||
|
||||
ChatType convertToChatType(int);
|
||||
|
||||
GenericDdInterface *db;
|
||||
QMap<QString, int> aliases;
|
||||
QMap<QString, QPair<int, ChatType>> chats;
|
||||
bool isEncrypted;
|
||||
};
|
||||
|
||||
#endif // HISTORYKEEPER_H
|
172
src/misc/db/encrypteddb.cpp
Normal file
172
src/misc/db/encrypteddb.cpp
Normal file
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
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 "encrypteddb.h"
|
||||
#include "src/misc/settings.h"
|
||||
#include "src/core.h"
|
||||
|
||||
#include <tox/toxencryptsave.h>
|
||||
|
||||
#include <QSqlQuery>
|
||||
#include <QDebug>
|
||||
#include <QSqlError>
|
||||
|
||||
qint64 EncryptedDb::plainChunkSize = 4096;
|
||||
qint64 EncryptedDb::encryptedChunkSize = EncryptedDb::plainChunkSize + tox_pass_encryption_extra_length();
|
||||
|
||||
EncryptedDb::EncryptedDb(const QString &fname, QList<QString> initList) :
|
||||
PlainDb(":memory:", initList), encrFile(fname)
|
||||
{
|
||||
QByteArray fileContent;
|
||||
if (pullFileContent())
|
||||
{
|
||||
chunkPosition = encrFile.size() / encryptedChunkSize;
|
||||
|
||||
encrFile.seek(0);
|
||||
fileContent = encrFile.readAll();
|
||||
} else {
|
||||
qWarning() << "corrupted history log file will be wiped!";
|
||||
chunkPosition = 0;
|
||||
}
|
||||
|
||||
encrFile.close();
|
||||
encrFile.open(QIODevice::WriteOnly);
|
||||
encrFile.write(fileContent);
|
||||
encrFile.flush();
|
||||
}
|
||||
|
||||
EncryptedDb::~EncryptedDb()
|
||||
{
|
||||
encrFile.close(); // Q: what if program is killed without being able to clean up?
|
||||
// A: cleanup isn't necessary, everything handled int appendToEncrypted(..) function
|
||||
}
|
||||
|
||||
QSqlQuery EncryptedDb::exec(const QString &query)
|
||||
{
|
||||
QSqlQuery retQSqlQuery = PlainDb::exec(query);
|
||||
if (query.startsWith("INSERT", Qt::CaseInsensitive))
|
||||
appendToEncrypted(query);
|
||||
|
||||
return retQSqlQuery;
|
||||
}
|
||||
|
||||
bool EncryptedDb::pullFileContent()
|
||||
{
|
||||
encrFile.open(QIODevice::ReadOnly);
|
||||
QByteArray fileContent;
|
||||
|
||||
while (!encrFile.atEnd())
|
||||
{
|
||||
QByteArray encrChunk = encrFile.read(encryptedChunkSize);
|
||||
buffer = Core::getInstance()->decryptData(encrChunk, Core::ptHistory);
|
||||
if (buffer.size() > 0)
|
||||
{
|
||||
fileContent += buffer;
|
||||
} else {
|
||||
qWarning() << "Encrypted history log is corrupted: can't decrypt";
|
||||
buffer = QByteArray();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
QList<QByteArray> splittedBA = fileContent.split('\n');
|
||||
QList<QString> sqlCmds;
|
||||
|
||||
for (auto ba_line : splittedBA)
|
||||
{
|
||||
QString line = QByteArray::fromBase64(ba_line);
|
||||
if (line.size() == 0)
|
||||
continue;
|
||||
|
||||
bool isGoodLine = false;
|
||||
if (line.startsWith("CREATE", Qt::CaseInsensitive) || line.startsWith("INSERT", Qt::CaseInsensitive))
|
||||
{
|
||||
if (line.endsWith(");"))
|
||||
{
|
||||
sqlCmds.append(line);
|
||||
isGoodLine = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isGoodLine)
|
||||
{
|
||||
qWarning() << "Encrypted history log is corrupted: errors in content";
|
||||
buffer = QByteArray();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto line : sqlCmds)
|
||||
{
|
||||
QSqlQuery r = PlainDb::exec(line);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void EncryptedDb::appendToEncrypted(const QString &sql)
|
||||
{
|
||||
QByteArray b64Str;
|
||||
b64Str.append(sql);
|
||||
b64Str = b64Str.toBase64(); // much easier to parse strings like this from file
|
||||
|
||||
buffer += b64Str + "\n";
|
||||
|
||||
while (buffer.size() > plainChunkSize)
|
||||
{
|
||||
QByteArray filledChunk = buffer.left(plainChunkSize);
|
||||
encrFile.seek(chunkPosition * encryptedChunkSize);
|
||||
QByteArray encr = Core::getInstance()->encryptData(filledChunk, Core::ptHistory);
|
||||
if (encr.size() > 0)
|
||||
{
|
||||
encrFile.write(encr);
|
||||
}
|
||||
|
||||
buffer = buffer.right(buffer.size() - plainChunkSize);
|
||||
chunkPosition++;
|
||||
}
|
||||
encrFile.seek(chunkPosition * encryptedChunkSize);
|
||||
|
||||
QByteArray encr = Core::getInstance()->encryptData(buffer, Core::ptHistory);
|
||||
if (encr.size() > 0)
|
||||
{
|
||||
encrFile.write(encr);
|
||||
}
|
||||
encrFile.flush();
|
||||
}
|
||||
|
||||
bool EncryptedDb::check(const QString &fname)
|
||||
{
|
||||
QFile file(fname);
|
||||
file.open(QIODevice::ReadOnly);
|
||||
bool state = true;
|
||||
|
||||
if (file.size() > 0)
|
||||
{
|
||||
QByteArray encrChunk = file.read(encryptedChunkSize);
|
||||
QByteArray buf = Core::getInstance()->decryptData(encrChunk, Core::ptHistory);
|
||||
if (buf.size() == 0)
|
||||
{
|
||||
state = false;
|
||||
}
|
||||
} else {
|
||||
file.close();
|
||||
file.open(QIODevice::WriteOnly);
|
||||
}
|
||||
|
||||
file.close();
|
||||
return state;
|
||||
}
|
47
src/misc/db/encrypteddb.h
Normal file
47
src/misc/db/encrypteddb.h
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
#ifndef ENCRYPTEDDB_H
|
||||
#define ENCRYPTEDDB_H
|
||||
|
||||
#include "plaindb.h"
|
||||
|
||||
#include <QList>
|
||||
#include <QFile>
|
||||
|
||||
class EncryptedDb : public PlainDb
|
||||
{
|
||||
public:
|
||||
EncryptedDb(const QString& fname, QList<QString> initList);
|
||||
virtual ~EncryptedDb();
|
||||
|
||||
virtual QSqlQuery exec(const QString &query);
|
||||
static bool check(const QString &fname);
|
||||
|
||||
private:
|
||||
bool pullFileContent();
|
||||
void appendToEncrypted(const QString &sql);
|
||||
|
||||
QFile encrFile;
|
||||
|
||||
static qint64 plainChunkSize;
|
||||
static qint64 encryptedChunkSize;
|
||||
|
||||
qint64 chunkPosition;
|
||||
QByteArray buffer;
|
||||
};
|
||||
|
||||
#endif // ENCRYPTEDDB_H
|
21
src/misc/db/genericddinterface.cpp
Normal file
21
src/misc/db/genericddinterface.cpp
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
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 "genericddinterface.h"
|
||||
|
||||
GenericDdInterface::~GenericDdInterface()
|
||||
{
|
||||
}
|
31
src/misc/db/genericddinterface.h
Normal file
31
src/misc/db/genericddinterface.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
#ifndef GENERICDDINTERFACE_H
|
||||
#define GENERICDDINTERFACE_H
|
||||
|
||||
class QSqlQuery;
|
||||
class QString;
|
||||
|
||||
class GenericDdInterface
|
||||
{
|
||||
public:
|
||||
virtual ~GenericDdInterface();
|
||||
|
||||
virtual QSqlQuery exec(const QString &query) = 0;
|
||||
};
|
||||
|
||||
#endif // GENERICDDINTERFACE_H
|
51
src/misc/db/plaindb.cpp
Normal file
51
src/misc/db/plaindb.cpp
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
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 "plaindb.h"
|
||||
#include <QDebug>
|
||||
#include <QSqlQuery>
|
||||
#include <QString>
|
||||
|
||||
PlainDb::PlainDb(const QString &db_name, QList<QString> initList)
|
||||
{
|
||||
db = new QSqlDatabase();
|
||||
*db = QSqlDatabase::addDatabase("QSQLITE");
|
||||
db->setDatabaseName(db_name);
|
||||
|
||||
if (!db->open())
|
||||
{
|
||||
qWarning() << QString("Can't open file: %1, history will not be saved!").arg(db_name);
|
||||
db->setDatabaseName(":memory:");
|
||||
db->open();
|
||||
}
|
||||
|
||||
for (const QString &cmd : initList)
|
||||
db->exec(cmd);
|
||||
}
|
||||
|
||||
PlainDb::~PlainDb()
|
||||
{
|
||||
db->close();
|
||||
QString dbConName = db->connectionName();
|
||||
delete db;
|
||||
|
||||
QSqlDatabase::removeDatabase(dbConName);
|
||||
}
|
||||
|
||||
QSqlQuery PlainDb::exec(const QString &query)
|
||||
{
|
||||
return db->exec(query);
|
||||
}
|
36
src/misc/db/plaindb.h
Normal file
36
src/misc/db/plaindb.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
#ifndef PLAINDB_H
|
||||
#define PLAINDB_H
|
||||
|
||||
#include "genericddinterface.h"
|
||||
|
||||
#include <QSqlDatabase>
|
||||
|
||||
class PlainDb : public GenericDdInterface
|
||||
{
|
||||
public:
|
||||
PlainDb(const QString &db_name, QList<QString> initList);
|
||||
virtual ~PlainDb();
|
||||
|
||||
virtual QSqlQuery exec(const QString &query);
|
||||
|
||||
private:
|
||||
QSqlDatabase *db;
|
||||
};
|
||||
|
||||
#endif // PLAINDB_H
|
|
@ -152,6 +152,9 @@ void Settings::load()
|
|||
|
||||
s.beginGroup("Privacy");
|
||||
typingNotification = s.value("typingNotification", false).toBool();
|
||||
enableLogging = s.value("enableLogging", false).toBool();
|
||||
encryptLogs = s.value("encryptLogs", false).toBool();
|
||||
encryptTox = s.value("encryptTox", false).toBool();
|
||||
s.endGroup();
|
||||
|
||||
s.beginGroup("AutoAccept");
|
||||
|
@ -268,6 +271,9 @@ void Settings::save(QString path)
|
|||
|
||||
s.beginGroup("Privacy");
|
||||
s.setValue("typingNotification", typingNotification);
|
||||
s.setValue("enableLogging", enableLogging);
|
||||
s.setValue("encryptLogs", encryptLogs);
|
||||
s.setValue("encryptTox", encryptTox);
|
||||
s.endGroup();
|
||||
|
||||
s.beginGroup("AutoAccept");
|
||||
|
@ -506,6 +512,16 @@ void Settings::setEncryptLogs(bool newValue)
|
|||
encryptLogs = newValue;
|
||||
}
|
||||
|
||||
bool Settings::getEncryptTox() const
|
||||
{
|
||||
return encryptTox;
|
||||
}
|
||||
|
||||
void Settings::setEncryptTox(bool newValue)
|
||||
{
|
||||
encryptTox = newValue;
|
||||
}
|
||||
|
||||
int Settings::getAutoAwayTime() const
|
||||
{
|
||||
return autoAwayTime;
|
||||
|
|
|
@ -85,6 +85,9 @@ public:
|
|||
bool getEncryptLogs() const;
|
||||
void setEncryptLogs(bool newValue);
|
||||
|
||||
bool getEncryptTox() const;
|
||||
void setEncryptTox(bool newValue);
|
||||
|
||||
int getAutoAwayTime() const;
|
||||
void setAutoAwayTime(int newValue);
|
||||
|
||||
|
@ -207,6 +210,7 @@ private:
|
|||
|
||||
bool enableLogging;
|
||||
bool encryptLogs;
|
||||
bool encryptTox;
|
||||
|
||||
int autoAwayTime;
|
||||
|
||||
|
|
|
@ -152,6 +152,7 @@ QTextTable *ChatAreaWidget::getMsgTable()
|
|||
|
||||
QTextCursor tc = textCursor();
|
||||
tc.movePosition(QTextCursor::End);
|
||||
|
||||
QTextTable *chatTextTable = tc.insertTable(1, 5, *tableFrmt);
|
||||
|
||||
return chatTextTable;
|
||||
|
@ -167,3 +168,24 @@ void ChatAreaWidget::setNameColWidth(int w)
|
|||
|
||||
nameWidth = w;
|
||||
}
|
||||
|
||||
void ChatAreaWidget::clearChatArea()
|
||||
{
|
||||
QList<ChatAction*> newMsgs;
|
||||
for (ChatAction* message : messages)
|
||||
{
|
||||
if (message->isInteractive())
|
||||
{
|
||||
newMsgs.append(message);
|
||||
} else {
|
||||
delete message;
|
||||
}
|
||||
}
|
||||
messages.clear();
|
||||
this->clear();
|
||||
|
||||
for (ChatAction* message : newMsgs)
|
||||
{
|
||||
insertMessage(message);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,11 +30,15 @@ public:
|
|||
explicit ChatAreaWidget(QWidget *parent = 0);
|
||||
virtual ~ChatAreaWidget();
|
||||
void insertMessage(ChatAction *msgAction);
|
||||
QList<ChatAction*>& getMesages() {return messages;}
|
||||
|
||||
int nameColWidth() {return nameWidth;}
|
||||
void setNameColWidth(int w);
|
||||
int getNumberOfMessages();
|
||||
|
||||
public slots:
|
||||
void clearChatArea();
|
||||
|
||||
signals:
|
||||
void onFileTranfertInterract(QString widgetName, QString buttonName);
|
||||
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
#include <QDragEnterEvent>
|
||||
#include <QBitmap>
|
||||
#include "chatform.h"
|
||||
#include "src/historykeeper.h"
|
||||
#include "src/widget/form/loadhistorydialog.h"
|
||||
#include "src/friend.h"
|
||||
#include "src/widget/friendwidget.h"
|
||||
#include "src/filetransferinstance.h"
|
||||
|
@ -57,6 +59,8 @@ ChatForm::ChatForm(Friend* chatFriend)
|
|||
headTextLayout->addStretch();
|
||||
headTextLayout->setSpacing(0);
|
||||
|
||||
menu.addAction(tr("Load History..."), this, SLOT(onLoadHistory()));
|
||||
|
||||
connect(Core::getInstance(), &Core::fileSendStarted, this, &ChatForm::startFileSend);
|
||||
connect(sendButton, &QPushButton::clicked, this, &ChatForm::onSendTriggered);
|
||||
connect(fileButton, &QPushButton::clicked, this, &ChatForm::onAttachClicked);
|
||||
|
@ -87,15 +91,18 @@ void ChatForm::onSendTriggered()
|
|||
if (msg.isEmpty())
|
||||
return;
|
||||
QString name = Widget::getInstance()->getUsername();
|
||||
if (msg.startsWith("/me"))
|
||||
QDateTime timestamp = QDateTime::currentDateTime();
|
||||
HistoryKeeper::getInstance()->addChatEntry(f->userId, msg, Core::getInstance()->getSelfId().publicKey, timestamp);
|
||||
|
||||
if (msg.startsWith("/me "))
|
||||
{
|
||||
msg = msg.right(msg.length() - 4);
|
||||
addMessage(name, msg, true);
|
||||
addMessage(name, msg, true, timestamp);
|
||||
emit sendAction(f->friendId, msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
addMessage(name, msg, false);
|
||||
addMessage(name, msg, false, timestamp);
|
||||
emit sendMessage(f->friendId, msg);
|
||||
}
|
||||
msgEdit->clear();
|
||||
|
@ -502,7 +509,7 @@ void ChatForm::onFileSendFailed(int FriendId, const QString &fname)
|
|||
if (FriendId != f->friendId)
|
||||
return;
|
||||
|
||||
addSystemInfoMessage("File: \"" + fname + "\" failed to send.", "red");
|
||||
addSystemInfoMessage("File: \"" + fname + "\" failed to send.", "red", QDateTime::currentDateTime());
|
||||
}
|
||||
|
||||
void ChatForm::onAvatarChange(int FriendId, const QPixmap &pic)
|
||||
|
@ -540,3 +547,61 @@ void ChatForm::onAvatarRemoved(int FriendId)
|
|||
|
||||
avatar->setPixmap(QPixmap(":/img/contact_dark.png"), Qt::transparent);
|
||||
}
|
||||
|
||||
void ChatForm::onLoadHistory()
|
||||
{
|
||||
LoadHistoryDialog dlg;
|
||||
|
||||
if (dlg.exec())
|
||||
{
|
||||
QDateTime fromTime = dlg.getFromDate();
|
||||
QDateTime toTime = QDateTime::currentDateTime();
|
||||
|
||||
if (fromTime > toTime)
|
||||
return;
|
||||
|
||||
if (earliestMessage)
|
||||
{
|
||||
if (*earliestMessage < fromTime)
|
||||
return;
|
||||
if (*earliestMessage < toTime)
|
||||
{
|
||||
toTime = *earliestMessage;
|
||||
toTime = toTime.addMSecs(-1);
|
||||
}
|
||||
}
|
||||
|
||||
auto msgs = HistoryKeeper::getInstance()->getChatHistory(HistoryKeeper::ctSingle, f->userId, fromTime, toTime);
|
||||
|
||||
QString storedPrevName = previousName;
|
||||
previousName = "";
|
||||
QList<ChatAction*> historyMessages;
|
||||
|
||||
for (const auto &it : msgs)
|
||||
{
|
||||
QString name = f->getName();
|
||||
if (it.sender == Core::getInstance()->getSelfId().publicKey)
|
||||
name = Core::getInstance()->getUsername();
|
||||
|
||||
ChatAction *ca = genMessageActionAction(name, it.message, false, it.timestamp.toLocalTime());
|
||||
historyMessages.append(ca);
|
||||
}
|
||||
previousName = storedPrevName;
|
||||
|
||||
for (ChatAction *ca : chatWidget->getMesages())
|
||||
historyMessages.append(ca);
|
||||
|
||||
int savedSliderPos = chatWidget->verticalScrollBar()->maximum() - chatWidget->verticalScrollBar()->value();
|
||||
|
||||
chatWidget->getMesages().clear();
|
||||
chatWidget->clear();
|
||||
if (earliestMessage != nullptr)
|
||||
*earliestMessage = fromTime;
|
||||
|
||||
for (ChatAction *ca : historyMessages)
|
||||
chatWidget->insertMessage(ca);
|
||||
|
||||
savedSliderPos = chatWidget->verticalScrollBar()->maximum() - savedSliderPos;
|
||||
chatWidget->verticalScrollBar()->setValue(savedSliderPos);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,6 +69,7 @@ private slots:
|
|||
void onCancelCallTriggered();
|
||||
void onFileTansBtnClicked(QString widgetName, QString buttonName);
|
||||
void onFileSendFailed(int FriendId, const QString &fname);
|
||||
void onLoadHistory();
|
||||
|
||||
protected:
|
||||
// drag & drop
|
||||
|
|
|
@ -31,7 +31,8 @@
|
|||
#include "src/widget/maskablepixmapwidget.h"
|
||||
|
||||
GenericChatForm::GenericChatForm(QWidget *parent) :
|
||||
QWidget(parent)
|
||||
QWidget(parent),
|
||||
earliestMessage(nullptr)
|
||||
{
|
||||
curRow = 0;
|
||||
|
||||
|
@ -116,6 +117,10 @@ GenericChatForm::GenericChatForm(QWidget *parent) :
|
|||
fileButton->setAttribute(Qt::WA_LayoutUsesWidgetRect);
|
||||
emoteButton->setAttribute(Qt::WA_LayoutUsesWidgetRect);
|
||||
|
||||
menu.addAction(tr("Save chat log"), this, SLOT(onSaveLogClicked()));
|
||||
menu.addAction(tr("Clear displayed messages"), this, SLOT(clearChatArea(bool)));
|
||||
menu.addSeparator();
|
||||
|
||||
connect(emoteButton, SIGNAL(clicked()), this, SLOT(onEmoteButtonClicked()));
|
||||
connect(chatWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onChatContextMenuRequested(QPoint)));
|
||||
|
||||
|
@ -147,8 +152,6 @@ void GenericChatForm::onChatContextMenuRequested(QPoint pos)
|
|||
{
|
||||
QWidget* sender = (QWidget*)QObject::sender();
|
||||
pos = sender->mapToGlobal(pos);
|
||||
QMenu menu;
|
||||
menu.addAction(tr("Save chat log"), this, SLOT(onSaveLogClicked()));
|
||||
menu.exec(pos);
|
||||
}
|
||||
|
||||
|
@ -169,29 +172,10 @@ void GenericChatForm::onSaveLogClicked()
|
|||
file.close();
|
||||
}
|
||||
|
||||
void GenericChatForm::addMessage(QString author, QString message, bool isAction, QDateTime datetime)
|
||||
void GenericChatForm::addMessage(const QString &author, const QString &message, bool isAction, const QDateTime &datetime)
|
||||
{
|
||||
QString date = datetime.toString(Settings::getInstance().getTimestampFormat());
|
||||
bool isMe = (author == Widget::getInstance()->getUsername());
|
||||
|
||||
if (!isAction && message.startsWith("/me"))
|
||||
{ // always render actions regardless of what core thinks
|
||||
isAction = true;
|
||||
message = message.right(message.length()-4);
|
||||
}
|
||||
|
||||
if (isAction)
|
||||
{
|
||||
chatWidget->insertMessage(new ActionAction (getElidedName(author), message, date, isMe));
|
||||
previousName = ""; // next msg has a name regardless
|
||||
return;
|
||||
}
|
||||
else if (previousName == author)
|
||||
chatWidget->insertMessage(new MessageAction("", message, date, isMe));
|
||||
else
|
||||
chatWidget->insertMessage(new MessageAction(getElidedName(author), message, date, isMe));
|
||||
|
||||
previousName = author;
|
||||
ChatAction *ca = genMessageActionAction(author, message, isAction, datetime);
|
||||
chatWidget->insertMessage(ca);
|
||||
}
|
||||
|
||||
void GenericChatForm::addAlertMessage(QString author, QString message, QDateTime datetime)
|
||||
|
@ -235,10 +219,8 @@ void GenericChatForm::focusInput()
|
|||
|
||||
void GenericChatForm::addSystemInfoMessage(const QString &message, const QString &type, const QDateTime &datetime)
|
||||
{
|
||||
previousName = "";
|
||||
QString date = datetime.toString(Settings::getInstance().getTimestampFormat());
|
||||
|
||||
chatWidget->insertMessage(new SystemMessageAction(message, type, date));
|
||||
ChatAction *ca = genSystemInfoAction(message, type, datetime);
|
||||
chatWidget->insertMessage(ca);
|
||||
}
|
||||
|
||||
QString GenericChatForm::getElidedName(const QString& name)
|
||||
|
@ -248,3 +230,58 @@ QString GenericChatForm::getElidedName(const QString& name)
|
|||
|
||||
return fm.elidedText(name, Qt::ElideRight, chatWidget->nameColWidth());
|
||||
}
|
||||
|
||||
void GenericChatForm::clearChatArea(bool notinform)
|
||||
{
|
||||
chatWidget->clearChatArea();
|
||||
previousName = "";
|
||||
|
||||
if (!notinform)
|
||||
addSystemInfoMessage(tr("Cleared"), "white", QDateTime::currentDateTime());
|
||||
|
||||
if (earliestMessage)
|
||||
{
|
||||
delete earliestMessage;
|
||||
earliestMessage = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
ChatAction* GenericChatForm::genMessageActionAction(const QString &author, QString message, bool isAction, const QDateTime &datetime)
|
||||
{
|
||||
if (earliestMessage == nullptr)
|
||||
{
|
||||
earliestMessage = new QDateTime(datetime);
|
||||
}
|
||||
|
||||
QString date = datetime.toString(Settings::getInstance().getTimestampFormat());
|
||||
bool isMe = (author == Widget::getInstance()->getUsername());
|
||||
|
||||
if (!isAction && message.startsWith("/me "))
|
||||
{ // always render actions regardless of what core thinks
|
||||
isAction = true;
|
||||
message = message.right(message.length()-4);
|
||||
}
|
||||
|
||||
if (isAction)
|
||||
{
|
||||
previousName = ""; // next msg has a name regardless
|
||||
return (new ActionAction (getElidedName(author), message, date, isMe));
|
||||
}
|
||||
|
||||
ChatAction *res;
|
||||
if (previousName == author)
|
||||
res = (new MessageAction("", message, date, isMe));
|
||||
else
|
||||
res = (new MessageAction(getElidedName(author), message, date, isMe));
|
||||
|
||||
previousName = author;
|
||||
return res;
|
||||
}
|
||||
|
||||
ChatAction* GenericChatForm::genSystemInfoAction(const QString &message, const QString &type, const QDateTime &datetime)
|
||||
{
|
||||
previousName = "";
|
||||
QString date = datetime.toString(Settings::getInstance().getTimestampFormat());
|
||||
|
||||
return (new SystemMessageAction(message, type, date));
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <QWidget>
|
||||
#include <QPoint>
|
||||
#include <QDateTime>
|
||||
#include <QMenu>
|
||||
|
||||
// Spacing in px inserted when the author of the last message changes
|
||||
#define AUTHOR_CHANGE_SPACING 5 // why the hell is this a thing? surely the different font is enough?
|
||||
|
@ -31,6 +32,7 @@ class CroppingLabel;
|
|||
class ChatTextEdit;
|
||||
class ChatAreaWidget;
|
||||
class MaskablePixmapWidget;
|
||||
class ChatAction;
|
||||
|
||||
namespace Ui {
|
||||
class MainWindow;
|
||||
|
@ -44,11 +46,11 @@ public:
|
|||
|
||||
virtual void setName(const QString &newName);
|
||||
virtual void show(Ui::MainWindow &ui);
|
||||
void addMessage(QString author, QString message, bool isAction = false, QDateTime datetime=QDateTime::currentDateTime());
|
||||
void addAlertMessage(QString author, QString message, QDateTime datetime=QDateTime::currentDateTime());
|
||||
void addSystemInfoMessage(const QString &message, const QString &type, const QDateTime &datetime=QDateTime::currentDateTime());
|
||||
void addMessage(const QString &author, const QString &message, bool isAction, const QDateTime &datetime);
|
||||
void addSystemInfoMessage(const QString &message, const QString &type, const QDateTime &datetime);
|
||||
void addAlertMessage(QString author, QString message, QDateTime datetime);
|
||||
int getNumberOfMessages();
|
||||
|
||||
|
||||
signals:
|
||||
void sendMessage(int, QString);
|
||||
void sendAction(int, QString);
|
||||
|
@ -61,10 +63,14 @@ protected slots:
|
|||
void onSaveLogClicked();
|
||||
void onEmoteButtonClicked();
|
||||
void onEmoteInsertRequested(QString str);
|
||||
void clearChatArea(bool);
|
||||
|
||||
protected:
|
||||
QString getElidedName(const QString& name);
|
||||
ChatAction* genMessageActionAction(const QString &author, QString message, bool isAction, const QDateTime &datetime);
|
||||
ChatAction* genSystemInfoAction(const QString &message, const QString &type, const QDateTime &datetime);
|
||||
|
||||
QMenu menu;
|
||||
CroppingLabel *nameLabel;
|
||||
MaskablePixmapWidget *avatar;
|
||||
QWidget *headWidget;
|
||||
|
@ -75,6 +81,7 @@ protected:
|
|||
QString previousName;
|
||||
ChatAreaWidget *chatWidget;
|
||||
int curRow;
|
||||
QDateTime *earliestMessage;
|
||||
};
|
||||
|
||||
#endif // GENERICCHATFORM_H
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <QPushButton>
|
||||
#include <QMimeData>
|
||||
#include <QDragEnterEvent>
|
||||
#include "src/historykeeper.h"
|
||||
|
||||
GroupChatForm::GroupChatForm(Group* chatGroup)
|
||||
: group(chatGroup)
|
||||
|
|
38
src/widget/form/inputpassworddialog.cpp
Normal file
38
src/widget/form/inputpassworddialog.cpp
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
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 "inputpassworddialog.h"
|
||||
#include "ui_inputpassworddialog.h"
|
||||
|
||||
InputPasswordDialog::InputPasswordDialog(QString title, QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::InputPasswordDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
if (title != QString())
|
||||
setWindowTitle(title);
|
||||
}
|
||||
|
||||
InputPasswordDialog::~InputPasswordDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
QString InputPasswordDialog::getPassword()
|
||||
{
|
||||
return ui->passwordLineEdit->text();
|
||||
}
|
40
src/widget/form/inputpassworddialog.h
Normal file
40
src/widget/form/inputpassworddialog.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
#ifndef INPUTPASSWORDDIALOG_H
|
||||
#define INPUTPASSWORDDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
namespace Ui {
|
||||
class InputPasswordDialog;
|
||||
}
|
||||
|
||||
class InputPasswordDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit InputPasswordDialog(QString title = QString(), QWidget *parent = 0);
|
||||
~InputPasswordDialog();
|
||||
|
||||
QString getPassword();
|
||||
|
||||
private:
|
||||
Ui::InputPasswordDialog *ui;
|
||||
};
|
||||
|
||||
#endif // INPUTPASSWORDDIALOG_H
|
97
src/widget/form/inputpassworddialog.ui
Normal file
97
src/widget/form/inputpassworddialog.ui
Normal file
|
@ -0,0 +1,97 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>InputPasswordDialog</class>
|
||||
<widget class="QDialog" name="InputPasswordDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>123</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Password Dialog</string>
|
||||
</property>
|
||||
<property name="modal">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Input password:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="passwordLineEdit">
|
||||
<property name="cursor">
|
||||
<cursorShape>IBeamCursor</cursorShape>
|
||||
</property>
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Password</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>InputPasswordDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>InputPasswordDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
36
src/widget/form/loadhistorydialog.cpp
Normal file
36
src/widget/form/loadhistorydialog.cpp
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
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 "loadhistorydialog.h"
|
||||
#include "ui_loadhistorydialog.h"
|
||||
|
||||
LoadHistoryDialog::LoadHistoryDialog(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::LoadHistoryDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
}
|
||||
|
||||
LoadHistoryDialog::~LoadHistoryDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
QDateTime LoadHistoryDialog::getFromDate()
|
||||
{
|
||||
QDateTime res(ui->fromDate->selectedDate());
|
||||
return res;
|
||||
}
|
41
src/widget/form/loadhistorydialog.h
Normal file
41
src/widget/form/loadhistorydialog.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
#ifndef LOADHISTORYDIALOG_H
|
||||
#define LOADHISTORYDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QDateTime>
|
||||
|
||||
namespace Ui {
|
||||
class LoadHistoryDialog;
|
||||
}
|
||||
|
||||
class LoadHistoryDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit LoadHistoryDialog(QWidget *parent = 0);
|
||||
~LoadHistoryDialog();
|
||||
|
||||
QDateTime getFromDate();
|
||||
|
||||
private:
|
||||
Ui::LoadHistoryDialog *ui;
|
||||
};
|
||||
|
||||
#endif // LOADHISTORYDIALOG_H
|
81
src/widget/form/loadhistorydialog.ui
Normal file
81
src/widget/form/loadhistorydialog.ui
Normal file
|
@ -0,0 +1,81 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>LoadHistoryDialog</class>
|
||||
<widget class="QDialog" name="LoadHistoryDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>347</width>
|
||||
<height>264</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Load History Dialog</string>
|
||||
</property>
|
||||
<property name="modal">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="fromLabel">
|
||||
<property name="text">
|
||||
<string>Load history from:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCalendarWidget" name="fromDate">
|
||||
<property name="gridVisible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>LoadHistoryDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>LoadHistoryDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
47
src/widget/form/setpassworddialog.cpp
Normal file
47
src/widget/form/setpassworddialog.cpp
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
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 "setpassworddialog.h"
|
||||
#include "ui_setpassworddialog.h"
|
||||
#include <QPushButton>
|
||||
|
||||
SetPasswordDialog::SetPasswordDialog(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::SetPasswordDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
connect(ui->passwordlineEdit, SIGNAL(textChanged(QString)), this, SLOT(onPasswordEdit()));
|
||||
connect(ui->repasswordlineEdit, SIGNAL(textChanged(QString)), this, SLOT(onPasswordEdit()));
|
||||
}
|
||||
|
||||
SetPasswordDialog::~SetPasswordDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void SetPasswordDialog::onPasswordEdit()
|
||||
{
|
||||
if (ui->passwordlineEdit->text() == ui->repasswordlineEdit->text())
|
||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
|
||||
else
|
||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
|
||||
}
|
||||
|
||||
QString SetPasswordDialog::getPassword()
|
||||
{
|
||||
return ui->passwordlineEdit->text();
|
||||
}
|
42
src/widget/form/setpassworddialog.h
Normal file
42
src/widget/form/setpassworddialog.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
#ifndef SETPASSWORDDIALOG_H
|
||||
#define SETPASSWORDDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
namespace Ui {
|
||||
class SetPasswordDialog;
|
||||
}
|
||||
|
||||
class SetPasswordDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SetPasswordDialog(QWidget *parent = 0);
|
||||
~SetPasswordDialog();
|
||||
QString getPassword();
|
||||
|
||||
private slots:
|
||||
void onPasswordEdit();
|
||||
|
||||
private:
|
||||
Ui::SetPasswordDialog *ui;
|
||||
};
|
||||
|
||||
#endif // SETPASSWORDDIALOG_H
|
111
src/widget/form/setpassworddialog.ui
Normal file
111
src/widget/form/setpassworddialog.ui
Normal file
|
@ -0,0 +1,111 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>SetPasswordDialog</class>
|
||||
<widget class="QDialog" name="SetPasswordDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>434</width>
|
||||
<height>175</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Dialog</string>
|
||||
</property>
|
||||
<property name="modal">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Type Password</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="passwordlineEdit">
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Password</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Repeat Password</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="repasswordlineEdit">
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Password</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
<property name="centerButtons">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>SetPasswordDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>SetPasswordDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
|
@ -21,6 +21,7 @@
|
|||
#include "src/misc/settings.h"
|
||||
#include "src/widget/croppinglabel.h"
|
||||
#include "src/widget/widget.h"
|
||||
#include "src/historykeeper.h"
|
||||
#include "src/misc/style.h"
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
|
@ -133,6 +134,7 @@ void IdentityForm::onRenameClicked()
|
|||
{
|
||||
QFile::rename(dir.filePath(cur+Core::TOX_EXT), file);
|
||||
bodyUI->profiles->setItemText(bodyUI->profiles->currentIndex(), name);
|
||||
HistoryKeeper::renameHistory(cur, name);
|
||||
Settings::getInstance().setCurrentProfile(name);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -15,13 +15,121 @@
|
|||
*/
|
||||
|
||||
#include "privacyform.h"
|
||||
#include "ui_privacysettings.h"
|
||||
#include "src/widget/form/settingswidget.h"
|
||||
#include "src/misc/settings.h"
|
||||
#include "src/historykeeper.h"
|
||||
#include "src/core.h"
|
||||
#include "src/widget/widget.h"
|
||||
#include "src/widget/form/setpassworddialog.h"
|
||||
#include <QMessageBox>
|
||||
|
||||
PrivacyForm::PrivacyForm() :
|
||||
GenericForm(tr("Privacy"), QPixmap(":/img/settings/privacy.png"))
|
||||
{
|
||||
bodyUI = new Ui::PrivacySettings;
|
||||
bodyUI->setupUi(this);
|
||||
|
||||
bodyUI->cbTypingNotification->setChecked(Settings::getInstance().isTypingNotificationEnabled());
|
||||
bodyUI->cbKeepHistory->setChecked(Settings::getInstance().getEnableLogging());
|
||||
bodyUI->cbEncryptHistory->setChecked(Settings::getInstance().getEncryptLogs());
|
||||
bodyUI->cbEncryptHistory->setEnabled(Settings::getInstance().getEnableLogging());
|
||||
bodyUI->cbEncryptTox->setChecked(Settings::getInstance().getEncryptTox());
|
||||
|
||||
connect(bodyUI->cbTypingNotification, SIGNAL(stateChanged(int)), this, SLOT(onTypingNotificationEnabledUpdated()));
|
||||
connect(bodyUI->cbKeepHistory, SIGNAL(stateChanged(int)), this, SLOT(onEnableLoggingUpdated()));
|
||||
connect(bodyUI->cbEncryptHistory, SIGNAL(clicked()), this, SLOT(onEncryptLogsUpdated()));
|
||||
connect(bodyUI->cbEncryptTox, SIGNAL(clicked()), this, SLOT(onEncryptToxUpdated()));
|
||||
}
|
||||
|
||||
PrivacyForm::~PrivacyForm()
|
||||
{
|
||||
delete bodyUI;
|
||||
}
|
||||
|
||||
void PrivacyForm::onEnableLoggingUpdated()
|
||||
{
|
||||
Settings::getInstance().setEnableLogging(bodyUI->cbKeepHistory->isChecked());
|
||||
bodyUI->cbEncryptHistory->setEnabled(bodyUI->cbKeepHistory->isChecked());
|
||||
HistoryKeeper::getInstance()->resetInstance();
|
||||
}
|
||||
|
||||
void PrivacyForm::onTypingNotificationEnabledUpdated()
|
||||
{
|
||||
Settings::getInstance().setTypingNotification(bodyUI->cbTypingNotification->isChecked());
|
||||
}
|
||||
|
||||
void PrivacyForm::onEncryptLogsUpdated()
|
||||
{
|
||||
bool encrytionState = bodyUI->cbEncryptHistory->isChecked();
|
||||
|
||||
if (encrytionState)
|
||||
{
|
||||
if (!Core::getInstance()->isPasswordSet(Core::ptHistory))
|
||||
{
|
||||
SetPasswordDialog dialog;
|
||||
if (dialog.exec())
|
||||
{
|
||||
QString pswd = dialog.getPassword();
|
||||
if (pswd.size() == 0)
|
||||
encrytionState = false;
|
||||
|
||||
Core::getInstance()->setPassword(pswd, Core::ptHistory);
|
||||
} else {
|
||||
encrytionState = false;
|
||||
Core::getInstance()->clearPassword(Core::ptHistory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Settings::getInstance().setEncryptLogs(encrytionState);
|
||||
if (encrytionState && !HistoryKeeper::checkPassword())
|
||||
{
|
||||
if (QMessageBox::Ok != QMessageBox::warning(nullptr, tr("Encrypted log"),
|
||||
tr("You already have history log file encrypted with different password\nDo you want to delete old history file?"),
|
||||
QMessageBox::Ok | QMessageBox::Cancel))
|
||||
{
|
||||
// TODO: ask user about reencryption with new password
|
||||
encrytionState = false;
|
||||
}
|
||||
}
|
||||
|
||||
Settings::getInstance().setEncryptLogs(encrytionState);
|
||||
bodyUI->cbEncryptHistory->setChecked(encrytionState);
|
||||
|
||||
if (encrytionState)
|
||||
HistoryKeeper::resetInstance();
|
||||
|
||||
if (!Settings::getInstance().getEncryptLogs())
|
||||
Core::getInstance()->clearPassword(Core::ptHistory);
|
||||
}
|
||||
|
||||
void PrivacyForm::onEncryptToxUpdated()
|
||||
{
|
||||
bool encrytionState = bodyUI->cbEncryptTox->isChecked();
|
||||
|
||||
if (encrytionState)
|
||||
{
|
||||
if (!Core::getInstance()->isPasswordSet(Core::ptMain))
|
||||
{
|
||||
SetPasswordDialog dialog;
|
||||
if (dialog.exec())
|
||||
{
|
||||
QString pswd = dialog.getPassword();
|
||||
if (pswd.size() == 0)
|
||||
encrytionState = false;
|
||||
|
||||
Core::getInstance()->setPassword(pswd, Core::ptMain);
|
||||
} else {
|
||||
encrytionState = false;
|
||||
Core::getInstance()->clearPassword(Core::ptMain);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bodyUI->cbEncryptTox->setChecked(encrytionState);
|
||||
Settings::getInstance().setEncryptTox(encrytionState);
|
||||
|
||||
if (!Settings::getInstance().getEncryptTox())
|
||||
Core::getInstance()->clearPassword(Core::ptMain);
|
||||
}
|
||||
|
|
|
@ -19,6 +19,10 @@
|
|||
|
||||
#include "genericsettings.h"
|
||||
|
||||
namespace Ui {
|
||||
class PrivacySettings;
|
||||
}
|
||||
|
||||
class PrivacyForm : public GenericForm
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -26,8 +30,15 @@ public:
|
|||
PrivacyForm();
|
||||
~PrivacyForm();
|
||||
|
||||
private slots:
|
||||
void onEnableLoggingUpdated();
|
||||
void onTypingNotificationEnabledUpdated();
|
||||
|
||||
void onEncryptLogsUpdated();
|
||||
void onEncryptToxUpdated();
|
||||
|
||||
private:
|
||||
|
||||
Ui::PrivacySettings* bodyUI;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
83
src/widget/form/settings/privacysettings.ui
Normal file
83
src/widget/form/settings/privacysettings.ui
Normal file
|
@ -0,0 +1,83 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>PrivacySettings</class>
|
||||
<widget class="QWidget" name="PrivacySettings">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cbTypingNotification">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Typing Notification</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cbKeepHistory">
|
||||
<property name="toolTip">
|
||||
<string extracomment="History keeping still under developing. Log format changin is possible."/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Keep History (unstable)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="encryptionGroup">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Encryption</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cbEncryptTox">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Encrypt Tox datafile</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cbEncryptHistory">
|
||||
<property name="text">
|
||||
<string>Encrypt History</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -22,21 +22,6 @@ ActionAction::ActionAction(const QString &author, QString message, const QString
|
|||
{
|
||||
}
|
||||
|
||||
void ActionAction::setup(QTextCursor cursor, QTextEdit *)
|
||||
{
|
||||
// When this function is called, we're supposed to only update ourselve when needed
|
||||
// Nobody should ask us to do anything with our content, we're on our own
|
||||
// Except we never udpate on our own, so we can safely free our resources
|
||||
|
||||
(void) cursor;
|
||||
message.clear();
|
||||
message.squeeze();
|
||||
name.clear();
|
||||
name.squeeze();
|
||||
date.clear();
|
||||
date.squeeze();
|
||||
}
|
||||
|
||||
QString ActionAction::getName()
|
||||
{
|
||||
return QString("<div class=action>*</div>");
|
||||
|
|
|
@ -26,7 +26,7 @@ public:
|
|||
virtual ~ActionAction(){;}
|
||||
virtual QString getMessage();
|
||||
virtual QString getName();
|
||||
virtual void setup(QTextCursor cursor, QTextEdit*) override;
|
||||
virtual void setup(QTextCursor, QTextEdit*) override {;}
|
||||
|
||||
private:
|
||||
QString message;
|
||||
|
|
|
@ -33,6 +33,7 @@ public:
|
|||
virtual QString getName();
|
||||
virtual QString getMessage() = 0;
|
||||
virtual QString getDate();
|
||||
virtual bool isInteractive(){return false;}
|
||||
|
||||
protected:
|
||||
QString toHtmlChars(const QString &str);
|
||||
|
|
|
@ -74,15 +74,15 @@ void FileTransferAction::updateHtml()
|
|||
|
||||
// restore old slider value
|
||||
edit->verticalScrollBar()->setValue(vSliderVal);
|
||||
}
|
||||
|
||||
// Free our ressources if we'll never need to update again
|
||||
bool FileTransferAction::isInteractive()
|
||||
{
|
||||
if (w->getState() == FileTransferInstance::TransfState::tsCanceled
|
||||
|| w->getState() == FileTransferInstance::TransfState::tsFinished)
|
||||
{
|
||||
name.clear();
|
||||
name.squeeze();
|
||||
date.clear();
|
||||
date.squeeze();
|
||||
cur = QTextCursor();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ public:
|
|||
virtual ~FileTransferAction();
|
||||
virtual QString getMessage();
|
||||
virtual void setup(QTextCursor cursor, QTextEdit* textEdit) override;
|
||||
virtual bool isInteractive();
|
||||
|
||||
private slots:
|
||||
void updateHtml();
|
||||
|
|
|
@ -23,21 +23,6 @@ MessageAction::MessageAction(const QString &author, const QString &message, cons
|
|||
{
|
||||
}
|
||||
|
||||
void MessageAction::setup(QTextCursor cursor, QTextEdit *)
|
||||
{
|
||||
// When this function is called, we're supposed to only update ourselve when needed
|
||||
// Nobody should ask us to do anything with our content, we're on our own
|
||||
// Except we never udpate on our own, so we can safely free our resources
|
||||
|
||||
(void) cursor;
|
||||
message.clear();
|
||||
message.squeeze();
|
||||
name.clear();
|
||||
name.squeeze();
|
||||
date.clear();
|
||||
date.squeeze();
|
||||
}
|
||||
|
||||
QString MessageAction::getMessage(QString div)
|
||||
{
|
||||
QString message_ = SmileyPack::getInstance().smileyfied(toHtmlChars(message));
|
||||
|
|
|
@ -26,7 +26,7 @@ public:
|
|||
virtual ~MessageAction(){;}
|
||||
virtual QString getMessage();
|
||||
virtual QString getMessage(QString div);
|
||||
virtual void setup(QTextCursor cursor, QTextEdit*) override;
|
||||
virtual void setup(QTextCursor, QTextEdit*) override {;}
|
||||
|
||||
protected:
|
||||
QString message;
|
||||
|
|
|
@ -27,20 +27,3 @@ QString SystemMessageAction::getMessage()
|
|||
{
|
||||
return QString("<table width=100%><tr><td align=center><div class=" + type + ">" + message + "</td><tr></div></table>");
|
||||
}
|
||||
|
||||
void SystemMessageAction::setup(QTextCursor cursor, QTextEdit *)
|
||||
{
|
||||
// When this function is called, we're supposed to only update ourselve when needed
|
||||
// Nobody should ask us to do anything with our content, we're on our own
|
||||
// Except we never udpate on our own, so we can safely free our resources
|
||||
|
||||
(void) cursor;
|
||||
message.clear();
|
||||
message.squeeze();
|
||||
name.clear();
|
||||
name.squeeze();
|
||||
date.clear();
|
||||
date.squeeze();
|
||||
type.clear();
|
||||
type.squeeze();
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ class SystemMessageAction : public ChatAction
|
|||
public:
|
||||
SystemMessageAction(const QString &message, const QString& type, const QString &date);
|
||||
virtual ~SystemMessageAction(){;}
|
||||
virtual void setup(QTextCursor cursor, QTextEdit*) override;
|
||||
virtual void setup(QTextCursor, QTextEdit*) override {;}
|
||||
|
||||
virtual QString getName() {return QString();}
|
||||
virtual QString getMessage();
|
||||
|
|
|
@ -45,8 +45,9 @@
|
|||
#include <QTimer>
|
||||
#include <QStyleFactory>
|
||||
#include <QTranslator>
|
||||
#include "src/historykeeper.h"
|
||||
#include <tox/tox.h>
|
||||
|
||||
#include "form/inputpassworddialog.h"
|
||||
|
||||
Widget *Widget::instance{nullptr};
|
||||
|
||||
|
@ -181,6 +182,7 @@ void Widget::init()
|
|||
qRegisterMetaType<QPixmap>("QPixmap");
|
||||
qRegisterMetaType<ToxFile>("ToxFile");
|
||||
qRegisterMetaType<ToxFile::FileDirection>("ToxFile::FileDirection");
|
||||
qRegisterMetaType<Core::PasswordType>("Core::PasswordType");
|
||||
|
||||
QString profilePath = detectProfile();
|
||||
coreThread = new QThread(this);
|
||||
|
@ -215,6 +217,7 @@ void Widget::init()
|
|||
connect(core, &Core::emptyGroupCreated, this, &Widget::onEmptyGroupCreated);
|
||||
connect(core, &Core::avInvite, this, &Widget::playRingtone);
|
||||
connect(core, &Core::blockingClearContacts, this, &Widget::clearContactsList, Qt::BlockingQueuedConnection);
|
||||
connect(core, &Core::blockingGetPassword, this, &Widget::getPassword, Qt::BlockingQueuedConnection);
|
||||
|
||||
connect(core, SIGNAL(messageSentResult(int,QString,int)), this, SLOT(onMessageSendResult(int,QString,int)));
|
||||
connect(core, SIGNAL(groupSentResult(int,QString,int)), this, SLOT(onGroupSendResult(int,QString,int)));
|
||||
|
@ -710,7 +713,13 @@ void Widget::onFriendMessageReceived(int friendId, const QString& message, bool
|
|||
if (!f)
|
||||
return;
|
||||
|
||||
f->chatForm->addMessage(f->getName(), message, isAction);
|
||||
QDateTime timestamp = QDateTime::currentDateTime();
|
||||
f->chatForm->addMessage(f->getName(), message, isAction, timestamp);
|
||||
|
||||
if (isAction)
|
||||
HistoryKeeper::getInstance()->addChatEntry(f->userId, "/me " + message, f->userId, timestamp);
|
||||
else
|
||||
HistoryKeeper::getInstance()->addChatEntry(f->userId, message, f->userId, timestamp);
|
||||
|
||||
if (activeChatroomWidget != nullptr)
|
||||
{
|
||||
|
@ -831,9 +840,9 @@ void Widget::onGroupMessageReceived(int groupnumber, const QString& message, con
|
|||
QString name = core->getUsername();
|
||||
bool targeted = (author != name) && message.contains(name, Qt::CaseInsensitive);
|
||||
if (targeted)
|
||||
g->chatForm->addAlertMessage(author, message);
|
||||
g->chatForm->addAlertMessage(author, message, QDateTime::currentDateTime());
|
||||
else
|
||||
g->chatForm->addMessage(author, message);
|
||||
g->chatForm->addMessage(author, message, false, QDateTime::currentDateTime());
|
||||
|
||||
if ((static_cast<GenericChatroomWidget*>(g->widget) != activeChatroomWidget) || isMinimized() || !isActiveWindow())
|
||||
{
|
||||
|
@ -1004,7 +1013,7 @@ void Widget::onMessageSendResult(int friendId, const QString& message, int messa
|
|||
return;
|
||||
|
||||
if (!messageId)
|
||||
f->chatForm->addSystemInfoMessage("Message failed to send", "red");
|
||||
f->chatForm->addSystemInfoMessage("Message failed to send", "red", QDateTime::currentDateTime());
|
||||
}
|
||||
|
||||
void Widget::onGroupSendResult(int groupId, const QString& message, int result)
|
||||
|
@ -1015,5 +1024,19 @@ void Widget::onGroupSendResult(int groupId, const QString& message, int result)
|
|||
return;
|
||||
|
||||
if (result == -1)
|
||||
g->chatForm->addSystemInfoMessage("Message failed to send", "red");
|
||||
g->chatForm->addSystemInfoMessage("Message failed to send", "red", QDateTime::currentDateTime());
|
||||
}
|
||||
|
||||
void Widget::getPassword(QString info, int passtype)
|
||||
{
|
||||
Core::PasswordType pt = static_cast<Core::PasswordType>(passtype);
|
||||
InputPasswordDialog dialog(info);
|
||||
if (dialog.exec())
|
||||
{
|
||||
QString pswd = dialog.getPassword();
|
||||
if (pswd.isEmpty())
|
||||
core->clearPassword(pt);
|
||||
else
|
||||
core->setPassword(pswd, pt);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -98,7 +98,7 @@ private slots:
|
|||
void setStatusMessage(const QString &statusMessage);
|
||||
void addFriend(int friendId, const QString& userId);
|
||||
void addFriendFailed(const QString& userId);
|
||||
void onFriendStatusChanged(int friendId, Status status);
|
||||
void onFriendStatusChanged(int friendId, Status status);
|
||||
void onFriendStatusMessageChanged(int friendId, const QString& message);
|
||||
void onFriendUsernameChanged(int friendId, const QString& username);
|
||||
void onChatroomWidgetClicked(GenericChatroomWidget *);
|
||||
|
@ -119,6 +119,7 @@ private slots:
|
|||
void playRingtone();
|
||||
void onIconClick(QSystemTrayIcon::ActivationReason);
|
||||
void onUserAway();
|
||||
void getPassword(QString info, int passtype);
|
||||
|
||||
private:
|
||||
void init();
|
||||
|
|
Loading…
Reference in New Issue
Block a user