mirror of
https://github.com/qTox/qTox.git
synced 2024-03-22 14:00:36 +08:00
Merge branch 'encryption' into master
Conflicts: src/autoupdate.cpp src/core.cpp src/core.h src/misc/settings.cpp src/widget/widget.cpp
This commit is contained in:
commit
2dc5cea1d2
6
qtox.pro
6
qtox.pro
|
@ -32,7 +32,6 @@ FORMS += \
|
||||||
src/widget/form/settings/identitysettings.ui \
|
src/widget/form/settings/identitysettings.ui \
|
||||||
src/widget/form/settings/privacysettings.ui \
|
src/widget/form/settings/privacysettings.ui \
|
||||||
src/widget/form/loadhistorydialog.ui \
|
src/widget/form/loadhistorydialog.ui \
|
||||||
src/widget/form/inputpassworddialog.ui \
|
|
||||||
src/widget/form/setpassworddialog.ui \
|
src/widget/form/setpassworddialog.ui \
|
||||||
src/widget/form/settings/advancedsettings.ui
|
src/widget/form/settings/advancedsettings.ui
|
||||||
|
|
||||||
|
@ -99,7 +98,7 @@ win32 {
|
||||||
target.path = /usr/bin
|
target.path = /usr/bin
|
||||||
INSTALLS += target
|
INSTALLS += target
|
||||||
LIBS += -L$$PWD/libs/lib/ -lopus -lvpx -lopenal -Wl,-Bstatic -ltoxcore -ltoxav -ltoxencryptsave -ltoxdns -lsodium -lopencv_highgui -lopencv_imgproc -lopencv_core -lz -Wl,-Bdynamic
|
LIBS += -L$$PWD/libs/lib/ -lopus -lvpx -lopenal -Wl,-Bstatic -ltoxcore -ltoxav -ltoxencryptsave -ltoxdns -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 -ltoxencryptsave -ltoxdns -lvpx -lsodium -lopenal -lopencv_core -lopencv_highgui -lopencv_imgproc
|
LIBS += -L$$PWD/libs/lib/ -ltoxcore -ltoxav -ltoxencryptsave -ltoxdns -lvpx -lsodium -lopenal -lopencv_core -lopencv_highgui -lopencv_imgproc
|
||||||
|
@ -176,7 +175,6 @@ HEADERS += src/widget/form/addfriendform.h \
|
||||||
src/misc/db/genericddinterface.h \
|
src/misc/db/genericddinterface.h \
|
||||||
src/misc/db/plaindb.h \
|
src/misc/db/plaindb.h \
|
||||||
src/misc/db/encrypteddb.h \
|
src/misc/db/encrypteddb.h \
|
||||||
src/widget/form/inputpassworddialog.h \
|
|
||||||
src/widget/form/setpassworddialog.h \
|
src/widget/form/setpassworddialog.h \
|
||||||
src/widget/form/tabcompleter.h \
|
src/widget/form/tabcompleter.h \
|
||||||
src/video/videoframe.h \
|
src/video/videoframe.h \
|
||||||
|
@ -207,6 +205,7 @@ SOURCES += \
|
||||||
src/widget/groupwidget.cpp \
|
src/widget/groupwidget.cpp \
|
||||||
src/widget/widget.cpp \
|
src/widget/widget.cpp \
|
||||||
src/core.cpp \
|
src/core.cpp \
|
||||||
|
src/coreencryption.cpp \
|
||||||
src/friend.cpp \
|
src/friend.cpp \
|
||||||
src/friendlist.cpp \
|
src/friendlist.cpp \
|
||||||
src/group.cpp \
|
src/group.cpp \
|
||||||
|
@ -243,7 +242,6 @@ SOURCES += \
|
||||||
src/misc/db/genericddinterface.cpp \
|
src/misc/db/genericddinterface.cpp \
|
||||||
src/misc/db/plaindb.cpp \
|
src/misc/db/plaindb.cpp \
|
||||||
src/misc/db/encrypteddb.cpp \
|
src/misc/db/encrypteddb.cpp \
|
||||||
src/widget/form/inputpassworddialog.cpp \
|
|
||||||
src/widget/form/setpassworddialog.cpp \
|
src/widget/form/setpassworddialog.cpp \
|
||||||
src/video/netvideosource.cpp \
|
src/video/netvideosource.cpp \
|
||||||
src/widget/form/tabcompleter.cpp \
|
src/widget/form/tabcompleter.cpp \
|
||||||
|
|
|
@ -490,8 +490,8 @@ void AutoUpdater::checkUpdatesAsyncInteractiveWorker()
|
||||||
QDir updateDir(updateDirStr);
|
QDir updateDir(updateDirStr);
|
||||||
|
|
||||||
if ((updateDir.exists() && QFile(updateDirStr+"flist").exists())
|
if ((updateDir.exists() && QFile(updateDirStr+"flist").exists())
|
||||||
|| Widget::getInstance()->askMsgboxQuestion(QObject::tr("Update", "The title of a message box"),
|
|| Widget::getInstance()->askQuestion(QObject::tr("Update", "The title of a message box"),
|
||||||
QObject::tr("An update is available, do you want to download it now?\nIt will be installed when qTox restarts.")))
|
QObject::tr("An update is available, do you want to download it now?\nIt will be installed when qTox restarts."), true, false))
|
||||||
{
|
{
|
||||||
downloadUpdate();
|
downloadUpdate();
|
||||||
}
|
}
|
||||||
|
|
332
src/core.cpp
332
src/core.cpp
|
@ -23,7 +23,6 @@
|
||||||
#include "src/audio.h"
|
#include "src/audio.h"
|
||||||
|
|
||||||
#include <tox/tox.h>
|
#include <tox/tox.h>
|
||||||
#include <tox/toxencryptsave.h>
|
|
||||||
|
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
@ -39,7 +38,6 @@
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QBuffer>
|
#include <QBuffer>
|
||||||
#include <QMessageBox>
|
|
||||||
#include <QMutexLocker>
|
#include <QMutexLocker>
|
||||||
|
|
||||||
const QString Core::CONFIG_FILE_NAME = "data";
|
const QString Core::CONFIG_FILE_NAME = "data";
|
||||||
|
@ -90,13 +88,24 @@ Core::Core(Camera* cam, QThread *CoreThread, QString loadPath) :
|
||||||
Audio::openInput(inDevDescr);
|
Audio::openInput(inDevDescr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Core::deadifyTox()
|
||||||
|
{
|
||||||
|
if (toxav)
|
||||||
|
{
|
||||||
|
toxav_kill(toxav);
|
||||||
|
toxav = nullptr;
|
||||||
|
}
|
||||||
|
if (tox)
|
||||||
|
{
|
||||||
|
tox_kill(tox);
|
||||||
|
tox = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Core::~Core()
|
Core::~Core()
|
||||||
{
|
{
|
||||||
qDebug() << "Deleting Core";
|
qDebug() << "Deleting Core";
|
||||||
|
|
||||||
clearPassword(Core::ptMain);
|
|
||||||
clearPassword(Core::ptHistory);
|
|
||||||
|
|
||||||
toxTimer->stop();
|
toxTimer->stop();
|
||||||
coreThread->exit(0);
|
coreThread->exit(0);
|
||||||
while (coreThread->isRunning())
|
while (coreThread->isRunning())
|
||||||
|
@ -105,13 +114,7 @@ Core::~Core()
|
||||||
coreThread->wait(500);
|
coreThread->wait(500);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tox)
|
deadifyTox();
|
||||||
{
|
|
||||||
toxav_kill(toxav);
|
|
||||||
toxav = nullptr;
|
|
||||||
tox_kill(tox);
|
|
||||||
tox = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (videobuf)
|
if (videobuf)
|
||||||
{
|
{
|
||||||
|
@ -183,12 +186,9 @@ void Core::make_tox()
|
||||||
{
|
{
|
||||||
if (toxOptions.proxy_type != TOX_PROXY_NONE)
|
if (toxOptions.proxy_type != TOX_PROXY_NONE)
|
||||||
{
|
{
|
||||||
//QMessageBox::critical(Widget::getInstance(), tr("Proxy failure", "popup title"),
|
|
||||||
//tr("toxcore failed to start with your proxy settings. qTox cannot run; please modify your "
|
|
||||||
//"settings and restart.", "popup text"));
|
|
||||||
qCritical() << "Core: bad proxy! no toxcore!";
|
qCritical() << "Core: bad proxy! no toxcore!";
|
||||||
emit badProxy();
|
emit badProxy();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
qCritical() << "Tox core failed to start";
|
qCritical() << "Tox core failed to start";
|
||||||
|
@ -229,14 +229,26 @@ void Core::start()
|
||||||
|
|
||||||
if (loadPath != "")
|
if (loadPath != "")
|
||||||
{
|
{
|
||||||
if (!loadConfiguration(loadPath)) // loadPath is meaningless after this
|
while (!loadConfiguration(loadPath))
|
||||||
{
|
{
|
||||||
qCritical() << "Core: loadConfiguration failed, exiting now";
|
if (loadPath.isEmpty())
|
||||||
emit failedToStart();
|
{
|
||||||
tox_kill(tox);
|
QString profile;
|
||||||
tox = nullptr;
|
if ((profile = loadOldInformation()).isEmpty())
|
||||||
return;
|
{
|
||||||
|
qCritical() << "Core: loadConfiguration failed, exiting now";
|
||||||
|
emit failedToStart();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
loadPath = QDir(Settings::getSettingsDirPath()).filePath(profile + TOX_EXT);
|
||||||
|
Settings::getInstance().switchProfile(profile);
|
||||||
|
HistoryKeeper::resetInstance(); // I'm not actually sure if this is necessary
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
// loadPath is meaningless after this
|
||||||
loadPath = "";
|
loadPath = "";
|
||||||
}
|
}
|
||||||
else // new ID
|
else // new ID
|
||||||
|
@ -292,7 +304,7 @@ void Core::start()
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
qDebug() << "Core: Error loading self avatar";
|
qDebug() << "Core: Error loading self avatar";
|
||||||
|
|
||||||
ready = true;
|
ready = true;
|
||||||
|
|
||||||
process(); // starts its own timer
|
process(); // starts its own timer
|
||||||
|
@ -309,7 +321,7 @@ void Core::start()
|
||||||
|
|
||||||
void Core::process()
|
void Core::process()
|
||||||
{
|
{
|
||||||
if (!tox)
|
if (!isReady())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
static int tolerance = CORE_DISCONNECT_TOLERANCE;
|
static int tolerance = CORE_DISCONNECT_TOLERANCE;
|
||||||
|
@ -501,7 +513,6 @@ void Core::onGroupInvite(Tox*, int friendnumber, uint8_t type, const uint8_t *da
|
||||||
void Core::onGroupMessage(Tox*, int groupnumber, int peernumber, const uint8_t * message, uint16_t length, void *_core)
|
void Core::onGroupMessage(Tox*, int groupnumber, int peernumber, const uint8_t * message, uint16_t length, void *_core)
|
||||||
{
|
{
|
||||||
Core* core = static_cast<Core*>(_core);
|
Core* core = static_cast<Core*>(_core);
|
||||||
|
|
||||||
emit core->groupMessageReceived(groupnumber, peernumber, CString::toString(message, length), false);
|
emit core->groupMessageReceived(groupnumber, peernumber, CString::toString(message, length), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1012,7 +1023,7 @@ void Core::acceptFileRecvRequest(int friendId, int fileNum, QString path)
|
||||||
|
|
||||||
void Core::removeFriend(int friendId, bool fake)
|
void Core::removeFriend(int friendId, bool fake)
|
||||||
{
|
{
|
||||||
if (!tox || fake)
|
if (!isReady() || fake)
|
||||||
return;
|
return;
|
||||||
if (tox_del_friend(tox, friendId) == -1) {
|
if (tox_del_friend(tox, friendId) == -1) {
|
||||||
emit failedToRemoveFriend(friendId);
|
emit failedToRemoveFriend(friendId);
|
||||||
|
@ -1024,7 +1035,7 @@ void Core::removeFriend(int friendId, bool fake)
|
||||||
|
|
||||||
void Core::removeGroup(int groupId, bool fake)
|
void Core::removeGroup(int groupId, bool fake)
|
||||||
{
|
{
|
||||||
if (!tox || fake)
|
if (!isReady() || fake)
|
||||||
return;
|
return;
|
||||||
tox_del_groupchat(tox, groupId);
|
tox_del_groupchat(tox, groupId);
|
||||||
|
|
||||||
|
@ -1162,7 +1173,8 @@ QString Core::sanitize(QString name)
|
||||||
|
|
||||||
bool Core::loadConfiguration(QString path)
|
bool Core::loadConfiguration(QString path)
|
||||||
{
|
{
|
||||||
// setting the profile is now the responsibility of the caller
|
loadPath = ""; // if not empty upon return, then user forgot a password and is switching
|
||||||
|
|
||||||
QFile configurationFile(path);
|
QFile configurationFile(path);
|
||||||
qDebug() << "Core::loadConfiguration: reading from " << path;
|
qDebug() << "Core::loadConfiguration: reading from " << path;
|
||||||
|
|
||||||
|
@ -1186,51 +1198,21 @@ bool Core::loadConfiguration(QString path)
|
||||||
}
|
}
|
||||||
else if (error == 1) // Encrypted data save
|
else if (error == 1) // Encrypted data save
|
||||||
{
|
{
|
||||||
if (!Settings::getInstance().getEncryptTox())
|
if (!loadEncryptedSave(data))
|
||||||
Widget::getInstance()->showWarningMsgBox(tr("Encryption error"), tr("The .tox file is encrypted, but encryption was not checked, continuing regardless."));
|
|
||||||
uint8_t salt[tox_pass_salt_length()];
|
|
||||||
tox_get_salt(reinterpret_cast<uint8_t *>(data.data()), salt);
|
|
||||||
do
|
|
||||||
{
|
{
|
||||||
while (!pwsaltedkeys[ptMain])
|
configurationFile.close();
|
||||||
|
|
||||||
|
QString profile;
|
||||||
|
QMetaObject::invokeMethod(Widget::getInstance(), "askProfiles", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QString, profile));
|
||||||
|
|
||||||
|
if (!profile.isEmpty())
|
||||||
{
|
{
|
||||||
emit blockingGetPassword(tr("Tox datafile decryption password"), ptMain, salt);
|
loadPath = QDir(Settings::getSettingsDirPath()).filePath(profile + TOX_EXT);
|
||||||
if (!pwsaltedkeys[ptMain])
|
Settings::getInstance().switchProfile(profile);
|
||||||
Widget::getInstance()->showWarningMsgBox(tr("Password error"), tr("Failed to setup password.\nEmpty password."));
|
HistoryKeeper::resetInstance();
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
error = tox_encrypted_key_load(tox, reinterpret_cast<uint8_t *>(data.data()), data.size(), pwsaltedkeys[ptMain]);
|
}
|
||||||
if (error != 0)
|
|
||||||
{
|
|
||||||
QMessageBox msgb;
|
|
||||||
msgb.moveToThread(qApp->thread());
|
|
||||||
QPushButton *tryAgain = msgb.addButton(tr("Try Again"), QMessageBox::AcceptRole);
|
|
||||||
QPushButton *cancel = msgb.addButton(tr("Change profile"), QMessageBox::RejectRole);
|
|
||||||
QPushButton *wipe = msgb.addButton(tr("Reinit current profile"), QMessageBox::ActionRole);
|
|
||||||
msgb.setDefaultButton(tryAgain);
|
|
||||||
msgb.setWindowTitle(tr("Password error"));
|
|
||||||
msgb.setText(tr("Wrong password has been entered"));
|
|
||||||
// msgb.setInformativeText(tr(""));
|
|
||||||
|
|
||||||
msgb.exec();
|
|
||||||
|
|
||||||
if (msgb.clickedButton() == tryAgain)
|
|
||||||
clearPassword(ptMain);
|
|
||||||
else if (msgb.clickedButton() == cancel)
|
|
||||||
{
|
|
||||||
configurationFile.close();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if (msgb.clickedButton() == wipe)
|
|
||||||
{
|
|
||||||
clearPassword(ptMain);
|
|
||||||
Settings::getInstance().setEncryptTox(false);
|
|
||||||
error = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
Settings::getInstance().setEncryptTox(true);
|
|
||||||
} while (error != 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
configurationFile.close();
|
configurationFile.close();
|
||||||
|
@ -1239,7 +1221,7 @@ bool Core::loadConfiguration(QString path)
|
||||||
QString name = getUsername();
|
QString name = getUsername();
|
||||||
if (!name.isEmpty())
|
if (!name.isEmpty())
|
||||||
emit usernameSet(name);
|
emit usernameSet(name);
|
||||||
|
|
||||||
QString msg = getStatusMessage();
|
QString msg = getStatusMessage();
|
||||||
if (!msg.isEmpty())
|
if (!msg.isEmpty())
|
||||||
emit statusMessageSet(msg);
|
emit statusMessageSet(msg);
|
||||||
|
@ -1250,54 +1232,7 @@ bool Core::loadConfiguration(QString path)
|
||||||
|
|
||||||
// tox core is already decrypted
|
// tox core is already decrypted
|
||||||
if (Settings::getInstance().getEnableLogging() && Settings::getInstance().getEncryptLogs())
|
if (Settings::getInstance().getEnableLogging() && Settings::getInstance().getEncryptLogs())
|
||||||
{
|
checkEncryptedHistory();
|
||||||
bool error = true;
|
|
||||||
|
|
||||||
// get salt
|
|
||||||
QFile file(HistoryKeeper::getHistoryPath());
|
|
||||||
file.open(QIODevice::ReadOnly);
|
|
||||||
QByteArray data = file.read(tox_pass_encryption_extra_length());
|
|
||||||
file.close();
|
|
||||||
uint8_t salt[tox_pass_salt_length()];
|
|
||||||
int err = tox_get_salt(reinterpret_cast<uint8_t *>(data.data()), salt);
|
|
||||||
if (err)
|
|
||||||
{ // maybe we should handle this better
|
|
||||||
qWarning() << "Core: history db isn't encrypted, but encryption is set!! No history loaded...";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
do
|
|
||||||
{
|
|
||||||
while (!pwsaltedkeys[ptHistory])
|
|
||||||
{
|
|
||||||
emit blockingGetPassword(tr("History Log decryption password"), Core::ptHistory, salt);
|
|
||||||
if (!pwsaltedkeys[ptHistory])
|
|
||||||
Widget::getInstance()->showWarningMsgBox(tr("Password error"), tr("Failed to setup password.\nEmpty password."));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!HistoryKeeper::checkPassword())
|
|
||||||
{
|
|
||||||
if (QMessageBox::Ok == Widget::getInstance()->showWarningMsgBox(tr("Encrypted log"),
|
|
||||||
tr("Your history is encrypted with different password.\nDo you want to try another password?"),
|
|
||||||
QMessageBox::Ok | QMessageBox::Cancel))
|
|
||||||
{
|
|
||||||
error = true;
|
|
||||||
clearPassword(ptHistory);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
error = false;
|
|
||||||
clearPassword(ptHistory);
|
|
||||||
Widget::getInstance()->showWarningMsgBox(tr("History"), tr("Due to incorret password history will be disabled."));
|
|
||||||
Settings::getInstance().setEncryptLogs(false);
|
|
||||||
Settings::getInstance().setEnableLogging(false);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
error = false;
|
|
||||||
}
|
|
||||||
} while (error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadFriends();
|
loadFriends();
|
||||||
return true;
|
return true;
|
||||||
|
@ -1308,13 +1243,16 @@ void Core::saveConfiguration()
|
||||||
if (QThread::currentThread() != coreThread)
|
if (QThread::currentThread() != coreThread)
|
||||||
return (void) QMetaObject::invokeMethod(this, "saveConfiguration");
|
return (void) QMetaObject::invokeMethod(this, "saveConfiguration");
|
||||||
|
|
||||||
|
if (!isReady())
|
||||||
|
return;
|
||||||
|
|
||||||
QString dir = Settings::getSettingsDirPath();
|
QString dir = Settings::getSettingsDirPath();
|
||||||
QDir directory(dir);
|
QDir directory(dir);
|
||||||
if (!directory.exists() && !directory.mkpath(directory.absolutePath())) {
|
if (!directory.exists() && !directory.mkpath(directory.absolutePath())) {
|
||||||
qCritical() << "Error while creating directory " << dir;
|
qCritical() << "Error while creating directory " << dir;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString profile = Settings::getInstance().getCurrentProfile();
|
QString profile = Settings::getInstance().getCurrentProfile();
|
||||||
|
|
||||||
if (profile == "")
|
if (profile == "")
|
||||||
|
@ -1324,74 +1262,14 @@ void Core::saveConfiguration()
|
||||||
if (profile == "") // happens on creation of a new Tox ID
|
if (profile == "") // happens on creation of a new Tox ID
|
||||||
profile = getIDString();
|
profile = getIDString();
|
||||||
|
|
||||||
Settings::getInstance().setCurrentProfile(profile);
|
Settings::getInstance().switchProfile(profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString path = directory.filePath(profile + TOX_EXT);
|
QString path = directory.filePath(profile + TOX_EXT);
|
||||||
|
|
||||||
saveConfiguration(path);
|
saveConfiguration(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::saveConfiguration(const QString& path)
|
|
||||||
{
|
|
||||||
if (QThread::currentThread() != coreThread)
|
|
||||||
return (void) QMetaObject::invokeMethod(this, "saveConfiguration", Q_ARG(const QString&, path));
|
|
||||||
|
|
||||||
if (!tox)
|
|
||||||
{
|
|
||||||
qWarning() << "Core::saveConfiguration: Tox not started, aborting!";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Settings::getInstance().save();
|
|
||||||
|
|
||||||
QSaveFile configurationFile(path);
|
|
||||||
if (!configurationFile.open(QIODevice::WriteOnly))
|
|
||||||
{
|
|
||||||
qCritical() << "File " << path << " cannot be opened";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
qDebug() << "Core: writing tox_save to " << path;
|
|
||||||
|
|
||||||
uint32_t fileSize;
|
|
||||||
bool encrypt = Settings::getInstance().getEncryptTox();
|
|
||||||
if (encrypt)
|
|
||||||
fileSize = tox_encrypted_size(tox);
|
|
||||||
else
|
|
||||||
fileSize = tox_size(tox);
|
|
||||||
|
|
||||||
if (fileSize > 0 && fileSize <= INT32_MAX)
|
|
||||||
{
|
|
||||||
uint8_t *data = new uint8_t[fileSize];
|
|
||||||
if (encrypt)
|
|
||||||
{
|
|
||||||
if (!pwsaltedkeys[ptMain])
|
|
||||||
{
|
|
||||||
Widget::getInstance()->showWarningMsgBox(tr("NO Password"), tr("Will be saved without encryption!"));
|
|
||||||
tox_save(tox, data);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int ret = tox_encrypted_key_save(tox, data, pwsaltedkeys[ptMain]);
|
|
||||||
if (ret == -1)
|
|
||||||
{
|
|
||||||
qCritical() << "Core::saveConfiguration: encryption of save file failed!!!";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
tox_save(tox, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
configurationFile.write(reinterpret_cast<char *>(data), fileSize);
|
|
||||||
configurationFile.commit();
|
|
||||||
delete[] data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::switchConfiguration(const QString& profile)
|
void Core::switchConfiguration(const QString& profile)
|
||||||
{
|
{
|
||||||
if (profile.isEmpty())
|
if (profile.isEmpty())
|
||||||
|
@ -1400,19 +1278,15 @@ void Core::switchConfiguration(const QString& profile)
|
||||||
qDebug() << "Core: switching from" << Settings::getInstance().getCurrentProfile() << "to" << profile;
|
qDebug() << "Core: switching from" << Settings::getInstance().getCurrentProfile() << "to" << profile;
|
||||||
|
|
||||||
saveConfiguration();
|
saveConfiguration();
|
||||||
|
saveCurrentInformation(); // part of a hack, see core.h
|
||||||
|
|
||||||
ready = false;
|
ready = false;
|
||||||
|
Widget::getInstance()->setEnabledThreadsafe(false);
|
||||||
clearPassword(ptMain);
|
clearPassword(ptMain);
|
||||||
clearPassword(ptHistory);
|
clearPassword(ptHistory);
|
||||||
|
|
||||||
toxTimer->stop();
|
toxTimer->stop();
|
||||||
Widget::getInstance()->setEnabledThreadsafe(false);
|
deadifyTox();
|
||||||
if (tox) {
|
|
||||||
toxav_kill(toxav);
|
|
||||||
toxav = nullptr;
|
|
||||||
tox_kill(tox);
|
|
||||||
tox = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
emit selfAvatarChanged(QPixmap(":/img/contact_dark.png"));
|
emit selfAvatarChanged(QPixmap(":/img/contact_dark.png"));
|
||||||
emit blockingClearContacts(); // we need this to block, but signals are required for thread safety
|
emit blockingClearContacts(); // we need this to block, but signals are required for thread safety
|
||||||
|
@ -1422,15 +1296,12 @@ void Core::switchConfiguration(const QString& profile)
|
||||||
else
|
else
|
||||||
loadPath = QDir(Settings::getSettingsDirPath()).filePath(profile + TOX_EXT);
|
loadPath = QDir(Settings::getSettingsDirPath()).filePath(profile + TOX_EXT);
|
||||||
|
|
||||||
// the new profile needs to be set before resetting the settings, so that
|
Settings::getInstance().switchProfile(profile);
|
||||||
// we don't load the old profile's profile.ini
|
HistoryKeeper::resetInstance();
|
||||||
Settings::getInstance().setCurrentProfile(profile);
|
|
||||||
Settings::getInstance().save(false); // save new profile, but don't write old profile info to newprofile.ini
|
|
||||||
Settings::resetInstance();
|
|
||||||
HistoryKeeper::getInstance()->resetInstance();
|
|
||||||
|
|
||||||
start();
|
start();
|
||||||
Widget::getInstance()->setEnabledThreadsafe(true);
|
if (isReady())
|
||||||
|
Widget::getInstance()->setEnabledThreadsafe(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::loadFriends()
|
void Core::loadFriends()
|
||||||
|
@ -1795,69 +1666,6 @@ QList<CString> Core::splitMessage(const QString &message, int maxLen)
|
||||||
return splittedMsgs;
|
return splittedMsgs;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::setPassword(QString& password, PasswordType passtype, uint8_t* salt)
|
|
||||||
{
|
|
||||||
if (password.isEmpty())
|
|
||||||
{
|
|
||||||
clearPassword(passtype);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!pwsaltedkeys[passtype])
|
|
||||||
pwsaltedkeys[passtype] = new uint8_t[tox_pass_key_length()];
|
|
||||||
|
|
||||||
CString str(password);
|
|
||||||
if (salt)
|
|
||||||
tox_derive_key_with_salt(str.data(), str.size(), salt, pwsaltedkeys[passtype]);
|
|
||||||
else
|
|
||||||
tox_derive_key_from_pass(str.data(), str.size(), pwsaltedkeys[passtype]);
|
|
||||||
|
|
||||||
password.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::clearPassword(PasswordType passtype)
|
|
||||||
{
|
|
||||||
if (pwsaltedkeys[passtype])
|
|
||||||
{
|
|
||||||
delete[] pwsaltedkeys[passtype];
|
|
||||||
pwsaltedkeys[passtype] = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray Core::encryptData(const QByteArray& data, PasswordType passtype)
|
|
||||||
{
|
|
||||||
if (!pwsaltedkeys[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(), pwsaltedkeys[passtype], 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 (!pwsaltedkeys[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(), pwsaltedkeys[passtype], decrypted) != sz)
|
|
||||||
{
|
|
||||||
qWarning() << "Core::decryptData: decryption failed";
|
|
||||||
return QByteArray();
|
|
||||||
}
|
|
||||||
return QByteArray(reinterpret_cast<char*>(decrypted), sz);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Core::isPasswordSet(PasswordType passtype)
|
|
||||||
{
|
|
||||||
if (pwsaltedkeys[passtype])
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Core::getPeerName(const ToxID& id) const
|
QString Core::getPeerName(const ToxID& id) const
|
||||||
{
|
{
|
||||||
QString name;
|
QString name;
|
||||||
|
@ -1892,7 +1700,7 @@ QString Core::getPeerName(const ToxID& id) const
|
||||||
|
|
||||||
bool Core::isReady()
|
bool Core::isReady()
|
||||||
{
|
{
|
||||||
return ready;
|
return toxav && tox && ready;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::setNospam(uint32_t nospam)
|
void Core::setNospam(uint32_t nospam)
|
||||||
|
|
28
src/core.h
28
src/core.h
|
@ -46,12 +46,14 @@ public:
|
||||||
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();
|
||||||
|
|
||||||
static const QString TOX_EXT;
|
static const QString TOX_EXT;
|
||||||
static const QString CONFIG_FILE_NAME;
|
static const QString CONFIG_FILE_NAME;
|
||||||
static QString sanitize(QString name);
|
static QString sanitize(QString name);
|
||||||
static QList<CString> splitMessage(const QString &message, int maxLen);
|
static QList<CString> splitMessage(const QString &message, int maxLen);
|
||||||
|
|
||||||
|
static QByteArray getSaltFromFile(QString filename);
|
||||||
|
|
||||||
QString getPeerName(const ToxID& id) const;
|
QString getPeerName(const ToxID& id) const;
|
||||||
|
|
||||||
int getGroupNumberPeers(int groupId) const; ///< Return the number of peers in the group chat on success, or -1 on failure
|
int getGroupNumberPeers(int groupId) const; ///< Return the number of peers in the group chat on success, or -1 on failure
|
||||||
|
@ -64,9 +66,9 @@ public:
|
||||||
bool hasFriendWithPublicKey(const QString &pubkey) const; ///< Check if we have a friend by public key
|
bool hasFriendWithPublicKey(const QString &pubkey) const; ///< Check if we have a friend by public key
|
||||||
int joinGroupchat(int32_t friendNumber, uint8_t type, const uint8_t* pubkey,uint16_t length) const; ///< Accept a groupchat invite
|
int joinGroupchat(int32_t friendNumber, uint8_t type, const uint8_t* pubkey,uint16_t length) const; ///< Accept a groupchat invite
|
||||||
void quitGroupChat(int groupId) const; ///< Quit a groupchat
|
void quitGroupChat(int groupId) const; ///< Quit a groupchat
|
||||||
|
|
||||||
QString getIDString() const; ///< Get the 12 first characters of our Tox ID
|
QString getIDString() const; ///< Get the 12 first characters of our Tox ID
|
||||||
|
|
||||||
QString getUsername() const; ///< Returns our username, or an empty string on failure
|
QString getUsername() const; ///< Returns our username, or an empty string on failure
|
||||||
QString getStatusMessage() const; ///< Returns our status message, or an empty string on failure
|
QString getStatusMessage() const; ///< Returns our status message, or an empty string on failure
|
||||||
ToxID getSelfId() const; ///< Returns our Tox ID
|
ToxID getSelfId() const; ///< Returns our Tox ID
|
||||||
|
@ -83,10 +85,10 @@ public slots:
|
||||||
void start(); ///< Initializes the core, must be called before anything else
|
void start(); ///< Initializes the core, must be called before anything else
|
||||||
void process(); ///< Processes toxcore events and ensure we stay connected, called by its own timer
|
void process(); ///< Processes toxcore events and ensure we stay connected, called by its own timer
|
||||||
void bootstrapDht(); ///< Connects us to the Tox network
|
void bootstrapDht(); ///< Connects us to the Tox network
|
||||||
void switchConfiguration(const QString& profile); ///< Load a different profile and restart the core
|
|
||||||
|
|
||||||
void saveConfiguration();
|
void saveConfiguration();
|
||||||
void saveConfiguration(const QString& path);
|
void saveConfiguration(const QString& path);
|
||||||
|
void switchConfiguration(const QString& profile); ///< Load a different profile and restart the core
|
||||||
|
|
||||||
void acceptFriendRequest(const QString& userId);
|
void acceptFriendRequest(const QString& userId);
|
||||||
void requestFriendship(const QString& friendAddress, const QString& message);
|
void requestFriendship(const QString& friendAddress, const QString& message);
|
||||||
|
@ -137,6 +139,7 @@ public slots:
|
||||||
static bool isGroupCallVolEnabled(int groupId);
|
static bool isGroupCallVolEnabled(int groupId);
|
||||||
|
|
||||||
void setPassword(QString& password, PasswordType passtype, uint8_t* salt = nullptr);
|
void setPassword(QString& password, PasswordType passtype, uint8_t* salt = nullptr);
|
||||||
|
void useOtherPassword(PasswordType type);
|
||||||
void clearPassword(PasswordType passtype);
|
void clearPassword(PasswordType passtype);
|
||||||
QByteArray encryptData(const QByteArray& data, PasswordType passtype);
|
QByteArray encryptData(const QByteArray& data, PasswordType passtype);
|
||||||
QByteArray decryptData(const QByteArray& data, PasswordType passtype);
|
QByteArray decryptData(const QByteArray& data, PasswordType passtype);
|
||||||
|
@ -145,7 +148,6 @@ signals:
|
||||||
void connected();
|
void connected();
|
||||||
void disconnected();
|
void disconnected();
|
||||||
void blockingClearContacts();
|
void blockingClearContacts();
|
||||||
void blockingGetPassword(QString info, int passtype, uint8_t* salt = nullptr);
|
|
||||||
|
|
||||||
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);
|
||||||
|
@ -266,6 +268,8 @@ private:
|
||||||
bool checkConnection();
|
bool checkConnection();
|
||||||
|
|
||||||
bool loadConfiguration(QString path); // Returns false for a critical error, true otherwise
|
bool loadConfiguration(QString path); // Returns false for a critical error, true otherwise
|
||||||
|
bool loadEncryptedSave(QByteArray& data);
|
||||||
|
void checkEncryptedHistory();
|
||||||
void make_tox();
|
void make_tox();
|
||||||
void loadFriends();
|
void loadFriends();
|
||||||
|
|
||||||
|
@ -274,6 +278,8 @@ private:
|
||||||
|
|
||||||
void checkLastOnline(int friendId);
|
void checkLastOnline(int friendId);
|
||||||
|
|
||||||
|
void deadifyTox();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onFileTransferFinished(ToxFile file);
|
void onFileTransferFinished(ToxFile file);
|
||||||
|
|
||||||
|
@ -294,7 +300,17 @@ private:
|
||||||
QMutex fileSendMutex, messageSendMutex;
|
QMutex fileSendMutex, messageSendMutex;
|
||||||
bool ready;
|
bool ready;
|
||||||
|
|
||||||
uint8_t* pwsaltedkeys[PasswordType::ptCounter]; // use the pw's hash as the "pw"
|
uint8_t* pwsaltedkeys[PasswordType::ptCounter] = {nullptr}; // use the pw's hash as the "pw"
|
||||||
|
|
||||||
|
// Hack for reloading current profile if switching to an encrypted one fails.
|
||||||
|
// Testing the passwords before killing the current profile is perfectly doable,
|
||||||
|
// however it would require major refactoring;
|
||||||
|
// the Core class as a whole also requires major refactoring (especially to support multiple IDs at once),
|
||||||
|
// so I'm punting on this until then, when it would get fixed anyways
|
||||||
|
uint8_t* backupkeys[PasswordType::ptCounter] = {nullptr};
|
||||||
|
QString* backupProfile = nullptr;
|
||||||
|
void saveCurrentInformation();
|
||||||
|
QString loadOldInformation();
|
||||||
|
|
||||||
static const int videobufsize;
|
static const int videobufsize;
|
||||||
static uint8_t* videobuf;
|
static uint8_t* videobuf;
|
||||||
|
|
327
src/coreencryption.cpp
Normal file
327
src/coreencryption.cpp
Normal file
|
@ -0,0 +1,327 @@
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* This file is part of the Core class, but was separated for my sanity */
|
||||||
|
/* The load function delegates to loadEncrypted here, and the save function */
|
||||||
|
/* was permanently moved here to handle encryption */
|
||||||
|
|
||||||
|
#include "core.h"
|
||||||
|
#include "src/widget/widget.h"
|
||||||
|
#include <tox/tox.h>
|
||||||
|
#include <tox/toxencryptsave.h>
|
||||||
|
#include "src/misc/settings.h"
|
||||||
|
#include "misc/cstring.h"
|
||||||
|
#include "historykeeper.h"
|
||||||
|
#include <QApplication>
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QSaveFile>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QThread>
|
||||||
|
|
||||||
|
void Core::setPassword(QString& password, PasswordType passtype, uint8_t* salt)
|
||||||
|
{
|
||||||
|
clearPassword(passtype);
|
||||||
|
if (password.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
pwsaltedkeys[passtype] = new uint8_t[tox_pass_key_length()];
|
||||||
|
|
||||||
|
CString str(password);
|
||||||
|
if (salt)
|
||||||
|
tox_derive_key_with_salt(str.data(), str.size(), salt, pwsaltedkeys[passtype]);
|
||||||
|
else
|
||||||
|
tox_derive_key_from_pass(str.data(), str.size(), pwsaltedkeys[passtype]);
|
||||||
|
|
||||||
|
password.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
void Core::useOtherPassword(PasswordType type)
|
||||||
|
{
|
||||||
|
clearPassword(type);
|
||||||
|
pwsaltedkeys[type] = new uint8_t[tox_pass_key_length()];
|
||||||
|
|
||||||
|
PasswordType other = (type == ptMain) ? ptHistory : ptMain;
|
||||||
|
|
||||||
|
std::copy(pwsaltedkeys[other], pwsaltedkeys[other]+tox_pass_key_length(), pwsaltedkeys[type]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::clearPassword(PasswordType passtype)
|
||||||
|
{
|
||||||
|
delete[] pwsaltedkeys[passtype];
|
||||||
|
pwsaltedkeys[passtype] = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// part of a hack, see core.h
|
||||||
|
void Core::saveCurrentInformation()
|
||||||
|
{
|
||||||
|
if (pwsaltedkeys[ptMain])
|
||||||
|
{
|
||||||
|
backupkeys[ptMain] = new uint8_t[tox_pass_key_length()];
|
||||||
|
std::copy(pwsaltedkeys[ptMain], pwsaltedkeys[ptMain]+tox_pass_key_length(), backupkeys[ptMain]);
|
||||||
|
}
|
||||||
|
if (pwsaltedkeys[ptHistory])
|
||||||
|
{
|
||||||
|
backupkeys[ptHistory] = new uint8_t[tox_pass_key_length()];
|
||||||
|
std::copy(pwsaltedkeys[ptHistory], pwsaltedkeys[ptHistory]+tox_pass_key_length(), backupkeys[ptHistory]);
|
||||||
|
}
|
||||||
|
backupProfile = new QString(Settings::getInstance().getCurrentProfile());
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Core::loadOldInformation()
|
||||||
|
{
|
||||||
|
QString out;
|
||||||
|
if (backupProfile)
|
||||||
|
{
|
||||||
|
out = *backupProfile;
|
||||||
|
delete backupProfile;
|
||||||
|
backupProfile = nullptr;
|
||||||
|
}
|
||||||
|
backupProfile = nullptr;
|
||||||
|
clearPassword(ptMain);
|
||||||
|
clearPassword(ptHistory);
|
||||||
|
// we can just copy the pointer, as long as we null out backupkeys
|
||||||
|
// (if backupkeys was null anyways, then this is a null-op)
|
||||||
|
pwsaltedkeys[ptMain] = backupkeys[ptMain];
|
||||||
|
pwsaltedkeys[ptHistory] = backupkeys[ptHistory];
|
||||||
|
backupkeys[ptMain] = nullptr;
|
||||||
|
backupkeys[ptHistory] = nullptr;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray Core::encryptData(const QByteArray& data, PasswordType passtype)
|
||||||
|
{
|
||||||
|
if (!pwsaltedkeys[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(), pwsaltedkeys[passtype], 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 (!pwsaltedkeys[passtype])
|
||||||
|
return QByteArray();
|
||||||
|
int sz = data.size() - tox_pass_encryption_extra_length();
|
||||||
|
uint8_t decrypted[sz];
|
||||||
|
int decr_size = tox_pass_key_decrypt(reinterpret_cast<const uint8_t*>(data.data()), data.size(), pwsaltedkeys[passtype], decrypted);
|
||||||
|
if (decr_size != sz)
|
||||||
|
{
|
||||||
|
qWarning() << "Core::decryptData: decryption failed";
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
|
return QByteArray(reinterpret_cast<char*>(decrypted), sz);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Core::isPasswordSet(PasswordType passtype)
|
||||||
|
{
|
||||||
|
if (pwsaltedkeys[passtype])
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray Core::getSaltFromFile(QString filename)
|
||||||
|
{
|
||||||
|
QFile file(filename);
|
||||||
|
if (!file.open(QIODevice::ReadOnly))
|
||||||
|
{
|
||||||
|
qWarning() << "Core: file" << filename << "doesn't exist";
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
|
QByteArray data = file.read(tox_pass_encryption_extra_length());
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
uint8_t *salt = new uint8_t[tox_pass_salt_length()];
|
||||||
|
int err = tox_get_salt(reinterpret_cast<uint8_t *>(data.data()), salt);
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
qWarning() << "Core: can't get salt from" << filename << "header";
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray res = QByteArray::fromRawData(reinterpret_cast<const char*>(salt), tox_pass_salt_length());
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Core::loadEncryptedSave(QByteArray& data)
|
||||||
|
{
|
||||||
|
if (!Settings::getInstance().getEncryptTox())
|
||||||
|
Widget::getInstance()->showWarningMsgBox(tr("Encryption error"), tr("The .tox file is encrypted, but encryption was not checked, continuing regardless."));
|
||||||
|
|
||||||
|
int error = -1;
|
||||||
|
QString a(tr("Please enter the password for the %1 profile.", "used in load() when no pw is already set").arg(Settings::getInstance().getCurrentProfile()));
|
||||||
|
QString b(tr("The previous password is incorrect; please try again:", "used on retries in load()"));
|
||||||
|
QString dialogtxt;
|
||||||
|
|
||||||
|
if (pwsaltedkeys[ptMain]) // password set, try it
|
||||||
|
{
|
||||||
|
error = tox_encrypted_key_load(tox, reinterpret_cast<uint8_t *>(data.data()), data.size(), pwsaltedkeys[ptMain]);
|
||||||
|
if (!error)
|
||||||
|
{
|
||||||
|
Settings::getInstance().setEncryptTox(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
dialogtxt = tr("The stored profile password failed. Please try another:", "used only when pw set before load() doesn't work");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
dialogtxt = a;
|
||||||
|
|
||||||
|
uint8_t salt[tox_pass_salt_length()];
|
||||||
|
tox_get_salt(reinterpret_cast<uint8_t *>(data.data()), salt);
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
QString pw = Widget::getInstance()->passwordDialog(tr("Change profile"), dialogtxt);
|
||||||
|
|
||||||
|
if (pw.isEmpty())
|
||||||
|
{
|
||||||
|
clearPassword(ptMain);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
setPassword(pw, ptMain, salt);
|
||||||
|
|
||||||
|
error = tox_encrypted_key_load(tox, reinterpret_cast<uint8_t *>(data.data()), data.size(), pwsaltedkeys[ptMain]);
|
||||||
|
dialogtxt = a + "\n" + b;
|
||||||
|
} while (error != 0);
|
||||||
|
|
||||||
|
Settings::getInstance().setEncryptTox(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::checkEncryptedHistory()
|
||||||
|
{
|
||||||
|
QString path = HistoryKeeper::getHistoryPath();
|
||||||
|
bool exists = QFile::exists(path);
|
||||||
|
|
||||||
|
QByteArray salt = getSaltFromFile(path);
|
||||||
|
if (exists && salt.size() == 0)
|
||||||
|
{ // maybe we should handle this better
|
||||||
|
Widget::getInstance()->showWarningMsgBox(tr("Encrypted History"), tr("No encrypted history file found, or it was corrupted.\nHistory will be disabled!"));
|
||||||
|
Settings::getInstance().setEncryptLogs(false);
|
||||||
|
Settings::getInstance().setEnableLogging(false);
|
||||||
|
HistoryKeeper::resetInstance();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString a(tr("Please enter the password for the chat logs for the %1 profile.", "used in load() when no hist pw set").arg(Settings::getInstance().getCurrentProfile()));
|
||||||
|
QString b(tr("The previous password is incorrect; please try again:", "used on retries in load()"));
|
||||||
|
QString dialogtxt;
|
||||||
|
|
||||||
|
if (pwsaltedkeys[ptHistory])
|
||||||
|
{
|
||||||
|
if (!exists || HistoryKeeper::checkPassword())
|
||||||
|
return;
|
||||||
|
dialogtxt = tr("The stored chat log password failed. Please try another:", "used only when pw set before load() doesn't work");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
dialogtxt = a;
|
||||||
|
|
||||||
|
if (pwsaltedkeys[ptMain])
|
||||||
|
{
|
||||||
|
useOtherPassword(ptHistory);
|
||||||
|
if (!exists || HistoryKeeper::checkPassword())
|
||||||
|
{
|
||||||
|
qDebug() << "Core: using main password for history";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
clearPassword(ptHistory);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool error = true;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
QString pw = Widget::getInstance()->passwordDialog(tr("Disable history"), dialogtxt);
|
||||||
|
|
||||||
|
if (pw.isEmpty())
|
||||||
|
{
|
||||||
|
clearPassword(ptHistory);
|
||||||
|
Settings::getInstance().setEncryptLogs(false);
|
||||||
|
Settings::getInstance().setEnableLogging(false);
|
||||||
|
HistoryKeeper::resetInstance();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
setPassword(pw, ptHistory, reinterpret_cast<uint8_t*>(salt.data()));
|
||||||
|
|
||||||
|
error = exists && !HistoryKeeper::checkPassword();
|
||||||
|
dialogtxt = a + "\n" + b;
|
||||||
|
} while (error);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::saveConfiguration(const QString& path)
|
||||||
|
{
|
||||||
|
if (QThread::currentThread() != coreThread)
|
||||||
|
return (void) QMetaObject::invokeMethod(this, "saveConfiguration", Q_ARG(const QString&, path));
|
||||||
|
|
||||||
|
if (!isReady())
|
||||||
|
{
|
||||||
|
qWarning() << "Core::saveConfiguration: Tox not started, aborting!";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSaveFile configurationFile(path);
|
||||||
|
if (!configurationFile.open(QIODevice::WriteOnly)) {
|
||||||
|
qCritical() << "File " << path << " cannot be opened";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "Core: writing tox_save to " << path;
|
||||||
|
|
||||||
|
uint32_t fileSize; bool encrypt = Settings::getInstance().getEncryptTox();
|
||||||
|
if (encrypt)
|
||||||
|
fileSize = tox_encrypted_size(tox);
|
||||||
|
else
|
||||||
|
fileSize = tox_size(tox);
|
||||||
|
|
||||||
|
if (fileSize > 0 && fileSize <= INT32_MAX) {
|
||||||
|
uint8_t *data = new uint8_t[fileSize];
|
||||||
|
|
||||||
|
if (encrypt)
|
||||||
|
{
|
||||||
|
if (!pwsaltedkeys[ptMain])
|
||||||
|
{
|
||||||
|
// probably zero chance event
|
||||||
|
Widget::getInstance()->showWarningMsgBox(tr("NO Password"), tr("Encryption is enabled, but there is no password! Encryption will be disabled."));
|
||||||
|
Settings::getInstance().setEncryptTox(false);
|
||||||
|
tox_save(tox, data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int ret = tox_encrypted_key_save(tox, data, pwsaltedkeys[ptMain]);
|
||||||
|
if (ret == -1)
|
||||||
|
{
|
||||||
|
qCritical() << "Core::saveConfiguration: encryption of save file failed!!!";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
tox_save(tox, data);
|
||||||
|
|
||||||
|
configurationFile.write(reinterpret_cast<char *>(data), fileSize);
|
||||||
|
configurationFile.commit();
|
||||||
|
delete[] data;
|
||||||
|
}
|
||||||
|
|
||||||
|
Settings::getInstance().save();
|
||||||
|
}
|
|
@ -68,20 +68,15 @@ HistoryKeeper *HistoryKeeper::getInstance()
|
||||||
return historyInstance;
|
return historyInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HistoryKeeper::checkPassword()
|
bool HistoryKeeper::checkPassword(int encrypted)
|
||||||
{
|
{
|
||||||
if (Settings::getInstance().getEnableLogging())
|
if (!Settings::getInstance().getEnableLogging() && (encrypted == -1))
|
||||||
{
|
|
||||||
if (Settings::getInstance().getEncryptLogs())
|
|
||||||
{
|
|
||||||
QString dbpath = getHistoryPath();
|
|
||||||
return EncryptedDb::check(dbpath);
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
|
if ((encrypted == 1) || (encrypted == -1 && Settings::getInstance().getEncryptLogs()))
|
||||||
|
return EncryptedDb::check(getHistoryPath(Settings::getInstance().getCurrentProfile(), encrypted));
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
HistoryKeeper::HistoryKeeper(GenericDdInterface *db_) :
|
HistoryKeeper::HistoryKeeper(GenericDdInterface *db_) :
|
||||||
|
@ -132,9 +127,10 @@ HistoryKeeper::HistoryKeeper(GenericDdInterface *db_) :
|
||||||
|
|
||||||
setSyncType(Settings::getInstance().getDbSyncType());
|
setSyncType(Settings::getInstance().getDbSyncType());
|
||||||
|
|
||||||
|
messageID = 0;
|
||||||
QSqlQuery sqlAnswer = db->exec("select seq from sqlite_sequence where name=\"history\";");
|
QSqlQuery sqlAnswer = db->exec("select seq from sqlite_sequence where name=\"history\";");
|
||||||
sqlAnswer.first();
|
if (sqlAnswer.first())
|
||||||
messageID = sqlAnswer.value(0).toInt();
|
messageID = sqlAnswer.value(0).toLongLong();
|
||||||
}
|
}
|
||||||
|
|
||||||
HistoryKeeper::~HistoryKeeper()
|
HistoryKeeper::~HistoryKeeper()
|
||||||
|
@ -142,16 +138,13 @@ HistoryKeeper::~HistoryKeeper()
|
||||||
delete db;
|
delete db;
|
||||||
}
|
}
|
||||||
|
|
||||||
int HistoryKeeper::addChatEntry(const QString& chat, const QString& message, const QString& sender, const QDateTime &dt, bool isSent)
|
qint64 HistoryKeeper::addChatEntry(const QString& chat, const QString& message, const QString& sender, const QDateTime &dt, bool isSent)
|
||||||
{
|
{
|
||||||
int chat_id = getChatID(chat, ctSingle).first;
|
QList<QString> cmds = generateAddChatEntryCmd(chat, message, sender, dt, isSent);
|
||||||
int sender_id = getAliasID(sender);
|
|
||||||
|
|
||||||
db->exec("BEGIN TRANSACTION;");
|
db->exec("BEGIN TRANSACTION;");
|
||||||
db->exec(QString("INSERT INTO history (timestamp, chat_id, sender, message) ") +
|
for (auto &it : cmds)
|
||||||
QString("VALUES (%1, %2, %3, '%4');")
|
db->exec(it);
|
||||||
.arg(dt.toMSecsSinceEpoch()).arg(chat_id).arg(sender_id).arg(wrapMessage(message)));
|
|
||||||
db->exec(QString("INSERT INTO sent_status (status) VALUES (%1);").arg(isSent));
|
|
||||||
db->exec("COMMIT TRANSACTION;");
|
db->exec("COMMIT TRANSACTION;");
|
||||||
|
|
||||||
messageID++;
|
messageID++;
|
||||||
|
@ -190,12 +183,66 @@ QList<HistoryKeeper::HistMessage> HistoryKeeper::getChatHistory(HistoryKeeper::C
|
||||||
|
|
||||||
QDateTime time = QDateTime::fromMSecsSinceEpoch(timeInt);
|
QDateTime time = QDateTime::fromMSecsSinceEpoch(timeInt);
|
||||||
|
|
||||||
res.push_back({id, sender,message,time,isSent});
|
res.push_back(HistMessage(id, "", sender, message, time, isSent));
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QList<HistoryKeeper::HistMessage> HistoryKeeper::exportMessages()
|
||||||
|
{
|
||||||
|
QSqlQuery dbAnswer;
|
||||||
|
dbAnswer = db->exec(QString("SELECT history.id, timestamp, user_id, message, status, name FROM history LEFT JOIN sent_status ON history.id = sent_status.id ") +
|
||||||
|
QString("INNER JOIN aliases ON history.sender = aliases.id INNER JOIN chats ON history.chat_id = chats.id;"));
|
||||||
|
|
||||||
|
QList<HistMessage> res;
|
||||||
|
|
||||||
|
while (dbAnswer.next())
|
||||||
|
{
|
||||||
|
qint64 id = dbAnswer.value(0).toLongLong();
|
||||||
|
qint64 timeInt = dbAnswer.value(1).toLongLong();
|
||||||
|
QString sender = dbAnswer.value(2).toString();
|
||||||
|
QString message = unWrapMessage(dbAnswer.value(3).toString());
|
||||||
|
bool isSent = true;
|
||||||
|
if (!dbAnswer.value(4).isNull())
|
||||||
|
isSent = dbAnswer.value(4).toBool();
|
||||||
|
QString chat = dbAnswer.value(5).toString();
|
||||||
|
QDateTime time = QDateTime::fromMSecsSinceEpoch(timeInt);
|
||||||
|
|
||||||
|
res.push_back(HistMessage(id, chat, sender, message, time, isSent));
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HistoryKeeper::importMessages(const QList<HistoryKeeper::HistMessage> &lst)
|
||||||
|
{
|
||||||
|
db->exec("BEGIN TRANSACTION;");
|
||||||
|
for (const HistMessage &msg : lst)
|
||||||
|
{
|
||||||
|
QList<QString> cmds = generateAddChatEntryCmd(msg.chat, msg.message, msg.sender, msg.timestamp, msg.isSent);
|
||||||
|
for (auto &it : cmds)
|
||||||
|
db->exec(it);
|
||||||
|
|
||||||
|
messageID++;
|
||||||
|
}
|
||||||
|
db->exec("COMMIT TRANSACTION;");
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QString> HistoryKeeper::generateAddChatEntryCmd(const QString& chat, const QString& message, const QString& sender, const QDateTime &dt, bool isSent)
|
||||||
|
{
|
||||||
|
QList<QString> cmds;
|
||||||
|
|
||||||
|
int chat_id = getChatID(chat, ctSingle).first;
|
||||||
|
int sender_id = getAliasID(sender);
|
||||||
|
|
||||||
|
cmds.push_back(QString("INSERT INTO history (timestamp, chat_id, sender, message) VALUES (%1, %2, %3, '%4');")
|
||||||
|
.arg(dt.toMSecsSinceEpoch()).arg(chat_id).arg(sender_id).arg(wrapMessage(message)));
|
||||||
|
cmds.push_back(QString("INSERT INTO sent_status (status) VALUES (%1);").arg(isSent));
|
||||||
|
|
||||||
|
return cmds;
|
||||||
|
}
|
||||||
|
|
||||||
QString HistoryKeeper::wrapMessage(const QString &str)
|
QString HistoryKeeper::wrapMessage(const QString &str)
|
||||||
{
|
{
|
||||||
QString wrappedMessage(str);
|
QString wrappedMessage(str);
|
||||||
|
@ -272,7 +319,7 @@ void HistoryKeeper::resetInstance()
|
||||||
historyInstance = nullptr;
|
historyInstance = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
int HistoryKeeper::addGroupChatEntry(const QString &chat, const QString &message, const QString &sender, const QDateTime &dt)
|
qint64 HistoryKeeper::addGroupChatEntry(const QString &chat, const QString &message, const QString &sender, const QDateTime &dt)
|
||||||
{
|
{
|
||||||
Q_UNUSED(chat)
|
Q_UNUSED(chat)
|
||||||
Q_UNUSED(message)
|
Q_UNUSED(message)
|
||||||
|
@ -342,3 +389,29 @@ void HistoryKeeper::setSyncType(Db::syncType sType)
|
||||||
|
|
||||||
db->exec(QString("PRAGMA synchronous=%1;").arg(syncCmd));
|
db->exec(QString("PRAGMA synchronous=%1;").arg(syncCmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool HistoryKeeper::isFileExist()
|
||||||
|
{
|
||||||
|
QString path = getHistoryPath();
|
||||||
|
QFile file(path);
|
||||||
|
|
||||||
|
return file.exists();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HistoryKeeper::removeHistory(int encrypted)
|
||||||
|
{
|
||||||
|
resetInstance();
|
||||||
|
|
||||||
|
QString path = getHistoryPath(QString(), encrypted);
|
||||||
|
QFile DbFile(path);
|
||||||
|
return DbFile.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<HistoryKeeper::HistMessage> HistoryKeeper::exportMessagesDeleteFile(int encrypted)
|
||||||
|
{
|
||||||
|
auto msgs = getInstance()->exportMessages();
|
||||||
|
qDebug() << "HistoryKeeper: count" << msgs.size() << "messages exported";
|
||||||
|
if (!removeHistory(encrypted))
|
||||||
|
qWarning() << "HistoryKeeper: couldn't delete old log file!";
|
||||||
|
return msgs;
|
||||||
|
}
|
||||||
|
|
|
@ -31,7 +31,11 @@ public:
|
||||||
|
|
||||||
struct HistMessage
|
struct HistMessage
|
||||||
{
|
{
|
||||||
|
HistMessage(qint64 id, QString chat, QString sender, QString message, QDateTime timestamp, bool isSent) :
|
||||||
|
id(id), chat(chat), sender(sender), message(message), timestamp(timestamp), isSent(isSent) {}
|
||||||
|
|
||||||
qint64 id;
|
qint64 id;
|
||||||
|
QString chat;
|
||||||
QString sender;
|
QString sender;
|
||||||
QString message;
|
QString message;
|
||||||
QDateTime timestamp;
|
QDateTime timestamp;
|
||||||
|
@ -44,14 +48,20 @@ public:
|
||||||
static void resetInstance();
|
static void resetInstance();
|
||||||
|
|
||||||
static QString getHistoryPath(QString currentProfile = QString(), int encrypted = -1); // -1 defaults to checking settings, 0 or 1 to specify
|
static QString getHistoryPath(QString currentProfile = QString(), int encrypted = -1); // -1 defaults to checking settings, 0 or 1 to specify
|
||||||
static bool checkPassword();
|
static bool checkPassword(int encrypted = -1);
|
||||||
|
static bool isFileExist();
|
||||||
static void renameHistory(QString from, QString to);
|
static void renameHistory(QString from, QString to);
|
||||||
|
static bool removeHistory(int encrypted = -1);
|
||||||
|
static QList<HistMessage> exportMessagesDeleteFile(int encrypted = -1);
|
||||||
|
|
||||||
int addChatEntry(const QString& chat, const QString& message, const QString& sender, const QDateTime &dt, bool isSent);
|
qint64 addChatEntry(const QString& chat, const QString& message, const QString& sender, const QDateTime &dt, bool isSent);
|
||||||
int addGroupChatEntry(const QString& chat, const QString& message, const QString& sender, const QDateTime &dt);
|
qint64 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);
|
QList<HistMessage> getChatHistory(ChatType ct, const QString &chat, const QDateTime &time_from, const QDateTime &time_to);
|
||||||
void markAsSent(int m_id);
|
void markAsSent(int m_id);
|
||||||
|
|
||||||
|
QList<HistMessage> exportMessages();
|
||||||
|
void importMessages(const QList<HistoryKeeper::HistMessage> &lst);
|
||||||
|
|
||||||
void setSyncType(Db::syncType sType);
|
void setSyncType(Db::syncType sType);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -65,13 +75,14 @@ private:
|
||||||
int getAliasID(const QString &id_str);
|
int getAliasID(const QString &id_str);
|
||||||
QString wrapMessage(const QString &str);
|
QString wrapMessage(const QString &str);
|
||||||
QString unWrapMessage(const QString &str);
|
QString unWrapMessage(const QString &str);
|
||||||
|
QList<QString> generateAddChatEntryCmd(const QString& chat, const QString& message, const QString& sender, const QDateTime &dt, bool isSent);
|
||||||
|
|
||||||
ChatType convertToChatType(int);
|
ChatType convertToChatType(int);
|
||||||
|
|
||||||
GenericDdInterface *db;
|
GenericDdInterface *db;
|
||||||
QMap<QString, int> aliases;
|
QMap<QString, int> aliases;
|
||||||
QMap<QString, QPair<int, ChatType>> chats;
|
QMap<QString, QPair<int, ChatType>> chats;
|
||||||
int messageID;
|
qint64 messageID;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // HISTORYKEEPER_H
|
#endif // HISTORYKEEPER_H
|
||||||
|
|
|
@ -76,7 +76,7 @@ int main(int argc, char *argv[])
|
||||||
if (QDir(Settings::getSettingsDirPath()).exists(profile + ".tox"))
|
if (QDir(Settings::getSettingsDirPath()).exists(profile + ".tox"))
|
||||||
{
|
{
|
||||||
qDebug() << "Setting profile to" << profile;
|
qDebug() << "Setting profile to" << profile;
|
||||||
Settings::getInstance().setCurrentProfile(profile);
|
Settings::getInstance().switchProfile(profile);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
qWarning() << "Warning: -P profile" << profile + ".tox" << "doesn't exist";
|
qWarning() << "Warning: -P profile" << profile + ".tox" << "doesn't exist";
|
||||||
|
|
|
@ -24,28 +24,36 @@
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QSqlError>
|
#include <QSqlError>
|
||||||
|
|
||||||
qint64 EncryptedDb::plainChunkSize = 4096;
|
qint64 EncryptedDb::encryptedChunkSize = 4096;
|
||||||
qint64 EncryptedDb::encryptedChunkSize = EncryptedDb::plainChunkSize + tox_pass_encryption_extra_length();
|
qint64 EncryptedDb::plainChunkSize = EncryptedDb::encryptedChunkSize - tox_pass_encryption_extra_length();
|
||||||
|
|
||||||
EncryptedDb::EncryptedDb(const QString &fname, QList<QString> initList) :
|
EncryptedDb::EncryptedDb(const QString &fname, QList<QString> initList) :
|
||||||
PlainDb(":memory:", initList), encrFile(fname)
|
PlainDb(":memory:", initList), fileName(fname)
|
||||||
{
|
{
|
||||||
QByteArray fileContent;
|
QByteArray fileContent;
|
||||||
if (pullFileContent())
|
if (pullFileContent(fileName, buffer))
|
||||||
{
|
{
|
||||||
chunkPosition = encrFile.size() / encryptedChunkSize;
|
qDebug() << "EncryptedDb: writing old data";
|
||||||
|
encrFile.setFileName(fileName);
|
||||||
encrFile.seek(0);
|
encrFile.open(QIODevice::ReadOnly);
|
||||||
fileContent = encrFile.readAll();
|
fileContent = encrFile.readAll();
|
||||||
} else {
|
chunkPosition = encrFile.size() / encryptedChunkSize;
|
||||||
qWarning() << "corrupted history log file will be wiped!";
|
encrFile.close();
|
||||||
chunkPosition = 0;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
chunkPosition = 0;
|
||||||
|
|
||||||
encrFile.close();
|
encrFile.setFileName(fileName);
|
||||||
encrFile.open(QIODevice::WriteOnly);
|
|
||||||
encrFile.write(fileContent);
|
if (!encrFile.open(QIODevice::WriteOnly))
|
||||||
encrFile.flush();
|
{
|
||||||
|
qWarning() << "can't open file:" << fileName;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
encrFile.write(fileContent);
|
||||||
|
encrFile.flush();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EncryptedDb::~EncryptedDb()
|
EncryptedDb::~EncryptedDb()
|
||||||
|
@ -57,27 +65,34 @@ EncryptedDb::~EncryptedDb()
|
||||||
QSqlQuery EncryptedDb::exec(const QString &query)
|
QSqlQuery EncryptedDb::exec(const QString &query)
|
||||||
{
|
{
|
||||||
QSqlQuery retQSqlQuery = PlainDb::exec(query);
|
QSqlQuery retQSqlQuery = PlainDb::exec(query);
|
||||||
if (query.startsWith("INSERT", Qt::CaseInsensitive))
|
if (checkCmd(query))
|
||||||
appendToEncrypted(query);
|
appendToEncrypted(query);
|
||||||
|
|
||||||
return retQSqlQuery;
|
return retQSqlQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EncryptedDb::pullFileContent()
|
bool EncryptedDb::pullFileContent(const QString &fname, QByteArray &buf)
|
||||||
{
|
{
|
||||||
encrFile.open(QIODevice::ReadOnly);
|
QFile dbFile(fname);
|
||||||
|
if (!dbFile.open(QIODevice::ReadOnly))
|
||||||
|
{
|
||||||
|
qDebug() << "EncryptedDb::pullFileContent: file doesn't exist";
|
||||||
|
buf = QByteArray();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
QByteArray fileContent;
|
QByteArray fileContent;
|
||||||
|
|
||||||
while (!encrFile.atEnd())
|
while (!dbFile.atEnd())
|
||||||
{
|
{
|
||||||
QByteArray encrChunk = encrFile.read(encryptedChunkSize);
|
QByteArray encrChunk = dbFile.read(encryptedChunkSize);
|
||||||
buffer = Core::getInstance()->decryptData(encrChunk, Core::ptHistory);
|
qDebug() << "EncryptedDb::pullFileContent: got chunk:" << encrChunk.size();
|
||||||
if (buffer.size() > 0)
|
buf = Core::getInstance()->decryptData(encrChunk, Core::ptHistory);
|
||||||
|
if (buf.size() > 0)
|
||||||
{
|
{
|
||||||
fileContent += buffer;
|
fileContent += buf;
|
||||||
} else {
|
} else {
|
||||||
qWarning() << "Encrypted history log is corrupted: can't decrypt";
|
qWarning() << "EncryptedDb::pullFileContent: Encrypted history log is corrupted: can't decrypt, will be deleted";
|
||||||
buffer = QByteArray();
|
buf = QByteArray();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,31 +103,17 @@ bool EncryptedDb::pullFileContent()
|
||||||
for (auto ba_line : splittedBA)
|
for (auto ba_line : splittedBA)
|
||||||
{
|
{
|
||||||
QString line = QByteArray::fromBase64(ba_line);
|
QString line = QByteArray::fromBase64(ba_line);
|
||||||
if (line.size() == 0)
|
sqlCmds.append(line);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PlainDb::exec("BEGIN TRANSACTION;");
|
||||||
for (auto line : sqlCmds)
|
for (auto line : sqlCmds)
|
||||||
{
|
{
|
||||||
QSqlQuery r = PlainDb::exec(line);
|
QSqlQuery r = PlainDb::exec(line);
|
||||||
}
|
}
|
||||||
|
PlainDb::exec("COMMIT TRANSACTION;");
|
||||||
|
|
||||||
|
dbFile.close();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -133,6 +134,7 @@ void EncryptedDb::appendToEncrypted(const QString &sql)
|
||||||
if (encr.size() > 0)
|
if (encr.size() > 0)
|
||||||
{
|
{
|
||||||
encrFile.write(encr);
|
encrFile.write(encr);
|
||||||
|
encrFile.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer = buffer.right(buffer.size() - plainChunkSize);
|
buffer = buffer.right(buffer.size() - plainChunkSize);
|
||||||
|
@ -170,3 +172,14 @@ bool EncryptedDb::check(const QString &fname)
|
||||||
file.close();
|
file.close();
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool EncryptedDb::checkCmd(const QString &cmd)
|
||||||
|
{
|
||||||
|
if (cmd.startsWith("INSERT", Qt::CaseInsensitive) || cmd.startsWith("UPDATE", Qt::CaseInsensitive)
|
||||||
|
|| cmd.startsWith("DELETE", Qt::CaseInsensitive))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
|
@ -32,10 +32,12 @@ public:
|
||||||
static bool check(const QString &fname);
|
static bool check(const QString &fname);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool pullFileContent();
|
bool pullFileContent(const QString& fname, QByteArray &buf);
|
||||||
void appendToEncrypted(const QString &sql);
|
void appendToEncrypted(const QString &sql);
|
||||||
|
bool checkCmd(const QString &cmd);
|
||||||
|
|
||||||
QFile encrFile;
|
QFile encrFile;
|
||||||
|
QString fileName;
|
||||||
|
|
||||||
static qint64 plainChunkSize;
|
static qint64 plainChunkSize;
|
||||||
static qint64 encryptedChunkSize;
|
static qint64 encryptedChunkSize;
|
||||||
|
|
|
@ -63,6 +63,13 @@ void Settings::resetInstance()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Settings::switchProfile(const QString& profile)
|
||||||
|
{
|
||||||
|
setCurrentProfile(profile);
|
||||||
|
save(false);
|
||||||
|
resetInstance();
|
||||||
|
}
|
||||||
|
|
||||||
void Settings::load()
|
void Settings::load()
|
||||||
{
|
{
|
||||||
if (loaded)
|
if (loaded)
|
||||||
|
@ -191,13 +198,6 @@ void Settings::load()
|
||||||
splitterState = s.value("splitterState", QByteArray()).toByteArray();
|
splitterState = s.value("splitterState", QByteArray()).toByteArray();
|
||||||
s.endGroup();
|
s.endGroup();
|
||||||
|
|
||||||
s.beginGroup("Privacy");
|
|
||||||
typingNotification = s.value("typingNotification", true).toBool();
|
|
||||||
enableLogging = s.value("enableLogging", false).toBool();
|
|
||||||
encryptLogs = s.value("encryptLogs", false).toBool();
|
|
||||||
encryptTox = s.value("encryptTox", false).toBool();
|
|
||||||
s.endGroup();
|
|
||||||
|
|
||||||
s.beginGroup("Audio");
|
s.beginGroup("Audio");
|
||||||
inDev = s.value("inDev", "").toString();
|
inDev = s.value("inDev", "").toString();
|
||||||
outDev = s.value("outDev", "").toString();
|
outDev = s.value("outDev", "").toString();
|
||||||
|
@ -230,38 +230,45 @@ void Settings::load()
|
||||||
|
|
||||||
loaded = true;
|
loaded = true;
|
||||||
|
|
||||||
if (currentProfile.isEmpty()) // new profile in Core::switchConfiguration
|
if (!currentProfile.isEmpty()) // new profile in Core::switchConfiguration
|
||||||
return;
|
{
|
||||||
|
// load from a profile specific friend data list if possible
|
||||||
|
QString tmp = dir.filePath(currentProfile + ".ini");
|
||||||
|
if (QFile(tmp).exists()) // otherwise, filePath remains the global file
|
||||||
|
filePath = tmp;
|
||||||
|
|
||||||
// load from a profile specific friend data list if possible
|
QSettings ps(filePath, QSettings::IniFormat);
|
||||||
QString tmp = dir.filePath(currentProfile + ".ini");
|
friendLst.clear();
|
||||||
if (QFile(tmp).exists())
|
ps.beginGroup("Friends");
|
||||||
filePath = tmp;
|
int size = ps.beginReadArray("Friend");
|
||||||
|
for (int i = 0; i < size; i ++)
|
||||||
|
{
|
||||||
|
ps.setArrayIndex(i);
|
||||||
|
friendProp fp;
|
||||||
|
fp.addr = ps.value("addr").toString();
|
||||||
|
fp.alias = ps.value("alias").toString();
|
||||||
|
fp.autoAcceptDir = ps.value("autoAcceptDir").toString();
|
||||||
|
friendLst[ToxID::fromString(fp.addr).publicKey] = fp;
|
||||||
|
}
|
||||||
|
ps.endArray();
|
||||||
|
ps.endGroup();
|
||||||
|
|
||||||
QSettings fs(filePath, QSettings::IniFormat);
|
ps.beginGroup("Privacy");
|
||||||
friendLst.clear();
|
typingNotification = ps.value("typingNotification", false).toBool();
|
||||||
fs.beginGroup("Friends");
|
enableLogging = ps.value("enableLogging", false).toBool();
|
||||||
int size = fs.beginReadArray("Friend");
|
encryptLogs = ps.value("encryptLogs", false).toBool();
|
||||||
for (int i = 0; i < size; i ++)
|
encryptTox = ps.value("encryptTox", false).toBool();
|
||||||
{
|
ps.endGroup();
|
||||||
fs.setArrayIndex(i);
|
}
|
||||||
friendProp fp;
|
|
||||||
fp.addr = fs.value("addr").toString();
|
|
||||||
fp.alias = fs.value("alias").toString();
|
|
||||||
fp.autoAcceptDir = fs.value("autoAcceptDir").toString();
|
|
||||||
friendLst[ToxID::fromString(fp.addr).publicKey] = fp;
|
|
||||||
}
|
|
||||||
fs.endArray();
|
|
||||||
fs.endGroup();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Settings::save(bool writeFriends)
|
void Settings::save(bool writePersonal)
|
||||||
{
|
{
|
||||||
QString filePath = QDir(getSettingsDirPath()).filePath(FILENAME);
|
QString filePath = QDir(getSettingsDirPath()).filePath(FILENAME);
|
||||||
save(filePath, writeFriends);
|
save(filePath, writePersonal);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Settings::save(QString path, bool writeFriends)
|
void Settings::save(QString path, bool writePersonal)
|
||||||
{
|
{
|
||||||
qDebug() << "Settings: Saving in "<<path;
|
qDebug() << "Settings: Saving in "<<path;
|
||||||
|
|
||||||
|
@ -341,13 +348,6 @@ void Settings::save(QString path, bool writeFriends)
|
||||||
s.setValue("splitterState", splitterState);
|
s.setValue("splitterState", splitterState);
|
||||||
s.endGroup();
|
s.endGroup();
|
||||||
|
|
||||||
s.beginGroup("Privacy");
|
|
||||||
s.setValue("typingNotification", typingNotification);
|
|
||||||
s.setValue("enableLogging", enableLogging);
|
|
||||||
s.setValue("encryptLogs", encryptLogs);
|
|
||||||
s.setValue("encryptTox", encryptTox);
|
|
||||||
s.endGroup();
|
|
||||||
|
|
||||||
s.beginGroup("Audio");
|
s.beginGroup("Audio");
|
||||||
s.setValue("inDev", inDev);
|
s.setValue("inDev", inDev);
|
||||||
s.setValue("outDev", outDev);
|
s.setValue("outDev", outDev);
|
||||||
|
@ -358,23 +358,30 @@ void Settings::save(QString path, bool writeFriends)
|
||||||
s.setValue("camVideoRes",camVideoRes);
|
s.setValue("camVideoRes",camVideoRes);
|
||||||
s.endGroup();
|
s.endGroup();
|
||||||
|
|
||||||
if (!writeFriends || currentProfile.isEmpty()) // Core::switchConfiguration
|
if (writePersonal && !currentProfile.isEmpty()) // Core::switchConfiguration
|
||||||
return;
|
{
|
||||||
|
QSettings ps(QFileInfo(path).dir().filePath(currentProfile + ".ini"), QSettings::IniFormat);
|
||||||
|
ps.beginGroup("Friends");
|
||||||
|
ps.beginWriteArray("Friend", friendLst.size());
|
||||||
|
int index = 0;
|
||||||
|
for (auto& frnd : friendLst)
|
||||||
|
{
|
||||||
|
ps.setArrayIndex(index);
|
||||||
|
ps.setValue("addr", frnd.addr);
|
||||||
|
ps.setValue("alias", frnd.alias);
|
||||||
|
ps.setValue("autoAcceptDir", frnd.autoAcceptDir);
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
ps.endArray();
|
||||||
|
ps.endGroup();
|
||||||
|
|
||||||
QSettings fs(QFileInfo(path).dir().filePath(currentProfile + ".ini"), QSettings::IniFormat);
|
ps.beginGroup("Privacy");
|
||||||
fs.beginGroup("Friends");
|
ps.setValue("typingNotification", typingNotification);
|
||||||
fs.beginWriteArray("Friend", friendLst.size());
|
ps.setValue("enableLogging", enableLogging);
|
||||||
int index = 0;
|
ps.setValue("encryptLogs", encryptLogs);
|
||||||
for (auto& frnd : friendLst)
|
ps.setValue("encryptTox", encryptTox);
|
||||||
{
|
ps.endGroup();
|
||||||
fs.setArrayIndex(index);
|
}
|
||||||
fs.setValue("addr", frnd.addr);
|
|
||||||
fs.setValue("alias", frnd.alias);
|
|
||||||
fs.setValue("autoAcceptDir", frnd.autoAcceptDir);
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
fs.endArray();
|
|
||||||
fs.endGroup();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Settings::getSettingsDirPath()
|
QString Settings::getSettingsDirPath()
|
||||||
|
|
|
@ -32,6 +32,7 @@ class Settings : public QObject
|
||||||
public:
|
public:
|
||||||
static Settings& getInstance();
|
static Settings& getInstance();
|
||||||
static void resetInstance();
|
static void resetInstance();
|
||||||
|
void switchProfile(const QString& profile);
|
||||||
~Settings() = default;
|
~Settings() = default;
|
||||||
|
|
||||||
void executeSettingsDialog(QWidget* parent);
|
void executeSettingsDialog(QWidget* parent);
|
||||||
|
@ -236,8 +237,8 @@ public:
|
||||||
void setCompactLayout(bool compact);
|
void setCompactLayout(bool compact);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void save(bool writeFriends = true);
|
void save(bool writePersonal = true);
|
||||||
void save(QString path, bool writeFriends = true);
|
void save(QString path, bool writePersonal = true);
|
||||||
void load();
|
void load();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
/*
|
|
||||||
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();
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
/*
|
|
||||||
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
|
|
|
@ -1,97 +0,0 @@
|
||||||
<?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>
|
|
|
@ -18,14 +18,27 @@
|
||||||
#include "ui_setpassworddialog.h"
|
#include "ui_setpassworddialog.h"
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
|
|
||||||
SetPasswordDialog::SetPasswordDialog(QWidget *parent) :
|
const double SetPasswordDialog::reasonablePasswordLength = 8.;
|
||||||
QDialog(parent),
|
|
||||||
ui(new Ui::SetPasswordDialog)
|
SetPasswordDialog::SetPasswordDialog(QString body, QString extraButton, QWidget* parent)
|
||||||
|
: QDialog(parent)
|
||||||
|
, ui(new Ui::SetPasswordDialog)
|
||||||
|
, body(body+"\n")
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
connect(ui->passwordlineEdit, SIGNAL(textChanged(QString)), this, SLOT(onPasswordEdit()));
|
connect(ui->passwordlineEdit, SIGNAL(textChanged(QString)), this, SLOT(onPasswordEdit()));
|
||||||
connect(ui->repasswordlineEdit, SIGNAL(textChanged(QString)), this, SLOT(onPasswordEdit()));
|
connect(ui->repasswordlineEdit, SIGNAL(textChanged(QString)), this, SLOT(onPasswordEdit()));
|
||||||
|
|
||||||
|
ui->body->setText(body + "\n" + tr("The passwords don't match."));
|
||||||
|
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
|
||||||
|
|
||||||
|
if (!extraButton.isEmpty())
|
||||||
|
{
|
||||||
|
QPushButton* third = new QPushButton(extraButton);
|
||||||
|
ui->buttonBox->addButton(third, QDialogButtonBox::YesRole);
|
||||||
|
connect(third, &QPushButton::clicked, this, [=](){this->done(2);});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SetPasswordDialog::~SetPasswordDialog()
|
SetPasswordDialog::~SetPasswordDialog()
|
||||||
|
@ -35,10 +48,53 @@ SetPasswordDialog::~SetPasswordDialog()
|
||||||
|
|
||||||
void SetPasswordDialog::onPasswordEdit()
|
void SetPasswordDialog::onPasswordEdit()
|
||||||
{
|
{
|
||||||
if (ui->passwordlineEdit->text() == ui->repasswordlineEdit->text())
|
QString pswd = ui->passwordlineEdit->text();
|
||||||
|
|
||||||
|
if (pswd == ui->repasswordlineEdit->text() && pswd.length() > 0)
|
||||||
|
{
|
||||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
|
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
|
||||||
|
ui->body->setText(body);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
|
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
|
||||||
|
ui->body->setText(body + tr("The passwords don't match."));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Password strength calculator
|
||||||
|
// Based on code in the Master Password dialog in Firefox
|
||||||
|
// (pref-masterpass.js)
|
||||||
|
// Original code triple-licensed under the MPL, GPL, and LGPL
|
||||||
|
// so is license-compatible with this file
|
||||||
|
|
||||||
|
const double lengthFactor = reasonablePasswordLength / 8.0;
|
||||||
|
int pwlength = (int)(pswd.length() / lengthFactor);
|
||||||
|
if (pwlength > 5)
|
||||||
|
pwlength = 5;
|
||||||
|
|
||||||
|
const QRegExp numRxp("[0-9]", Qt::CaseSensitive, QRegExp::RegExp);
|
||||||
|
int numeric = (int)(pswd.count(numRxp) / lengthFactor);
|
||||||
|
if (numeric > 3)
|
||||||
|
numeric = 3;
|
||||||
|
|
||||||
|
const QRegExp symbRxp("\\W", Qt::CaseInsensitive, QRegExp::RegExp);
|
||||||
|
int numsymbols = (int)(pswd.count(symbRxp) / lengthFactor);
|
||||||
|
if (numsymbols > 3)
|
||||||
|
numsymbols = 3;
|
||||||
|
|
||||||
|
const QRegExp upperRxp("[A-Z]", Qt::CaseSensitive, QRegExp::RegExp);
|
||||||
|
int upper = (int)(pswd.count(upperRxp) / lengthFactor);
|
||||||
|
if (upper > 3)
|
||||||
|
upper = 3;
|
||||||
|
|
||||||
|
int pwstrength=((pwlength*10)-20) + (numeric*10) + (numsymbols*15) + (upper*10);
|
||||||
|
if (pwstrength < 0)
|
||||||
|
pwstrength = 0;
|
||||||
|
|
||||||
|
if (pwstrength > 100)
|
||||||
|
pwstrength = 100;
|
||||||
|
|
||||||
|
ui->strengthBar->setValue(pwstrength);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString SetPasswordDialog::getPassword()
|
QString SetPasswordDialog::getPassword()
|
||||||
|
|
|
@ -28,7 +28,7 @@ class SetPasswordDialog : public QDialog
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit SetPasswordDialog(QWidget *parent = 0);
|
explicit SetPasswordDialog(QString body, QString extraButton, QWidget* parent = 0);
|
||||||
~SetPasswordDialog();
|
~SetPasswordDialog();
|
||||||
QString getPassword();
|
QString getPassword();
|
||||||
|
|
||||||
|
@ -37,6 +37,8 @@ private slots:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::SetPasswordDialog *ui;
|
Ui::SetPasswordDialog *ui;
|
||||||
|
QString body;
|
||||||
|
static const double reasonablePasswordLength;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SETPASSWORDDIALOG_H
|
#endif // SETPASSWORDDIALOG_H
|
||||||
|
|
|
@ -6,44 +6,77 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>434</width>
|
<width>325</width>
|
||||||
<height>175</height>
|
<height>160</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string notr="true">Dialog</string>
|
<string>Set your password</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="modal">
|
<property name="modal">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="label">
|
<widget class="QLabel" name="body"/>
|
||||||
<property name="text">
|
|
||||||
<string>Type Password</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLineEdit" name="passwordlineEdit">
|
<layout class="QGridLayout" name="pswdsLayout">
|
||||||
<property name="echoMode">
|
<item row="4" column="0">
|
||||||
<enum>QLineEdit::Password</enum>
|
<widget class="QLabel" name="label_2">
|
||||||
</property>
|
<property name="alignment">
|
||||||
</widget>
|
<set>Qt::AlignRight</set>
|
||||||
</item>
|
</property>
|
||||||
<item>
|
<property name="text">
|
||||||
<widget class="QLabel" name="label_2">
|
<string>Repeat password</string>
|
||||||
<property name="text">
|
</property>
|
||||||
<string>Repeat Password</string>
|
</widget>
|
||||||
</property>
|
</item>
|
||||||
</widget>
|
<item row="2" column="0">
|
||||||
</item>
|
<widget class="QLabel" name="label">
|
||||||
<item>
|
<property name="alignment">
|
||||||
<widget class="QLineEdit" name="repasswordlineEdit">
|
<set>Qt::AlignRight</set>
|
||||||
<property name="echoMode">
|
</property>
|
||||||
<enum>QLineEdit::Password</enum>
|
<property name="text">
|
||||||
</property>
|
<string>Type password</string>
|
||||||
</widget>
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="1">
|
||||||
|
<widget class="QLineEdit" name="repasswordlineEdit">
|
||||||
|
<property name="echoMode">
|
||||||
|
<enum>QLineEdit::Password</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QLineEdit" name="passwordlineEdit">
|
||||||
|
<property name="echoMode">
|
||||||
|
<enum>QLineEdit::Password</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="0">
|
||||||
|
<widget class="QLabel" name="label_3">
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight</set>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Password strength</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="1">
|
||||||
|
<widget class="QProgressBar" name="strengthBar">
|
||||||
|
<property name="value">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="format">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<spacer name="verticalSpacer">
|
<spacer name="verticalSpacer">
|
||||||
|
@ -73,6 +106,10 @@
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
<tabstops>
|
||||||
|
<tabstop>passwordlineEdit</tabstop>
|
||||||
|
<tabstop>repasswordlineEdit</tabstop>
|
||||||
|
</tabstops>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections>
|
<connections>
|
||||||
<connection>
|
<connection>
|
||||||
|
|
|
@ -29,7 +29,6 @@
|
||||||
#include <QClipboard>
|
#include <QClipboard>
|
||||||
#include <QInputDialog>
|
#include <QInputDialog>
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
#include <QMessageBox>
|
|
||||||
|
|
||||||
IdentityForm::IdentityForm() :
|
IdentityForm::IdentityForm() :
|
||||||
GenericForm(tr("Identity"), QPixmap(":/img/settings/identity.png"))
|
GenericForm(tr("Identity"), QPixmap(":/img/settings/identity.png"))
|
||||||
|
@ -152,10 +151,11 @@ void IdentityForm::onRenameClicked()
|
||||||
name = Core::sanitize(name);
|
name = Core::sanitize(name);
|
||||||
QDir dir(Settings::getSettingsDirPath());
|
QDir dir(Settings::getSettingsDirPath());
|
||||||
QString file = dir.filePath(name+Core::TOX_EXT);
|
QString file = dir.filePath(name+Core::TOX_EXT);
|
||||||
if (!QFile::exists(file) || checkContinue(tr("Profile already exists", "rename confirm title"),
|
if (!QFile::exists(file) || Widget::getInstance()->askQuestion(tr("Profile already exists", "rename confirm title"),
|
||||||
tr("A profile named \"%1\" already exists. Do you want to erase it?", "rename confirm text").arg(cur)))
|
tr("A profile named \"%1\" already exists. Do you want to erase it?", "rename confirm text").arg(cur)))
|
||||||
{
|
{
|
||||||
QFile::rename(dir.filePath(cur+Core::TOX_EXT), file);
|
QFile::rename(dir.filePath(cur+Core::TOX_EXT), file);
|
||||||
|
QFile::rename(dir.filePath(cur+".ini"), dir.filePath(name+".ini"));
|
||||||
bodyUI->profiles->setItemText(bodyUI->profiles->currentIndex(), name);
|
bodyUI->profiles->setItemText(bodyUI->profiles->currentIndex(), name);
|
||||||
HistoryKeeper::renameHistory(cur, name);
|
HistoryKeeper::renameHistory(cur, name);
|
||||||
Settings::getInstance().setCurrentProfile(name);
|
Settings::getInstance().setCurrentProfile(name);
|
||||||
|
@ -176,8 +176,6 @@ void IdentityForm::onExportClicked()
|
||||||
if (QFile::exists(path))
|
if (QFile::exists(path))
|
||||||
{
|
{
|
||||||
// should we popup a warning?
|
// should we popup a warning?
|
||||||
// if (!checkContinue(tr("Overwriting a file"), tr("Are you sure you want to overwrite %1?").arg(path)))
|
|
||||||
// return;
|
|
||||||
success = QFile::remove(path);
|
success = QFile::remove(path);
|
||||||
if (!success)
|
if (!success)
|
||||||
{
|
{
|
||||||
|
@ -199,8 +197,8 @@ void IdentityForm::onDeleteClicked()
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (checkContinue(tr("Deletion imminent!","deletion confirmation title"),
|
if (Widget::getInstance()->askQuestion(tr("Deletion imminent!","deletion confirmation title"),
|
||||||
tr("Are you sure you want to delete this profile?\nAssociated friend information and chat logs will be deleted as well.","deletion confirmation text")))
|
tr("Are you sure you want to delete this profile?","deletion confirmation text")))
|
||||||
{
|
{
|
||||||
QString profile = bodyUI->profiles->currentText();
|
QString profile = bodyUI->profiles->currentText();
|
||||||
QDir dir(Settings::getSettingsDirPath());
|
QDir dir(Settings::getSettingsDirPath());
|
||||||
|
@ -238,7 +236,7 @@ void IdentityForm::onImportClicked()
|
||||||
|
|
||||||
QString profilePath = QDir(Settings::getSettingsDirPath()).filePath(profile + Core::TOX_EXT);
|
QString profilePath = QDir(Settings::getSettingsDirPath()).filePath(profile + Core::TOX_EXT);
|
||||||
|
|
||||||
if (QFileInfo(profilePath).exists() && !checkContinue(tr("Profile already exists", "import confirm title"),
|
if (QFileInfo(profilePath).exists() && !Widget::getInstance()->askQuestion(tr("Profile already exists", "import confirm title"),
|
||||||
tr("A profile named \"%1\" already exists. Do you want to erase it?", "import confirm text").arg(profile)))
|
tr("A profile named \"%1\" already exists. Do you want to erase it?", "import confirm text").arg(profile)))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -251,12 +249,6 @@ void IdentityForm::onNewClicked()
|
||||||
emit Widget::getInstance()->changeProfile(QString());
|
emit Widget::getInstance()->changeProfile(QString());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IdentityForm::checkContinue(const QString& title, const QString& msg)
|
|
||||||
{
|
|
||||||
QMessageBox::StandardButton resp = QMessageBox::question(this, title, msg, QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
|
|
||||||
return resp == QMessageBox::Yes;
|
|
||||||
}
|
|
||||||
|
|
||||||
void IdentityForm::disableSwitching()
|
void IdentityForm::disableSwitching()
|
||||||
{
|
{
|
||||||
bodyUI->loadButton->setEnabled(false);
|
bodyUI->loadButton->setEnabled(false);
|
||||||
|
|
|
@ -66,7 +66,6 @@ private slots:
|
||||||
void onDeleteClicked();
|
void onDeleteClicked();
|
||||||
void onImportClicked();
|
void onImportClicked();
|
||||||
void onNewClicked();
|
void onNewClicked();
|
||||||
bool checkContinue(const QString& title, const QString& msg);
|
|
||||||
void disableSwitching();
|
void disableSwitching();
|
||||||
void enableSwitching();
|
void enableSwitching();
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,8 @@
|
||||||
#include "src/widget/widget.h"
|
#include "src/widget/widget.h"
|
||||||
#include "src/widget/form/setpassworddialog.h"
|
#include "src/widget/form/setpassworddialog.h"
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
PrivacyForm::PrivacyForm() :
|
PrivacyForm::PrivacyForm() :
|
||||||
GenericForm(tr("Privacy"), QPixmap(":/img/settings/privacy.png"))
|
GenericForm(tr("Privacy"), QPixmap(":/img/settings/privacy.png"))
|
||||||
|
@ -30,10 +32,15 @@ PrivacyForm::PrivacyForm() :
|
||||||
bodyUI = new Ui::PrivacySettings;
|
bodyUI = new Ui::PrivacySettings;
|
||||||
bodyUI->setupUi(this);
|
bodyUI->setupUi(this);
|
||||||
|
|
||||||
|
bodyUI->encryptToxHLayout->addStretch();
|
||||||
|
bodyUI->encryptLogsHLayout->addStretch();
|
||||||
|
|
||||||
connect(bodyUI->cbTypingNotification, SIGNAL(stateChanged(int)), this, SLOT(onTypingNotificationEnabledUpdated()));
|
connect(bodyUI->cbTypingNotification, SIGNAL(stateChanged(int)), this, SLOT(onTypingNotificationEnabledUpdated()));
|
||||||
connect(bodyUI->cbKeepHistory, SIGNAL(stateChanged(int)), this, SLOT(onEnableLoggingUpdated()));
|
connect(bodyUI->cbKeepHistory, SIGNAL(stateChanged(int)), this, SLOT(onEnableLoggingUpdated()));
|
||||||
connect(bodyUI->cbEncryptHistory, SIGNAL(clicked()), this, SLOT(onEncryptLogsUpdated()));
|
connect(bodyUI->cbEncryptHistory, SIGNAL(clicked()), this, SLOT(onEncryptLogsUpdated()));
|
||||||
|
connect(bodyUI->changeLogsPwButton, &QPushButton::clicked, this, &PrivacyForm::setChatLogsPassword);
|
||||||
connect(bodyUI->cbEncryptTox, SIGNAL(clicked()), this, SLOT(onEncryptToxUpdated()));
|
connect(bodyUI->cbEncryptTox, SIGNAL(clicked()), this, SLOT(onEncryptToxUpdated()));
|
||||||
|
connect(bodyUI->changeToxPwButton, &QPushButton::clicked, this, &PrivacyForm::setToxPassword);
|
||||||
connect(bodyUI->nospamLineEdit, SIGNAL(editingFinished()), this, SLOT(setNospam()));
|
connect(bodyUI->nospamLineEdit, SIGNAL(editingFinished()), this, SLOT(setNospam()));
|
||||||
connect(bodyUI->randomNosapamButton, SIGNAL(clicked()), this, SLOT(generateRandomNospam()));
|
connect(bodyUI->randomNosapamButton, SIGNAL(clicked()), this, SLOT(generateRandomNospam()));
|
||||||
connect(bodyUI->nospamLineEdit, SIGNAL(textChanged(QString)), this, SLOT(onNospamEdit()));
|
connect(bodyUI->nospamLineEdit, SIGNAL(textChanged(QString)), this, SLOT(onNospamEdit()));
|
||||||
|
@ -48,7 +55,7 @@ void PrivacyForm::onEnableLoggingUpdated()
|
||||||
{
|
{
|
||||||
Settings::getInstance().setEnableLogging(bodyUI->cbKeepHistory->isChecked());
|
Settings::getInstance().setEnableLogging(bodyUI->cbKeepHistory->isChecked());
|
||||||
bodyUI->cbEncryptHistory->setEnabled(bodyUI->cbKeepHistory->isChecked());
|
bodyUI->cbEncryptHistory->setEnabled(bodyUI->cbKeepHistory->isChecked());
|
||||||
HistoryKeeper::getInstance()->resetInstance();
|
HistoryKeeper::resetInstance();
|
||||||
Widget::getInstance()->clearAllReceipts();
|
Widget::getInstance()->clearAllReceipts();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,79 +64,172 @@ void PrivacyForm::onTypingNotificationEnabledUpdated()
|
||||||
Settings::getInstance().setTypingNotification(bodyUI->cbTypingNotification->isChecked());
|
Settings::getInstance().setTypingNotification(bodyUI->cbTypingNotification->isChecked());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PrivacyForm::setChatLogsPassword()
|
||||||
|
{
|
||||||
|
Core* core = Core::getInstance();
|
||||||
|
SetPasswordDialog* dialog;
|
||||||
|
QString body = tr("Please set your new chat log password.");
|
||||||
|
if (core->isPasswordSet(Core::ptMain))
|
||||||
|
dialog = new SetPasswordDialog(body, tr("Use data file password", "pushbutton text"), this);
|
||||||
|
else
|
||||||
|
dialog = new SetPasswordDialog(body, QString(), this);
|
||||||
|
|
||||||
|
if (int r = dialog->exec())
|
||||||
|
{
|
||||||
|
QList<HistoryKeeper::HistMessage> oldMessages = HistoryKeeper::exportMessagesDeleteFile();
|
||||||
|
|
||||||
|
QString newpw = dialog->getPassword();
|
||||||
|
delete dialog;
|
||||||
|
|
||||||
|
if (r == 2)
|
||||||
|
core->useOtherPassword(Core::ptHistory);
|
||||||
|
else
|
||||||
|
core->setPassword(newpw, Core::ptHistory);
|
||||||
|
|
||||||
|
//if (!HistoryKeeper::checkPassword(true))
|
||||||
|
// if (checkContinue(tr("Old encrypted chat logs", "title"),
|
||||||
|
// tr("Would you like to re-encrypt your old chat logs?\nOtherwise they will be deleted.", "body")))
|
||||||
|
// for now, don't bother asking. Why wouldn't you want to reencrypt?
|
||||||
|
|
||||||
|
Settings::getInstance().setEncryptLogs(true);
|
||||||
|
HistoryKeeper::getInstance()->importMessages(oldMessages);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
delete dialog;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void PrivacyForm::onEncryptLogsUpdated()
|
void PrivacyForm::onEncryptLogsUpdated()
|
||||||
{
|
{
|
||||||
bool encrytionState = bodyUI->cbEncryptHistory->isChecked();
|
Core* core = Core::getInstance();
|
||||||
|
|
||||||
if (encrytionState)
|
if (bodyUI->cbEncryptHistory->isChecked())
|
||||||
{
|
{
|
||||||
if (!Core::getInstance()->isPasswordSet(Core::ptHistory))
|
if (!core->isPasswordSet(Core::ptHistory))
|
||||||
{
|
{
|
||||||
SetPasswordDialog dialog;
|
if (setChatLogsPassword())
|
||||||
if (dialog.exec())
|
|
||||||
{
|
{
|
||||||
QString pswd = dialog.getPassword();
|
bodyUI->cbEncryptHistory->setChecked(true);
|
||||||
if (pswd.size() == 0)
|
bodyUI->changeLogsPwButton->setEnabled(true);
|
||||||
encrytionState = false;
|
return;
|
||||||
|
|
||||||
Core::getInstance()->setPassword(pswd, Core::ptHistory);
|
|
||||||
} else {
|
|
||||||
encrytionState = false;
|
|
||||||
Core::getInstance()->clearPassword(Core::ptHistory);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
Settings::getInstance().setEncryptLogs(encrytionState);
|
|
||||||
if (encrytionState && !HistoryKeeper::checkPassword())
|
|
||||||
{
|
{
|
||||||
if (QMessageBox::Ok != QMessageBox::warning(nullptr, tr("Encrypted log"),
|
QMessageBox::StandardButton button = QMessageBox::warning(
|
||||||
tr("You already have history log file encrypted with different password\nDo you want to delete old history file?"),
|
Widget::getInstance(),
|
||||||
QMessageBox::Ok | QMessageBox::Cancel))
|
tr("Old encrypted chat logs", "title"),
|
||||||
|
tr("Would you like to decrypt your chat logs?\nOtherwise they will be deleted."),
|
||||||
|
QMessageBox::Ok | QMessageBox::No | QMessageBox::Cancel,
|
||||||
|
QMessageBox::Cancel
|
||||||
|
);
|
||||||
|
|
||||||
|
if (button == QMessageBox::Ok)
|
||||||
{
|
{
|
||||||
// TODO: ask user about reencryption with new password
|
QList<HistoryKeeper::HistMessage> oldMessages = HistoryKeeper::exportMessagesDeleteFile(true);
|
||||||
encrytionState = false;
|
core->clearPassword(Core::ptHistory);
|
||||||
|
Settings::getInstance().setEncryptLogs(false);
|
||||||
|
HistoryKeeper::getInstance()->importMessages(oldMessages);
|
||||||
|
}
|
||||||
|
else if (button == QMessageBox::No)
|
||||||
|
{
|
||||||
|
if (QMessageBox::critical(
|
||||||
|
Widget::getInstance(),
|
||||||
|
tr("Old encrypted chat logs", "title"),
|
||||||
|
tr("Are you sure you want to lose your entire chat history?"),
|
||||||
|
QMessageBox::Yes | QMessageBox::Cancel,
|
||||||
|
QMessageBox::Cancel
|
||||||
|
)
|
||||||
|
== QMessageBox::Yes)
|
||||||
|
{
|
||||||
|
HistoryKeeper::removeHistory(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bodyUI->cbEncryptHistory->setChecked(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bodyUI->cbEncryptHistory->setChecked(true);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Settings::getInstance().setEncryptLogs(encrytionState);
|
core->clearPassword(Core::ptHistory);
|
||||||
bodyUI->cbEncryptHistory->setChecked(encrytionState);
|
Settings::getInstance().setEncryptLogs(false);
|
||||||
|
bodyUI->cbEncryptHistory->setChecked(false);
|
||||||
|
bodyUI->changeLogsPwButton->setEnabled(false);
|
||||||
|
HistoryKeeper::resetInstance();
|
||||||
|
}
|
||||||
|
|
||||||
if (encrytionState)
|
bool PrivacyForm::setToxPassword()
|
||||||
HistoryKeeper::resetInstance();
|
{
|
||||||
|
Core* core = Core::getInstance();
|
||||||
|
SetPasswordDialog* dialog;
|
||||||
|
QString body = tr("Please set your new data file password.");
|
||||||
|
if (core->isPasswordSet(Core::ptHistory))
|
||||||
|
dialog = new SetPasswordDialog(body, tr("Use chat log password", "pushbutton text"), this);
|
||||||
|
else
|
||||||
|
dialog = new SetPasswordDialog(body, QString(), this);
|
||||||
|
|
||||||
if (!Settings::getInstance().getEncryptLogs())
|
if (int r = dialog->exec())
|
||||||
Core::getInstance()->clearPassword(Core::ptHistory);
|
{
|
||||||
|
QString newpw = dialog->getPassword();
|
||||||
|
delete dialog;
|
||||||
|
|
||||||
|
if (r == 2)
|
||||||
|
core->useOtherPassword(Core::ptMain);
|
||||||
|
else
|
||||||
|
core->setPassword(newpw, Core::ptMain);
|
||||||
|
|
||||||
|
Settings::getInstance().setEncryptTox(true);
|
||||||
|
core->saveConfiguration();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
delete dialog;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrivacyForm::onEncryptToxUpdated()
|
void PrivacyForm::onEncryptToxUpdated()
|
||||||
{
|
{
|
||||||
bool encrytionState = bodyUI->cbEncryptTox->isChecked();
|
Core* core = Core::getInstance();
|
||||||
|
|
||||||
if (encrytionState)
|
if (bodyUI->cbEncryptTox->isChecked())
|
||||||
{
|
{
|
||||||
if (!Core::getInstance()->isPasswordSet(Core::ptMain))
|
if (!core->isPasswordSet(Core::ptMain))
|
||||||
{
|
{
|
||||||
SetPasswordDialog dialog;
|
if (setToxPassword())
|
||||||
if (dialog.exec())
|
|
||||||
{
|
{
|
||||||
QString pswd = dialog.getPassword();
|
bodyUI->cbEncryptTox->setChecked(true);
|
||||||
if (pswd.size() == 0)
|
bodyUI->changeToxPwButton->setEnabled(true);
|
||||||
encrytionState = false;
|
return;
|
||||||
|
|
||||||
Core::getInstance()->setPassword(pswd, Core::ptMain);
|
|
||||||
} else {
|
|
||||||
encrytionState = false;
|
|
||||||
Core::getInstance()->clearPassword(Core::ptMain);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!Widget::getInstance()->askQuestion(tr("Decrypt your data file", "title"), tr("Would you like to decrypt your data file?")))
|
||||||
|
{
|
||||||
|
bodyUI->cbEncryptTox->setChecked(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// affirmative answer falls through to the catch all below
|
||||||
|
}
|
||||||
|
|
||||||
bodyUI->cbEncryptTox->setChecked(encrytionState);
|
bodyUI->cbEncryptTox->setChecked(false);
|
||||||
Settings::getInstance().setEncryptTox(encrytionState);
|
Settings::getInstance().setEncryptTox(false);
|
||||||
|
bodyUI->changeToxPwButton->setEnabled(false);
|
||||||
if (!Settings::getInstance().getEncryptTox())
|
core->clearPassword(Core::ptMain);
|
||||||
Core::getInstance()->clearPassword(Core::ptMain);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrivacyForm::setNospam()
|
void PrivacyForm::setNospam()
|
||||||
|
@ -148,8 +248,10 @@ void PrivacyForm::present()
|
||||||
bodyUI->cbTypingNotification->setChecked(Settings::getInstance().isTypingNotificationEnabled());
|
bodyUI->cbTypingNotification->setChecked(Settings::getInstance().isTypingNotificationEnabled());
|
||||||
bodyUI->cbKeepHistory->setChecked(Settings::getInstance().getEnableLogging());
|
bodyUI->cbKeepHistory->setChecked(Settings::getInstance().getEnableLogging());
|
||||||
bodyUI->cbEncryptHistory->setChecked(Settings::getInstance().getEncryptLogs());
|
bodyUI->cbEncryptHistory->setChecked(Settings::getInstance().getEncryptLogs());
|
||||||
|
bodyUI->changeLogsPwButton->setEnabled(Settings::getInstance().getEncryptLogs());
|
||||||
bodyUI->cbEncryptHistory->setEnabled(Settings::getInstance().getEnableLogging());
|
bodyUI->cbEncryptHistory->setEnabled(Settings::getInstance().getEnableLogging());
|
||||||
bodyUI->cbEncryptTox->setChecked(Settings::getInstance().getEncryptTox());
|
bodyUI->cbEncryptTox->setChecked(Settings::getInstance().getEncryptTox());
|
||||||
|
bodyUI->changeToxPwButton->setEnabled(Settings::getInstance().getEncryptTox());
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrivacyForm::generateRandomNospam()
|
void PrivacyForm::generateRandomNospam()
|
||||||
|
|
|
@ -39,7 +39,9 @@ private slots:
|
||||||
void generateRandomNospam();
|
void generateRandomNospam();
|
||||||
void onNospamEdit();
|
void onNospamEdit();
|
||||||
void onEncryptLogsUpdated();
|
void onEncryptLogsUpdated();
|
||||||
|
bool setChatLogsPassword();
|
||||||
void onEncryptToxUpdated();
|
void onEncryptToxUpdated();
|
||||||
|
bool setToxPassword();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::PrivacySettings* bodyUI;
|
Ui::PrivacySettings* bodyUI;
|
||||||
|
|
|
@ -57,7 +57,7 @@
|
||||||
<string extracomment="History keeping still under developing. Log format changin is possible."/>
|
<string extracomment="History keeping still under developing. Log format changin is possible."/>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Keep History (unstable)</string>
|
<string>Keep chat logs (mostly stable)</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -67,31 +67,63 @@
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
<property name="title">
|
<property name="title">
|
||||||
<string>Encryption</string>
|
<string>Local file encryption</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="cbEncryptTox">
|
<widget class="QLabel" name="encryptStatement">
|
||||||
<property name="enabled">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Encrypt Tox datafile</string>
|
<string>All internet communication is encrypted, and this cannot be disabled. However, you may optionally password protect your local Tox files.</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="cbEncryptHistory">
|
<layout class="QHBoxLayout" name="encryptToxHLayout">
|
||||||
<property name="enabled">
|
<item>
|
||||||
<bool>false</bool>
|
<widget class="QCheckBox" name="cbEncryptTox">
|
||||||
</property>
|
<property name="enabled">
|
||||||
<property name="text">
|
<bool>true</bool>
|
||||||
<string>Encrypt History</string>
|
</property>
|
||||||
</property>
|
<property name="text">
|
||||||
<property name="checkable">
|
<string>Encrypt Tox data file</string>
|
||||||
<bool>false</bool>
|
</property>
|
||||||
</property>
|
</widget>
|
||||||
</widget>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="changeToxPwButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Change password</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="encryptLogsHLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="cbEncryptHistory">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Encrypt chat logs</string>
|
||||||
|
</property>
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="changeLogsPwButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Change password</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
|
|
@ -30,12 +30,6 @@ void toxSaveEventHandler(const QByteArray& eventData)
|
||||||
handleToxSave(eventData);
|
handleToxSave(eventData);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool checkContinue(const QString& title, const QString& msg)
|
|
||||||
{
|
|
||||||
QMessageBox::StandardButton resp = QMessageBox::question(Widget::getInstance(), title, msg, QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
|
|
||||||
return resp == QMessageBox::Yes;
|
|
||||||
}
|
|
||||||
|
|
||||||
void handleToxSave(const QString& path)
|
void handleToxSave(const QString& path)
|
||||||
{
|
{
|
||||||
Core* core = Core::getInstance();
|
Core* core = Core::getInstance();
|
||||||
|
@ -67,7 +61,7 @@ void handleToxSave(const QString& path)
|
||||||
|
|
||||||
QString profilePath = QDir(Settings::getSettingsDirPath()).filePath(profile + Core::TOX_EXT);
|
QString profilePath = QDir(Settings::getSettingsDirPath()).filePath(profile + Core::TOX_EXT);
|
||||||
|
|
||||||
if (QFileInfo(profilePath).exists() && !checkContinue(QObject::tr("Profile already exists", "import confirm title"),
|
if (QFileInfo(profilePath).exists() && !Widget::getInstance()->askQuestion(QObject::tr("Profile already exists", "import confirm title"),
|
||||||
QObject::tr("A profile named \"%1\" already exists. Do you want to erase it?", "import confirm text").arg(profile)))
|
QObject::tr("A profile named \"%1\" already exists. Do you want to erase it?", "import confirm text").arg(profile)))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,6 @@
|
||||||
#include "form/chatform.h"
|
#include "form/chatform.h"
|
||||||
#include "maskablepixmapwidget.h"
|
#include "maskablepixmapwidget.h"
|
||||||
#include "src/historykeeper.h"
|
#include "src/historykeeper.h"
|
||||||
#include "form/inputpassworddialog.h"
|
|
||||||
#include "src/autoupdate.h"
|
#include "src/autoupdate.h"
|
||||||
#include "src/audio.h"
|
#include "src/audio.h"
|
||||||
#include "src/platform/timer.h"
|
#include "src/platform/timer.h"
|
||||||
|
@ -87,7 +86,7 @@ void Widget::init()
|
||||||
icon = new QSystemTrayIcon(this);
|
icon = new QSystemTrayIcon(this);
|
||||||
updateTrayIcon();
|
updateTrayIcon();
|
||||||
trayMenu = new QMenu;
|
trayMenu = new QMenu;
|
||||||
|
|
||||||
statusOnline = new QAction(tr("Online"), this);
|
statusOnline = new QAction(tr("Online"), this);
|
||||||
statusOnline->setIcon(QIcon(":ui/statusButton/dot_online.png"));
|
statusOnline->setIcon(QIcon(":ui/statusButton/dot_online.png"));
|
||||||
connect(statusOnline, SIGNAL(triggered()), this, SLOT(setStatusOnline()));
|
connect(statusOnline, SIGNAL(triggered()), this, SLOT(setStatusOnline()));
|
||||||
|
@ -99,19 +98,19 @@ void Widget::init()
|
||||||
connect(statusBusy, SIGNAL(triggered()), this, SLOT(setStatusBusy()));
|
connect(statusBusy, SIGNAL(triggered()), this, SLOT(setStatusBusy()));
|
||||||
actionQuit = new QAction(tr("&Quit"), this);
|
actionQuit = new QAction(tr("&Quit"), this);
|
||||||
connect(actionQuit, SIGNAL(triggered()), qApp, SLOT(quit()));
|
connect(actionQuit, SIGNAL(triggered()), qApp, SLOT(quit()));
|
||||||
|
|
||||||
trayMenu->addAction(statusOnline);
|
trayMenu->addAction(statusOnline);
|
||||||
trayMenu->addAction(statusAway);
|
trayMenu->addAction(statusAway);
|
||||||
trayMenu->addAction(statusBusy);
|
trayMenu->addAction(statusBusy);
|
||||||
trayMenu->addSeparator();
|
trayMenu->addSeparator();
|
||||||
trayMenu->addAction(actionQuit);
|
trayMenu->addAction(actionQuit);
|
||||||
icon->setContextMenu(trayMenu);
|
icon->setContextMenu(trayMenu);
|
||||||
|
|
||||||
connect(icon,
|
connect(icon,
|
||||||
SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
|
SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
|
||||||
this,
|
this,
|
||||||
SLOT(onIconClick(QSystemTrayIcon::ActivationReason)));
|
SLOT(onIconClick(QSystemTrayIcon::ActivationReason)));
|
||||||
|
|
||||||
if (Settings::getInstance().getShowSystemTray())
|
if (Settings::getInstance().getShowSystemTray())
|
||||||
{
|
{
|
||||||
icon->show();
|
icon->show();
|
||||||
|
@ -241,7 +240,6 @@ 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, &Core::friendTypingChanged, this, &Widget::onFriendTypingChanged);
|
connect(core, &Core::friendTypingChanged, this, &Widget::onFriendTypingChanged);
|
||||||
|
|
||||||
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)));
|
||||||
|
@ -387,7 +385,7 @@ QString Widget::detectProfile()
|
||||||
QString path, profile = Settings::getInstance().getCurrentProfile();
|
QString path, profile = Settings::getInstance().getCurrentProfile();
|
||||||
path = dir.filePath(profile + Core::TOX_EXT);
|
path = dir.filePath(profile + Core::TOX_EXT);
|
||||||
QFile file(path);
|
QFile file(path);
|
||||||
if (profile == "" || !file.exists())
|
if (profile.isEmpty() || !file.exists())
|
||||||
{
|
{
|
||||||
Settings::getInstance().setCurrentProfile("");
|
Settings::getInstance().setCurrentProfile("");
|
||||||
#if 1 // deprecation attempt
|
#if 1 // deprecation attempt
|
||||||
|
@ -402,10 +400,13 @@ QString Widget::detectProfile()
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
profile = askProfiles();
|
profile = askProfiles();
|
||||||
if (profile != "")
|
if (profile.isEmpty())
|
||||||
return dir.filePath(profile + Core::TOX_EXT);
|
|
||||||
else
|
|
||||||
return "";
|
return "";
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Settings::getInstance().switchProfile(profile);
|
||||||
|
return dir.filePath(profile + Core::TOX_EXT);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1176,20 +1177,6 @@ void Widget::onGroupSendResult(int groupId, const QString& message, int result)
|
||||||
g->getChatForm()->addSystemInfoMessage(tr("Message failed to send"), "red", QDateTime::currentDateTime());
|
g->getChatForm()->addSystemInfoMessage(tr("Message failed to send"), "red", QDateTime::currentDateTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Widget::getPassword(QString info, int passtype, uint8_t* salt)
|
|
||||||
{
|
|
||||||
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, salt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Widget::onFriendTypingChanged(int friendId, bool isTyping)
|
void Widget::onFriendTypingChanged(int friendId, bool isTyping)
|
||||||
{
|
{
|
||||||
Friend* f = FriendList::findFriend(friendId);
|
Friend* f = FriendList::findFriend(friendId);
|
||||||
|
@ -1220,21 +1207,17 @@ void Widget::onSplitterMoved(int pos, int index)
|
||||||
saveSplitterGeometry();
|
saveSplitterGeometry();
|
||||||
}
|
}
|
||||||
|
|
||||||
QMessageBox::StandardButton Widget::showWarningMsgBox(const QString& title, const QString& msg, QMessageBox::StandardButtons buttons)
|
void Widget::showWarningMsgBox(const QString& title, const QString& msg)
|
||||||
{
|
{
|
||||||
// We can only display widgets from the GUI thread
|
// We can only display widgets from the GUI thread
|
||||||
if (QThread::currentThread() != qApp->thread())
|
if (QThread::currentThread() != qApp->thread())
|
||||||
{
|
{
|
||||||
QMessageBox::StandardButton ret;
|
|
||||||
QMetaObject::invokeMethod(this, "showWarningMsgBox", Qt::BlockingQueuedConnection,
|
QMetaObject::invokeMethod(this, "showWarningMsgBox", Qt::BlockingQueuedConnection,
|
||||||
Q_RETURN_ARG(QMessageBox::StandardButton, ret),
|
Q_ARG(const QString&, title), Q_ARG(const QString&, msg));
|
||||||
Q_ARG(const QString&, title), Q_ARG(const QString&, msg),
|
|
||||||
Q_ARG(QMessageBox::StandardButtons, buttons));
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return QMessageBox::warning(this, title, msg, buttons);
|
QMessageBox::warning(this, title, msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1253,20 +1236,71 @@ void Widget::setEnabledThreadsafe(bool enabled)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Widget::askMsgboxQuestion(const QString& title, const QString& msg)
|
bool Widget::askQuestion(const QString& title, const QString& msg, bool defaultAns, bool warning)
|
||||||
{
|
{
|
||||||
// We can only display widgets from the GUI thread
|
// We can only display widgets from the GUI thread
|
||||||
if (QThread::currentThread() != qApp->thread())
|
if (QThread::currentThread() != qApp->thread())
|
||||||
{
|
{
|
||||||
bool ret;
|
bool ret;
|
||||||
QMetaObject::invokeMethod(this, "askMsgboxQuestion", Qt::BlockingQueuedConnection,
|
QMetaObject::invokeMethod(this, "askQuestion", Qt::BlockingQueuedConnection,
|
||||||
Q_RETURN_ARG(bool, ret),
|
Q_RETURN_ARG(bool, ret),
|
||||||
Q_ARG(const QString&, title), Q_ARG(const QString&, msg));
|
Q_ARG(const QString&, title), Q_ARG(const QString&, msg),
|
||||||
|
Q_ARG(bool, defaultAns), Q_ARG(bool, warning));
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return QMessageBox::question(this, title, msg) == QMessageBox::StandardButton::Yes;
|
if (warning)
|
||||||
|
{
|
||||||
|
QMessageBox::StandardButton def = QMessageBox::Cancel;
|
||||||
|
if (defaultAns)
|
||||||
|
def = QMessageBox::Ok;
|
||||||
|
return QMessageBox::warning(this, title, msg, QMessageBox::Ok | QMessageBox::Cancel, def) == QMessageBox::Ok;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QMessageBox::StandardButton def = QMessageBox::No;
|
||||||
|
if (defaultAns)
|
||||||
|
def = QMessageBox::Yes;
|
||||||
|
return QMessageBox::question(this, title, msg, QMessageBox::Yes | QMessageBox::No, def) == QMessageBox::Yes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Widget::passwordDialog(const QString& cancel, const QString& body)
|
||||||
|
{
|
||||||
|
// We can only display widgets from the GUI thread
|
||||||
|
if (QThread::currentThread() != qApp->thread())
|
||||||
|
{
|
||||||
|
QString ret;
|
||||||
|
QMetaObject::invokeMethod(this, "passwordDialog", Qt::BlockingQueuedConnection,
|
||||||
|
Q_RETURN_ARG(QString, ret),
|
||||||
|
Q_ARG(const QString&, cancel), Q_ARG(const QString&, body));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QString ret;
|
||||||
|
QInputDialog dialog;
|
||||||
|
dialog.setWindowTitle(tr("Enter your password"));
|
||||||
|
dialog.setOkButtonText(tr("Set password"));
|
||||||
|
dialog.setCancelButtonText(cancel);
|
||||||
|
dialog.setInputMode(QInputDialog::TextInput);
|
||||||
|
dialog.setTextEchoMode(QLineEdit::Password);
|
||||||
|
dialog.setLabelText(body);
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
int val = dialog.exec();
|
||||||
|
if (val != QDialog::Accepted)
|
||||||
|
return QString();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = dialog.textValue();
|
||||||
|
if (!ret.isEmpty())
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
dialog.setLabelText(body + "\n" + tr("You must enter a non-empty password."));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -64,10 +64,12 @@ public:
|
||||||
void clearContactsList();
|
void clearContactsList();
|
||||||
void setTranslation();
|
void setTranslation();
|
||||||
void updateTrayIcon();
|
void updateTrayIcon();
|
||||||
Q_INVOKABLE QMessageBox::StandardButton showWarningMsgBox(const QString& title, const QString& msg,
|
Q_INVOKABLE void showWarningMsgBox(const QString& title, const QString& msg);
|
||||||
QMessageBox::StandardButtons buttonss = QMessageBox::Ok);
|
|
||||||
Q_INVOKABLE void setEnabledThreadsafe(bool enabled);
|
Q_INVOKABLE void setEnabledThreadsafe(bool enabled);
|
||||||
Q_INVOKABLE bool askMsgboxQuestion(const QString& title, const QString& msg);
|
Q_INVOKABLE bool askQuestion(const QString& title, const QString& msg, bool defaultAns = false, bool warning = true);
|
||||||
|
Q_INVOKABLE QString passwordDialog(const QString& cancel, const QString& body);
|
||||||
|
Q_INVOKABLE QString askProfiles();
|
||||||
|
// hooray for threading hacks
|
||||||
~Widget();
|
~Widget();
|
||||||
|
|
||||||
virtual void closeEvent(QCloseEvent *event);
|
virtual void closeEvent(QCloseEvent *event);
|
||||||
|
@ -133,7 +135,6 @@ private slots:
|
||||||
void playRingtone();
|
void playRingtone();
|
||||||
void onIconClick(QSystemTrayIcon::ActivationReason);
|
void onIconClick(QSystemTrayIcon::ActivationReason);
|
||||||
void onUserAwayCheck();
|
void onUserAwayCheck();
|
||||||
void getPassword(QString info, int passtype, uint8_t* salt);
|
|
||||||
void onFriendTypingChanged(int friendId, bool isTyping);
|
void onFriendTypingChanged(int friendId, bool isTyping);
|
||||||
void onSetShowSystemTray(bool newValue);
|
void onSetShowSystemTray(bool newValue);
|
||||||
void onSplitterMoved(int pos, int index);
|
void onSplitterMoved(int pos, int index);
|
||||||
|
@ -147,7 +148,6 @@ private:
|
||||||
void removeGroup(Group* g, bool fake = false);
|
void removeGroup(Group* g, bool fake = false);
|
||||||
void saveWindowGeometry();
|
void saveWindowGeometry();
|
||||||
void saveSplitterGeometry();
|
void saveSplitterGeometry();
|
||||||
QString askProfiles();
|
|
||||||
QString detectProfile();
|
QString detectProfile();
|
||||||
QSystemTrayIcon *icon;
|
QSystemTrayIcon *icon;
|
||||||
QMenu *trayMenu;
|
QMenu *trayMenu;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user