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
|
# just for convenience
|
||||||
BASE_DIR=${SCRIPT_DIR}/${INSTALL_DIR}
|
BASE_DIR=${SCRIPT_DIR}/${INSTALL_DIR}
|
||||||
|
|
||||||
SODIUM_VER=0.7.0
|
SODIUM_VER=1.0.0
|
||||||
|
|
||||||
# directory names of cloned repositories
|
# directory names of cloned repositories
|
||||||
SODIUM_DIR=libsodium-$SODIUM_VER
|
SODIUM_DIR=libsodium-$SODIUM_VER
|
||||||
|
|
33
qtox.pro
33
qtox.pro
|
@ -20,7 +20,7 @@
|
||||||
# See the COPYING file for more details.
|
# 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
|
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||||
|
|
||||||
TARGET = qtox
|
TARGET = qtox
|
||||||
|
@ -29,7 +29,12 @@ FORMS += \
|
||||||
src/mainwindow.ui \
|
src/mainwindow.ui \
|
||||||
src/widget/form/settings/generalsettings.ui \
|
src/widget/form/settings/generalsettings.ui \
|
||||||
src/widget/form/settings/avsettings.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
|
CONFIG += c++11
|
||||||
|
|
||||||
TRANSLATIONS = translations/de.ts \
|
TRANSLATIONS = translations/de.ts \
|
||||||
|
@ -55,28 +60,28 @@ contains(JENKINS,YES) {
|
||||||
|
|
||||||
# Rules for Windows, Mac OSX, and Linux
|
# Rules for Windows, Mac OSX, and Linux
|
||||||
win32 {
|
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 += -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
|
LIBS += -lz -lopengl32 -lole32 -loleaut32 -luuid -lvfw32 -ljpeg -ltiff -lpng -ljasper -lIlmImf -lHalf -lws2_32
|
||||||
} else {
|
} else {
|
||||||
macx {
|
macx {
|
||||||
ICON = img/icons/qtox.icns
|
ICON = img/icons/qtox.icns
|
||||||
LIBS += -L$$PWD/libs/lib/ -ltoxcore -ltoxav -lsodium -lvpx -framework OpenAL -lopencv_core -lopencv_highgui
|
LIBS += -L$$PWD/libs/lib/ -ltoxcore -ltoxav -ltoxencryptsave -lsodium -lvpx -framework OpenAL -lopencv_core -lopencv_highgui
|
||||||
} else {
|
} else {
|
||||||
# If we're building a package, static link libtox[core,av] and libsodium, since they are not provided by any package
|
# If we're building a package, static link libtox[core,av] and libsodium, since they are not provided by any package
|
||||||
contains(STATICPKG, YES) {
|
contains(STATICPKG, YES) {
|
||||||
target.path = /usr/bin
|
target.path = /usr/bin
|
||||||
INSTALLS += target
|
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,-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
|
LIBS += -Wl,-Bdynamic -lv4l1 -lv4l2 -lavformat -lavcodec -lavutil -lswscale -lusb-1.0
|
||||||
|
|
||||||
} else {
|
} 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) {
|
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/videosource.h \
|
||||||
src/cameraworker.h \
|
src/cameraworker.h \
|
||||||
src/widget/videosurface.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
|
src/widget/form/tabcompleter.h
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
|
@ -189,5 +201,12 @@ SOURCES += \
|
||||||
src/widget/maskablepixmapwidget.cpp \
|
src/widget/maskablepixmapwidget.cpp \
|
||||||
src/cameraworker.cpp \
|
src/cameraworker.cpp \
|
||||||
src/widget/videosurface.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/netvideosource.cpp \
|
||||||
src/widget/form/tabcompleter.cpp
|
src/widget/form/tabcompleter.cpp
|
||||||
|
|
209
src/core.cpp
209
src/core.cpp
|
@ -19,8 +19,10 @@
|
||||||
#include "misc/cstring.h"
|
#include "misc/cstring.h"
|
||||||
#include "misc/settings.h"
|
#include "misc/settings.h"
|
||||||
#include "widget/widget.h"
|
#include "widget/widget.h"
|
||||||
|
#include "historykeeper.h"
|
||||||
|
|
||||||
#include <tox/tox.h>
|
#include <tox/tox.h>
|
||||||
|
#include <tox/toxencryptsave.h>
|
||||||
|
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
@ -51,6 +53,9 @@ Core::Core(Camera* cam, QThread *coreThread, QString loadPath) :
|
||||||
videobuf = new uint8_t[videobufsize];
|
videobuf = new uint8_t[videobufsize];
|
||||||
videoBusyness=0;
|
videoBusyness=0;
|
||||||
|
|
||||||
|
for (int i = 0; i < ptCounter; i++)
|
||||||
|
pwsaltedkey[i] = nullptr;
|
||||||
|
|
||||||
toxTimer = new QTimer(this);
|
toxTimer = new QTimer(this);
|
||||||
toxTimer->setSingleShot(true);
|
toxTimer->setSingleShot(true);
|
||||||
connect(toxTimer, &QTimer::timeout, this, &Core::process);
|
connect(toxTimer, &QTimer::timeout, this, &Core::process);
|
||||||
|
@ -113,6 +118,9 @@ Core::~Core()
|
||||||
alcCloseDevice(alOutDev);
|
alcCloseDevice(alOutDev);
|
||||||
if (alInDev)
|
if (alInDev)
|
||||||
alcCaptureCloseDevice(alInDev);
|
alcCaptureCloseDevice(alInDev);
|
||||||
|
|
||||||
|
clearPassword(Core::ptMain);
|
||||||
|
clearPassword(Core::ptHistory);
|
||||||
}
|
}
|
||||||
|
|
||||||
Core* Core::getInstance()
|
Core* Core::getInstance()
|
||||||
|
@ -1092,26 +1100,102 @@ bool Core::loadConfiguration(QString path)
|
||||||
return true;
|
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();
|
qint64 fileSize = configurationFile.size();
|
||||||
if (fileSize > 0) {
|
if (fileSize > 0) {
|
||||||
QByteArray data = configurationFile.readAll();
|
QByteArray data = configurationFile.readAll();
|
||||||
int error = tox_load(tox, reinterpret_cast<uint8_t *>(data.data()), data.size());
|
int error = tox_load(tox, reinterpret_cast<uint8_t *>(data.data()), data.size());
|
||||||
if (error < 0)
|
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
|
else if (error == 1) // Encrypted data save
|
||||||
{
|
{
|
||||||
qWarning() << "Core: Can not open encrypted tox save";
|
do
|
||||||
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 ?"),
|
while (!isPasswordSet(ptMain))
|
||||||
QMessageBox::Ok | QMessageBox::Cancel))
|
{
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
qWarning() << "Core: Couldn't open encrypted save, giving up";
|
|
||||||
configurationFile.close();
|
configurationFile.close();
|
||||||
return false;
|
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();
|
configurationFile.close();
|
||||||
|
@ -1143,22 +1227,18 @@ void Core::saveConfiguration()
|
||||||
}
|
}
|
||||||
|
|
||||||
QString profile = Settings::getInstance().getCurrentProfile();
|
QString profile = Settings::getInstance().getCurrentProfile();
|
||||||
//qDebug() << "saveConf read profile: " << profile;
|
|
||||||
if (profile == "")
|
if (profile == "")
|
||||||
{ // no profile active; this should only happen on startup, if at all
|
{ // no profile active; this should only happen on startup, if at all
|
||||||
profile = sanitize(getUsername());
|
profile = sanitize(getUsername());
|
||||||
|
|
||||||
if (profile == "") // happens on creation of a new Tox ID
|
if (profile == "") // happens on creation of a new Tox ID
|
||||||
profile = getIDString();
|
profile = getIDString();
|
||||||
//qDebug() << "saveConf: read sanitized user as " << profile;
|
|
||||||
Settings::getInstance().setCurrentProfile(profile);
|
Settings::getInstance().setCurrentProfile(profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString path = dir + QDir::separator() + profile + TOX_EXT;
|
QString path = directory.filePath(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";
|
|
||||||
// }
|
|
||||||
|
|
||||||
saveConfiguration(path);
|
saveConfiguration(path);
|
||||||
}
|
}
|
||||||
|
@ -1180,10 +1260,36 @@ void Core::saveConfiguration(const QString& path)
|
||||||
}
|
}
|
||||||
|
|
||||||
qDebug() << "Core: writing tox_save to " << 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) {
|
if (fileSize > 0 && fileSize <= INT32_MAX) {
|
||||||
uint8_t *data = new uint8_t[fileSize];
|
uint8_t *data = new uint8_t[fileSize];
|
||||||
|
|
||||||
|
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);
|
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.write(reinterpret_cast<char *>(data), fileSize);
|
||||||
configurationFile.commit();
|
configurationFile.commit();
|
||||||
delete[] data;
|
delete[] data;
|
||||||
|
@ -1196,8 +1302,10 @@ void Core::switchConfiguration(const QString& profile)
|
||||||
qDebug() << "Core: creating new Id";
|
qDebug() << "Core: creating new Id";
|
||||||
else
|
else
|
||||||
qDebug() << "Core: switching from" << Settings::getInstance().getCurrentProfile() << "to" << profile;
|
qDebug() << "Core: switching from" << Settings::getInstance().getCurrentProfile() << "to" << profile;
|
||||||
saveConfiguration();
|
|
||||||
|
|
||||||
|
saveConfiguration();
|
||||||
|
clearPassword(ptMain);
|
||||||
|
clearPassword(ptHistory);
|
||||||
toxTimer->stop();
|
toxTimer->stop();
|
||||||
|
|
||||||
if (tox) {
|
if (tox) {
|
||||||
|
@ -1214,6 +1322,7 @@ void Core::switchConfiguration(const QString& profile)
|
||||||
else
|
else
|
||||||
loadPath = QDir(Settings::getSettingsDirPath()).filePath(profile + TOX_EXT);
|
loadPath = QDir(Settings::getSettingsDirPath()).filePath(profile + TOX_EXT);
|
||||||
Settings::getInstance().setCurrentProfile(profile);
|
Settings::getInstance().setCurrentProfile(profile);
|
||||||
|
HistoryKeeper::getInstance()->resetInstance();
|
||||||
|
|
||||||
start();
|
start();
|
||||||
}
|
}
|
||||||
|
@ -1494,3 +1603,71 @@ QList<CString> Core::splitMessage(const QString &message)
|
||||||
|
|
||||||
return splittedMsgs;
|
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
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
enum PasswordType {ptMain = 0, ptHistory, ptCounter};
|
||||||
|
|
||||||
explicit Core(Camera* cam, QThread* coreThread, QString initialLoadPath);
|
explicit Core(Camera* cam, QThread* coreThread, QString initialLoadPath);
|
||||||
static Core* getInstance(); ///< Returns the global widget's Core instance
|
static Core* getInstance(); ///< Returns the global widget's Core instance
|
||||||
~Core();
|
~Core();
|
||||||
|
@ -66,6 +68,7 @@ public:
|
||||||
void decreaseVideoBusyness();
|
void decreaseVideoBusyness();
|
||||||
|
|
||||||
bool anyActiveCalls();
|
bool anyActiveCalls();
|
||||||
|
bool isPasswordSet(PasswordType passtype);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void start();
|
void start();
|
||||||
|
@ -106,10 +109,16 @@ public slots:
|
||||||
|
|
||||||
void micMuteToggle(int callId);
|
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:
|
signals:
|
||||||
void connected();
|
void connected();
|
||||||
void disconnected();
|
void disconnected();
|
||||||
void blockingClearContacts();
|
void blockingClearContacts();
|
||||||
|
void blockingGetPassword(QString info, int passtype);
|
||||||
|
|
||||||
void friendRequestReceived(const QString& userId, const QString& message);
|
void friendRequestReceived(const QString& userId, const QString& message);
|
||||||
void friendMessageReceived(int friendId, const QString& message, bool isAction);
|
void friendMessageReceived(int friendId, const QString& message, bool isAction);
|
||||||
|
@ -246,6 +255,9 @@ private:
|
||||||
static QList<ToxFile> fileSendQueue, fileRecvQueue;
|
static QList<ToxFile> fileSendQueue, fileRecvQueue;
|
||||||
static ToxCall calls[];
|
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 const int videobufsize;
|
||||||
static uint8_t* videobuf;
|
static uint8_t* videobuf;
|
||||||
static int videoBusyness; // Used to know when to drop frames
|
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");
|
s.beginGroup("Privacy");
|
||||||
typingNotification = s.value("typingNotification", false).toBool();
|
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.endGroup();
|
||||||
|
|
||||||
s.beginGroup("AutoAccept");
|
s.beginGroup("AutoAccept");
|
||||||
|
@ -268,6 +271,9 @@ void Settings::save(QString path)
|
||||||
|
|
||||||
s.beginGroup("Privacy");
|
s.beginGroup("Privacy");
|
||||||
s.setValue("typingNotification", typingNotification);
|
s.setValue("typingNotification", typingNotification);
|
||||||
|
s.setValue("enableLogging", enableLogging);
|
||||||
|
s.setValue("encryptLogs", encryptLogs);
|
||||||
|
s.setValue("encryptTox", encryptTox);
|
||||||
s.endGroup();
|
s.endGroup();
|
||||||
|
|
||||||
s.beginGroup("AutoAccept");
|
s.beginGroup("AutoAccept");
|
||||||
|
@ -506,6 +512,16 @@ void Settings::setEncryptLogs(bool newValue)
|
||||||
encryptLogs = newValue;
|
encryptLogs = newValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Settings::getEncryptTox() const
|
||||||
|
{
|
||||||
|
return encryptTox;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Settings::setEncryptTox(bool newValue)
|
||||||
|
{
|
||||||
|
encryptTox = newValue;
|
||||||
|
}
|
||||||
|
|
||||||
int Settings::getAutoAwayTime() const
|
int Settings::getAutoAwayTime() const
|
||||||
{
|
{
|
||||||
return autoAwayTime;
|
return autoAwayTime;
|
||||||
|
|
|
@ -85,6 +85,9 @@ public:
|
||||||
bool getEncryptLogs() const;
|
bool getEncryptLogs() const;
|
||||||
void setEncryptLogs(bool newValue);
|
void setEncryptLogs(bool newValue);
|
||||||
|
|
||||||
|
bool getEncryptTox() const;
|
||||||
|
void setEncryptTox(bool newValue);
|
||||||
|
|
||||||
int getAutoAwayTime() const;
|
int getAutoAwayTime() const;
|
||||||
void setAutoAwayTime(int newValue);
|
void setAutoAwayTime(int newValue);
|
||||||
|
|
||||||
|
@ -207,6 +210,7 @@ private:
|
||||||
|
|
||||||
bool enableLogging;
|
bool enableLogging;
|
||||||
bool encryptLogs;
|
bool encryptLogs;
|
||||||
|
bool encryptTox;
|
||||||
|
|
||||||
int autoAwayTime;
|
int autoAwayTime;
|
||||||
|
|
||||||
|
|
|
@ -152,6 +152,7 @@ QTextTable *ChatAreaWidget::getMsgTable()
|
||||||
|
|
||||||
QTextCursor tc = textCursor();
|
QTextCursor tc = textCursor();
|
||||||
tc.movePosition(QTextCursor::End);
|
tc.movePosition(QTextCursor::End);
|
||||||
|
|
||||||
QTextTable *chatTextTable = tc.insertTable(1, 5, *tableFrmt);
|
QTextTable *chatTextTable = tc.insertTable(1, 5, *tableFrmt);
|
||||||
|
|
||||||
return chatTextTable;
|
return chatTextTable;
|
||||||
|
@ -167,3 +168,24 @@ void ChatAreaWidget::setNameColWidth(int w)
|
||||||
|
|
||||||
nameWidth = 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);
|
explicit ChatAreaWidget(QWidget *parent = 0);
|
||||||
virtual ~ChatAreaWidget();
|
virtual ~ChatAreaWidget();
|
||||||
void insertMessage(ChatAction *msgAction);
|
void insertMessage(ChatAction *msgAction);
|
||||||
|
QList<ChatAction*>& getMesages() {return messages;}
|
||||||
|
|
||||||
int nameColWidth() {return nameWidth;}
|
int nameColWidth() {return nameWidth;}
|
||||||
void setNameColWidth(int w);
|
void setNameColWidth(int w);
|
||||||
int getNumberOfMessages();
|
int getNumberOfMessages();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void clearChatArea();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void onFileTranfertInterract(QString widgetName, QString buttonName);
|
void onFileTranfertInterract(QString widgetName, QString buttonName);
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,8 @@
|
||||||
#include <QDragEnterEvent>
|
#include <QDragEnterEvent>
|
||||||
#include <QBitmap>
|
#include <QBitmap>
|
||||||
#include "chatform.h"
|
#include "chatform.h"
|
||||||
|
#include "src/historykeeper.h"
|
||||||
|
#include "src/widget/form/loadhistorydialog.h"
|
||||||
#include "src/friend.h"
|
#include "src/friend.h"
|
||||||
#include "src/widget/friendwidget.h"
|
#include "src/widget/friendwidget.h"
|
||||||
#include "src/filetransferinstance.h"
|
#include "src/filetransferinstance.h"
|
||||||
|
@ -57,6 +59,8 @@ ChatForm::ChatForm(Friend* chatFriend)
|
||||||
headTextLayout->addStretch();
|
headTextLayout->addStretch();
|
||||||
headTextLayout->setSpacing(0);
|
headTextLayout->setSpacing(0);
|
||||||
|
|
||||||
|
menu.addAction(tr("Load History..."), this, SLOT(onLoadHistory()));
|
||||||
|
|
||||||
connect(Core::getInstance(), &Core::fileSendStarted, this, &ChatForm::startFileSend);
|
connect(Core::getInstance(), &Core::fileSendStarted, this, &ChatForm::startFileSend);
|
||||||
connect(sendButton, &QPushButton::clicked, this, &ChatForm::onSendTriggered);
|
connect(sendButton, &QPushButton::clicked, this, &ChatForm::onSendTriggered);
|
||||||
connect(fileButton, &QPushButton::clicked, this, &ChatForm::onAttachClicked);
|
connect(fileButton, &QPushButton::clicked, this, &ChatForm::onAttachClicked);
|
||||||
|
@ -87,15 +91,18 @@ void ChatForm::onSendTriggered()
|
||||||
if (msg.isEmpty())
|
if (msg.isEmpty())
|
||||||
return;
|
return;
|
||||||
QString name = Widget::getInstance()->getUsername();
|
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);
|
msg = msg.right(msg.length() - 4);
|
||||||
addMessage(name, msg, true);
|
addMessage(name, msg, true, timestamp);
|
||||||
emit sendAction(f->friendId, msg);
|
emit sendAction(f->friendId, msg);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
addMessage(name, msg, false);
|
addMessage(name, msg, false, timestamp);
|
||||||
emit sendMessage(f->friendId, msg);
|
emit sendMessage(f->friendId, msg);
|
||||||
}
|
}
|
||||||
msgEdit->clear();
|
msgEdit->clear();
|
||||||
|
@ -502,7 +509,7 @@ void ChatForm::onFileSendFailed(int FriendId, const QString &fname)
|
||||||
if (FriendId != f->friendId)
|
if (FriendId != f->friendId)
|
||||||
return;
|
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)
|
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);
|
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 onCancelCallTriggered();
|
||||||
void onFileTansBtnClicked(QString widgetName, QString buttonName);
|
void onFileTansBtnClicked(QString widgetName, QString buttonName);
|
||||||
void onFileSendFailed(int FriendId, const QString &fname);
|
void onFileSendFailed(int FriendId, const QString &fname);
|
||||||
|
void onLoadHistory();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// drag & drop
|
// drag & drop
|
||||||
|
|
|
@ -31,7 +31,8 @@
|
||||||
#include "src/widget/maskablepixmapwidget.h"
|
#include "src/widget/maskablepixmapwidget.h"
|
||||||
|
|
||||||
GenericChatForm::GenericChatForm(QWidget *parent) :
|
GenericChatForm::GenericChatForm(QWidget *parent) :
|
||||||
QWidget(parent)
|
QWidget(parent),
|
||||||
|
earliestMessage(nullptr)
|
||||||
{
|
{
|
||||||
curRow = 0;
|
curRow = 0;
|
||||||
|
|
||||||
|
@ -116,6 +117,10 @@ GenericChatForm::GenericChatForm(QWidget *parent) :
|
||||||
fileButton->setAttribute(Qt::WA_LayoutUsesWidgetRect);
|
fileButton->setAttribute(Qt::WA_LayoutUsesWidgetRect);
|
||||||
emoteButton->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(emoteButton, SIGNAL(clicked()), this, SLOT(onEmoteButtonClicked()));
|
||||||
connect(chatWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onChatContextMenuRequested(QPoint)));
|
connect(chatWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onChatContextMenuRequested(QPoint)));
|
||||||
|
|
||||||
|
@ -147,8 +152,6 @@ void GenericChatForm::onChatContextMenuRequested(QPoint pos)
|
||||||
{
|
{
|
||||||
QWidget* sender = (QWidget*)QObject::sender();
|
QWidget* sender = (QWidget*)QObject::sender();
|
||||||
pos = sender->mapToGlobal(pos);
|
pos = sender->mapToGlobal(pos);
|
||||||
QMenu menu;
|
|
||||||
menu.addAction(tr("Save chat log"), this, SLOT(onSaveLogClicked()));
|
|
||||||
menu.exec(pos);
|
menu.exec(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,29 +172,10 @@ void GenericChatForm::onSaveLogClicked()
|
||||||
file.close();
|
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());
|
ChatAction *ca = genMessageActionAction(author, message, isAction, datetime);
|
||||||
bool isMe = (author == Widget::getInstance()->getUsername());
|
chatWidget->insertMessage(ca);
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GenericChatForm::addAlertMessage(QString author, QString message, QDateTime datetime)
|
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)
|
void GenericChatForm::addSystemInfoMessage(const QString &message, const QString &type, const QDateTime &datetime)
|
||||||
{
|
{
|
||||||
previousName = "";
|
ChatAction *ca = genSystemInfoAction(message, type, datetime);
|
||||||
QString date = datetime.toString(Settings::getInstance().getTimestampFormat());
|
chatWidget->insertMessage(ca);
|
||||||
|
|
||||||
chatWidget->insertMessage(new SystemMessageAction(message, type, date));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString GenericChatForm::getElidedName(const QString& name)
|
QString GenericChatForm::getElidedName(const QString& name)
|
||||||
|
@ -248,3 +230,58 @@ QString GenericChatForm::getElidedName(const QString& name)
|
||||||
|
|
||||||
return fm.elidedText(name, Qt::ElideRight, chatWidget->nameColWidth());
|
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 <QWidget>
|
||||||
#include <QPoint>
|
#include <QPoint>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
|
#include <QMenu>
|
||||||
|
|
||||||
// Spacing in px inserted when the author of the last message changes
|
// 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?
|
#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 ChatTextEdit;
|
||||||
class ChatAreaWidget;
|
class ChatAreaWidget;
|
||||||
class MaskablePixmapWidget;
|
class MaskablePixmapWidget;
|
||||||
|
class ChatAction;
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class MainWindow;
|
class MainWindow;
|
||||||
|
@ -44,9 +46,9 @@ public:
|
||||||
|
|
||||||
virtual void setName(const QString &newName);
|
virtual void setName(const QString &newName);
|
||||||
virtual void show(Ui::MainWindow &ui);
|
virtual void show(Ui::MainWindow &ui);
|
||||||
void addMessage(QString author, QString message, bool isAction = false, QDateTime datetime=QDateTime::currentDateTime());
|
void addMessage(const QString &author, const QString &message, bool isAction, const QDateTime &datetime);
|
||||||
void addAlertMessage(QString author, QString message, QDateTime datetime=QDateTime::currentDateTime());
|
void addSystemInfoMessage(const QString &message, const QString &type, const QDateTime &datetime);
|
||||||
void addSystemInfoMessage(const QString &message, const QString &type, const QDateTime &datetime=QDateTime::currentDateTime());
|
void addAlertMessage(QString author, QString message, QDateTime datetime);
|
||||||
int getNumberOfMessages();
|
int getNumberOfMessages();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
@ -61,10 +63,14 @@ protected slots:
|
||||||
void onSaveLogClicked();
|
void onSaveLogClicked();
|
||||||
void onEmoteButtonClicked();
|
void onEmoteButtonClicked();
|
||||||
void onEmoteInsertRequested(QString str);
|
void onEmoteInsertRequested(QString str);
|
||||||
|
void clearChatArea(bool);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QString getElidedName(const QString& name);
|
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;
|
CroppingLabel *nameLabel;
|
||||||
MaskablePixmapWidget *avatar;
|
MaskablePixmapWidget *avatar;
|
||||||
QWidget *headWidget;
|
QWidget *headWidget;
|
||||||
|
@ -75,6 +81,7 @@ protected:
|
||||||
QString previousName;
|
QString previousName;
|
||||||
ChatAreaWidget *chatWidget;
|
ChatAreaWidget *chatWidget;
|
||||||
int curRow;
|
int curRow;
|
||||||
|
QDateTime *earliestMessage;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // GENERICCHATFORM_H
|
#endif // GENERICCHATFORM_H
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QMimeData>
|
#include <QMimeData>
|
||||||
#include <QDragEnterEvent>
|
#include <QDragEnterEvent>
|
||||||
|
#include "src/historykeeper.h"
|
||||||
|
|
||||||
GroupChatForm::GroupChatForm(Group* chatGroup)
|
GroupChatForm::GroupChatForm(Group* chatGroup)
|
||||||
: 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/misc/settings.h"
|
||||||
#include "src/widget/croppinglabel.h"
|
#include "src/widget/croppinglabel.h"
|
||||||
#include "src/widget/widget.h"
|
#include "src/widget/widget.h"
|
||||||
|
#include "src/historykeeper.h"
|
||||||
#include "src/misc/style.h"
|
#include "src/misc/style.h"
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QLineEdit>
|
#include <QLineEdit>
|
||||||
|
@ -133,6 +134,7 @@ void IdentityForm::onRenameClicked()
|
||||||
{
|
{
|
||||||
QFile::rename(dir.filePath(cur+Core::TOX_EXT), file);
|
QFile::rename(dir.filePath(cur+Core::TOX_EXT), file);
|
||||||
bodyUI->profiles->setItemText(bodyUI->profiles->currentIndex(), name);
|
bodyUI->profiles->setItemText(bodyUI->profiles->currentIndex(), name);
|
||||||
|
HistoryKeeper::renameHistory(cur, name);
|
||||||
Settings::getInstance().setCurrentProfile(name);
|
Settings::getInstance().setCurrentProfile(name);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,13 +15,121 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "privacyform.h"
|
#include "privacyform.h"
|
||||||
|
#include "ui_privacysettings.h"
|
||||||
#include "src/widget/form/settingswidget.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() :
|
PrivacyForm::PrivacyForm() :
|
||||||
GenericForm(tr("Privacy"), QPixmap(":/img/settings/privacy.png"))
|
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()
|
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"
|
#include "genericsettings.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class PrivacySettings;
|
||||||
|
}
|
||||||
|
|
||||||
class PrivacyForm : public GenericForm
|
class PrivacyForm : public GenericForm
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -26,8 +30,15 @@ public:
|
||||||
PrivacyForm();
|
PrivacyForm();
|
||||||
~PrivacyForm();
|
~PrivacyForm();
|
||||||
|
|
||||||
private:
|
private slots:
|
||||||
|
void onEnableLoggingUpdated();
|
||||||
|
void onTypingNotificationEnabledUpdated();
|
||||||
|
|
||||||
|
void onEncryptLogsUpdated();
|
||||||
|
void onEncryptToxUpdated();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::PrivacySettings* bodyUI;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#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()
|
QString ActionAction::getName()
|
||||||
{
|
{
|
||||||
return QString("<div class=action>*</div>");
|
return QString("<div class=action>*</div>");
|
||||||
|
|
|
@ -26,7 +26,7 @@ public:
|
||||||
virtual ~ActionAction(){;}
|
virtual ~ActionAction(){;}
|
||||||
virtual QString getMessage();
|
virtual QString getMessage();
|
||||||
virtual QString getName();
|
virtual QString getName();
|
||||||
virtual void setup(QTextCursor cursor, QTextEdit*) override;
|
virtual void setup(QTextCursor, QTextEdit*) override {;}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString message;
|
QString message;
|
||||||
|
|
|
@ -33,6 +33,7 @@ public:
|
||||||
virtual QString getName();
|
virtual QString getName();
|
||||||
virtual QString getMessage() = 0;
|
virtual QString getMessage() = 0;
|
||||||
virtual QString getDate();
|
virtual QString getDate();
|
||||||
|
virtual bool isInteractive(){return false;}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QString toHtmlChars(const QString &str);
|
QString toHtmlChars(const QString &str);
|
||||||
|
|
|
@ -74,15 +74,15 @@ void FileTransferAction::updateHtml()
|
||||||
|
|
||||||
// restore old slider value
|
// restore old slider value
|
||||||
edit->verticalScrollBar()->setValue(vSliderVal);
|
edit->verticalScrollBar()->setValue(vSliderVal);
|
||||||
|
}
|
||||||
|
|
||||||
// Free our ressources if we'll never need to update again
|
bool FileTransferAction::isInteractive()
|
||||||
|
{
|
||||||
if (w->getState() == FileTransferInstance::TransfState::tsCanceled
|
if (w->getState() == FileTransferInstance::TransfState::tsCanceled
|
||||||
|| w->getState() == FileTransferInstance::TransfState::tsFinished)
|
|| w->getState() == FileTransferInstance::TransfState::tsFinished)
|
||||||
{
|
{
|
||||||
name.clear();
|
return false;
|
||||||
name.squeeze();
|
|
||||||
date.clear();
|
|
||||||
date.squeeze();
|
|
||||||
cur = QTextCursor();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ public:
|
||||||
virtual ~FileTransferAction();
|
virtual ~FileTransferAction();
|
||||||
virtual QString getMessage();
|
virtual QString getMessage();
|
||||||
virtual void setup(QTextCursor cursor, QTextEdit* textEdit) override;
|
virtual void setup(QTextCursor cursor, QTextEdit* textEdit) override;
|
||||||
|
virtual bool isInteractive();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void updateHtml();
|
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 MessageAction::getMessage(QString div)
|
||||||
{
|
{
|
||||||
QString message_ = SmileyPack::getInstance().smileyfied(toHtmlChars(message));
|
QString message_ = SmileyPack::getInstance().smileyfied(toHtmlChars(message));
|
||||||
|
|
|
@ -26,7 +26,7 @@ public:
|
||||||
virtual ~MessageAction(){;}
|
virtual ~MessageAction(){;}
|
||||||
virtual QString getMessage();
|
virtual QString getMessage();
|
||||||
virtual QString getMessage(QString div);
|
virtual QString getMessage(QString div);
|
||||||
virtual void setup(QTextCursor cursor, QTextEdit*) override;
|
virtual void setup(QTextCursor, QTextEdit*) override {;}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QString message;
|
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>");
|
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:
|
public:
|
||||||
SystemMessageAction(const QString &message, const QString& type, const QString &date);
|
SystemMessageAction(const QString &message, const QString& type, const QString &date);
|
||||||
virtual ~SystemMessageAction(){;}
|
virtual ~SystemMessageAction(){;}
|
||||||
virtual void setup(QTextCursor cursor, QTextEdit*) override;
|
virtual void setup(QTextCursor, QTextEdit*) override {;}
|
||||||
|
|
||||||
virtual QString getName() {return QString();}
|
virtual QString getName() {return QString();}
|
||||||
virtual QString getMessage();
|
virtual QString getMessage();
|
||||||
|
|
|
@ -45,8 +45,9 @@
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QStyleFactory>
|
#include <QStyleFactory>
|
||||||
#include <QTranslator>
|
#include <QTranslator>
|
||||||
|
#include "src/historykeeper.h"
|
||||||
#include <tox/tox.h>
|
#include <tox/tox.h>
|
||||||
|
#include "form/inputpassworddialog.h"
|
||||||
|
|
||||||
Widget *Widget::instance{nullptr};
|
Widget *Widget::instance{nullptr};
|
||||||
|
|
||||||
|
@ -181,6 +182,7 @@ void Widget::init()
|
||||||
qRegisterMetaType<QPixmap>("QPixmap");
|
qRegisterMetaType<QPixmap>("QPixmap");
|
||||||
qRegisterMetaType<ToxFile>("ToxFile");
|
qRegisterMetaType<ToxFile>("ToxFile");
|
||||||
qRegisterMetaType<ToxFile::FileDirection>("ToxFile::FileDirection");
|
qRegisterMetaType<ToxFile::FileDirection>("ToxFile::FileDirection");
|
||||||
|
qRegisterMetaType<Core::PasswordType>("Core::PasswordType");
|
||||||
|
|
||||||
QString profilePath = detectProfile();
|
QString profilePath = detectProfile();
|
||||||
coreThread = new QThread(this);
|
coreThread = new QThread(this);
|
||||||
|
@ -215,6 +217,7 @@ void Widget::init()
|
||||||
connect(core, &Core::emptyGroupCreated, this, &Widget::onEmptyGroupCreated);
|
connect(core, &Core::emptyGroupCreated, this, &Widget::onEmptyGroupCreated);
|
||||||
connect(core, &Core::avInvite, this, &Widget::playRingtone);
|
connect(core, &Core::avInvite, this, &Widget::playRingtone);
|
||||||
connect(core, &Core::blockingClearContacts, this, &Widget::clearContactsList, Qt::BlockingQueuedConnection);
|
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(messageSentResult(int,QString,int)), this, SLOT(onMessageSendResult(int,QString,int)));
|
||||||
connect(core, SIGNAL(groupSentResult(int,QString,int)), this, SLOT(onGroupSendResult(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)
|
if (!f)
|
||||||
return;
|
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)
|
if (activeChatroomWidget != nullptr)
|
||||||
{
|
{
|
||||||
|
@ -831,9 +840,9 @@ void Widget::onGroupMessageReceived(int groupnumber, const QString& message, con
|
||||||
QString name = core->getUsername();
|
QString name = core->getUsername();
|
||||||
bool targeted = (author != name) && message.contains(name, Qt::CaseInsensitive);
|
bool targeted = (author != name) && message.contains(name, Qt::CaseInsensitive);
|
||||||
if (targeted)
|
if (targeted)
|
||||||
g->chatForm->addAlertMessage(author, message);
|
g->chatForm->addAlertMessage(author, message, QDateTime::currentDateTime());
|
||||||
else
|
else
|
||||||
g->chatForm->addMessage(author, message);
|
g->chatForm->addMessage(author, message, false, QDateTime::currentDateTime());
|
||||||
|
|
||||||
if ((static_cast<GenericChatroomWidget*>(g->widget) != activeChatroomWidget) || isMinimized() || !isActiveWindow())
|
if ((static_cast<GenericChatroomWidget*>(g->widget) != activeChatroomWidget) || isMinimized() || !isActiveWindow())
|
||||||
{
|
{
|
||||||
|
@ -1004,7 +1013,7 @@ void Widget::onMessageSendResult(int friendId, const QString& message, int messa
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!messageId)
|
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)
|
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;
|
return;
|
||||||
|
|
||||||
if (result == -1)
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -119,6 +119,7 @@ private slots:
|
||||||
void playRingtone();
|
void playRingtone();
|
||||||
void onIconClick(QSystemTrayIcon::ActivationReason);
|
void onIconClick(QSystemTrayIcon::ActivationReason);
|
||||||
void onUserAway();
|
void onUserAway();
|
||||||
|
void getPassword(QString info, int passtype);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void init();
|
void init();
|
||||||
|
|
Loading…
Reference in New Issue
Block a user