mirror of
https://github.com/qTox/qTox.git
synced 2024-03-22 14:00:36 +08:00
Merge branch 'encryption' into master
Conflicts: src/core.cpp src/historykeeper.h
This commit is contained in:
commit
a572ccff27
6
qtox.pro
6
qtox.pro
|
@ -32,7 +32,6 @@ FORMS += \
|
|||
src/widget/form/settings/identitysettings.ui \
|
||||
src/widget/form/settings/privacysettings.ui \
|
||||
src/widget/form/loadhistorydialog.ui \
|
||||
src/widget/form/inputpassworddialog.ui \
|
||||
src/widget/form/setpassworddialog.ui \
|
||||
src/widget/form/settings/advancedsettings.ui
|
||||
|
||||
|
@ -116,7 +115,7 @@ win32 {
|
|||
target.path = /usr/bin
|
||||
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 += -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
|
||||
} else {
|
||||
LIBS += -L$$PWD/libs/lib/ -ltoxcore -ltoxav -ltoxencryptsave -ltoxdns -lvpx -lsodium -lopenal -lopencv_core -lopencv_highgui -lopencv_imgproc
|
||||
|
@ -193,7 +192,6 @@ HEADERS += src/widget/form/addfriendform.h \
|
|||
src/misc/db/genericddinterface.h \
|
||||
src/misc/db/plaindb.h \
|
||||
src/misc/db/encrypteddb.h \
|
||||
src/widget/form/inputpassworddialog.h \
|
||||
src/widget/form/setpassworddialog.h \
|
||||
src/widget/form/tabcompleter.h \
|
||||
src/video/videoframe.h \
|
||||
|
@ -226,6 +224,7 @@ SOURCES += \
|
|||
src/widget/groupwidget.cpp \
|
||||
src/widget/widget.cpp \
|
||||
src/core.cpp \
|
||||
src/coreencryption.cpp \
|
||||
src/friend.cpp \
|
||||
src/friendlist.cpp \
|
||||
src/group.cpp \
|
||||
|
@ -262,7 +261,6 @@ SOURCES += \
|
|||
src/misc/db/genericddinterface.cpp \
|
||||
src/misc/db/plaindb.cpp \
|
||||
src/misc/db/encrypteddb.cpp \
|
||||
src/widget/form/inputpassworddialog.cpp \
|
||||
src/widget/form/setpassworddialog.cpp \
|
||||
src/video/netvideosource.cpp \
|
||||
src/widget/form/tabcompleter.cpp \
|
||||
|
|
|
@ -490,8 +490,8 @@ void AutoUpdater::checkUpdatesAsyncInteractiveWorker()
|
|||
QDir updateDir(updateDirStr);
|
||||
|
||||
if ((updateDir.exists() && QFile(updateDirStr+"flist").exists())
|
||||
|| Widget::getInstance()->askMsgboxQuestion(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.")))
|
||||
|| 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."), true, false))
|
||||
{
|
||||
downloadUpdate();
|
||||
}
|
||||
|
|
331
src/core.cpp
331
src/core.cpp
|
@ -23,7 +23,6 @@
|
|||
#include "src/audio.h"
|
||||
|
||||
#include <tox/tox.h>
|
||||
#include <tox/toxencryptsave.h>
|
||||
|
||||
#include <ctime>
|
||||
#include <functional>
|
||||
|
@ -39,7 +38,6 @@
|
|||
#include <QDateTime>
|
||||
#include <QList>
|
||||
#include <QBuffer>
|
||||
#include <QMessageBox>
|
||||
#include <QMutexLocker>
|
||||
|
||||
const QString Core::CONFIG_FILE_NAME = "data";
|
||||
|
@ -90,13 +88,24 @@ Core::Core(Camera* cam, QThread *CoreThread, QString loadPath) :
|
|||
Audio::openInput(inDevDescr);
|
||||
}
|
||||
|
||||
void Core::deadifyTox()
|
||||
{
|
||||
if (toxav)
|
||||
{
|
||||
toxav_kill(toxav);
|
||||
toxav = nullptr;
|
||||
}
|
||||
if (tox)
|
||||
{
|
||||
tox_kill(tox);
|
||||
tox = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Core::~Core()
|
||||
{
|
||||
qDebug() << "Deleting Core";
|
||||
|
||||
clearPassword(Core::ptMain);
|
||||
clearPassword(Core::ptHistory);
|
||||
|
||||
toxTimer->stop();
|
||||
coreThread->exit(0);
|
||||
while (coreThread->isRunning())
|
||||
|
@ -105,13 +114,7 @@ Core::~Core()
|
|||
coreThread->wait(500);
|
||||
}
|
||||
|
||||
if (tox)
|
||||
{
|
||||
toxav_kill(toxav);
|
||||
toxav = nullptr;
|
||||
tox_kill(tox);
|
||||
tox = nullptr;
|
||||
}
|
||||
deadifyTox();
|
||||
|
||||
if (videobuf)
|
||||
{
|
||||
|
@ -183,12 +186,9 @@ void Core::make_tox()
|
|||
{
|
||||
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!";
|
||||
emit badProxy();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
qCritical() << "Tox core failed to start";
|
||||
|
@ -229,14 +229,26 @@ void Core::start()
|
|||
|
||||
if (loadPath != "")
|
||||
{
|
||||
if (!loadConfiguration(loadPath)) // loadPath is meaningless after this
|
||||
{
|
||||
qCritical() << "Core: loadConfiguration failed, exiting now";
|
||||
emit failedToStart();
|
||||
tox_kill(tox);
|
||||
tox = nullptr;
|
||||
return;
|
||||
while (!loadConfiguration(loadPath))
|
||||
{
|
||||
if (loadPath.isEmpty())
|
||||
{
|
||||
QString profile;
|
||||
if ((profile = loadOldInformation()).isEmpty())
|
||||
{
|
||||
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 = "";
|
||||
}
|
||||
else // new ID
|
||||
|
@ -292,7 +304,7 @@ void Core::start()
|
|||
}
|
||||
else
|
||||
qDebug() << "Core: Error loading self avatar";
|
||||
|
||||
|
||||
ready = true;
|
||||
|
||||
process(); // starts its own timer
|
||||
|
@ -309,7 +321,7 @@ void Core::start()
|
|||
|
||||
void Core::process()
|
||||
{
|
||||
if (!tox)
|
||||
if (!isReady())
|
||||
return;
|
||||
|
||||
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)
|
||||
{
|
||||
Core* core = static_cast<Core*>(_core);
|
||||
|
||||
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)
|
||||
{
|
||||
if (!tox || fake)
|
||||
if (!isReady() || fake)
|
||||
return;
|
||||
if (tox_del_friend(tox, friendId) == -1) {
|
||||
emit failedToRemoveFriend(friendId);
|
||||
|
@ -1024,7 +1035,7 @@ void Core::removeFriend(int friendId, bool fake)
|
|||
|
||||
void Core::removeGroup(int groupId, bool fake)
|
||||
{
|
||||
if (!tox || fake)
|
||||
if (!isReady() || fake)
|
||||
return;
|
||||
tox_del_groupchat(tox, groupId);
|
||||
|
||||
|
@ -1162,7 +1173,8 @@ QString Core::sanitize(QString name)
|
|||
|
||||
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);
|
||||
qDebug() << "Core::loadConfiguration: reading from " << path;
|
||||
|
||||
|
@ -1186,51 +1198,21 @@ bool Core::loadConfiguration(QString path)
|
|||
}
|
||||
else if (error == 1) // Encrypted data save
|
||||
{
|
||||
if (!Settings::getInstance().getEncryptTox())
|
||||
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
|
||||
if (!loadEncryptedSave(data))
|
||||
{
|
||||
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);
|
||||
if (!pwsaltedkeys[ptMain])
|
||||
Widget::getInstance()->showWarningMsgBox(tr("Password error"), tr("Failed to setup password.\nEmpty password."));
|
||||
loadPath = QDir(Settings::getSettingsDirPath()).filePath(profile + TOX_EXT);
|
||||
Settings::getInstance().switchProfile(profile);
|
||||
HistoryKeeper::resetInstance();
|
||||
}
|
||||
|
||||
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);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
configurationFile.close();
|
||||
|
@ -1239,7 +1221,7 @@ bool Core::loadConfiguration(QString path)
|
|||
QString name = getUsername();
|
||||
if (!name.isEmpty())
|
||||
emit usernameSet(name);
|
||||
|
||||
|
||||
QString msg = getStatusMessage();
|
||||
if (!msg.isEmpty())
|
||||
emit statusMessageSet(msg);
|
||||
|
@ -1250,53 +1232,7 @@ bool Core::loadConfiguration(QString path)
|
|||
|
||||
// tox core is already decrypted
|
||||
if (Settings::getInstance().getEnableLogging() && Settings::getInstance().getEncryptLogs())
|
||||
{
|
||||
// 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
|
||||
{
|
||||
bool error = true;
|
||||
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);
|
||||
}
|
||||
}
|
||||
checkEncryptedHistory();
|
||||
|
||||
loadFriends();
|
||||
return true;
|
||||
|
@ -1307,13 +1243,16 @@ void Core::saveConfiguration()
|
|||
if (QThread::currentThread() != coreThread)
|
||||
return (void) QMetaObject::invokeMethod(this, "saveConfiguration");
|
||||
|
||||
if (!isReady())
|
||||
return;
|
||||
|
||||
QString dir = Settings::getSettingsDirPath();
|
||||
QDir directory(dir);
|
||||
if (!directory.exists() && !directory.mkpath(directory.absolutePath())) {
|
||||
qCritical() << "Error while creating directory " << dir;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
QString profile = Settings::getInstance().getCurrentProfile();
|
||||
|
||||
if (profile == "")
|
||||
|
@ -1323,74 +1262,14 @@ void Core::saveConfiguration()
|
|||
if (profile == "") // happens on creation of a new Tox ID
|
||||
profile = getIDString();
|
||||
|
||||
Settings::getInstance().setCurrentProfile(profile);
|
||||
Settings::getInstance().switchProfile(profile);
|
||||
}
|
||||
|
||||
|
||||
QString path = directory.filePath(profile + TOX_EXT);
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
if (profile.isEmpty())
|
||||
|
@ -1399,19 +1278,15 @@ void Core::switchConfiguration(const QString& profile)
|
|||
qDebug() << "Core: switching from" << Settings::getInstance().getCurrentProfile() << "to" << profile;
|
||||
|
||||
saveConfiguration();
|
||||
saveCurrentInformation(); // part of a hack, see core.h
|
||||
|
||||
ready = false;
|
||||
Widget::getInstance()->setEnabledThreadsafe(false);
|
||||
clearPassword(ptMain);
|
||||
clearPassword(ptHistory);
|
||||
|
||||
toxTimer->stop();
|
||||
Widget::getInstance()->setEnabledThreadsafe(false);
|
||||
if (tox) {
|
||||
toxav_kill(toxav);
|
||||
toxav = nullptr;
|
||||
tox_kill(tox);
|
||||
tox = nullptr;
|
||||
}
|
||||
deadifyTox();
|
||||
|
||||
emit selfAvatarChanged(QPixmap(":/img/contact_dark.png"));
|
||||
emit blockingClearContacts(); // we need this to block, but signals are required for thread safety
|
||||
|
@ -1421,15 +1296,12 @@ void Core::switchConfiguration(const QString& profile)
|
|||
else
|
||||
loadPath = QDir(Settings::getSettingsDirPath()).filePath(profile + TOX_EXT);
|
||||
|
||||
// the new profile needs to be set before resetting the settings, so that
|
||||
// we don't load the old profile's profile.ini
|
||||
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();
|
||||
Settings::getInstance().switchProfile(profile);
|
||||
HistoryKeeper::resetInstance();
|
||||
|
||||
start();
|
||||
Widget::getInstance()->setEnabledThreadsafe(true);
|
||||
if (isReady())
|
||||
Widget::getInstance()->setEnabledThreadsafe(true);
|
||||
}
|
||||
|
||||
void Core::loadFriends()
|
||||
|
@ -1794,69 +1666,6 @@ QList<CString> Core::splitMessage(const QString &message, int maxLen)
|
|||
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 name;
|
||||
|
@ -1891,7 +1700,7 @@ QString Core::getPeerName(const ToxID& id) const
|
|||
|
||||
bool Core::isReady()
|
||||
{
|
||||
return ready;
|
||||
return toxav && tox && ready;
|
||||
}
|
||||
|
||||
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);
|
||||
static Core* getInstance(); ///< Returns the global widget's Core instance
|
||||
~Core();
|
||||
|
||||
|
||||
static const QString TOX_EXT;
|
||||
static const QString CONFIG_FILE_NAME;
|
||||
static QString sanitize(QString name);
|
||||
static QList<CString> splitMessage(const QString &message, int maxLen);
|
||||
|
||||
static QByteArray getSaltFromFile(QString filename);
|
||||
|
||||
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
|
||||
|
@ -64,9 +66,9 @@ public:
|
|||
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
|
||||
void quitGroupChat(int groupId) const; ///< Quit a groupchat
|
||||
|
||||
|
||||
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 getStatusMessage() const; ///< Returns our status message, or an empty string on failure
|
||||
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 process(); ///< Processes toxcore events and ensure we stay connected, called by its own timer
|
||||
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(const QString& path);
|
||||
void switchConfiguration(const QString& profile); ///< Load a different profile and restart the core
|
||||
|
||||
void acceptFriendRequest(const QString& userId);
|
||||
void requestFriendship(const QString& friendAddress, const QString& message);
|
||||
|
@ -137,6 +139,7 @@ public slots:
|
|||
static bool isGroupCallVolEnabled(int groupId);
|
||||
|
||||
void setPassword(QString& password, PasswordType passtype, uint8_t* salt = nullptr);
|
||||
void useOtherPassword(PasswordType type);
|
||||
void clearPassword(PasswordType passtype);
|
||||
QByteArray encryptData(const QByteArray& data, PasswordType passtype);
|
||||
QByteArray decryptData(const QByteArray& data, PasswordType passtype);
|
||||
|
@ -145,7 +148,6 @@ signals:
|
|||
void connected();
|
||||
void disconnected();
|
||||
void blockingClearContacts();
|
||||
void blockingGetPassword(QString info, int passtype, uint8_t* salt = nullptr);
|
||||
|
||||
void friendRequestReceived(const QString& userId, const QString& message);
|
||||
void friendMessageReceived(int friendId, const QString& message, bool isAction);
|
||||
|
@ -266,6 +268,8 @@ private:
|
|||
bool checkConnection();
|
||||
|
||||
bool loadConfiguration(QString path); // Returns false for a critical error, true otherwise
|
||||
bool loadEncryptedSave(QByteArray& data);
|
||||
void checkEncryptedHistory();
|
||||
void make_tox();
|
||||
void loadFriends();
|
||||
|
||||
|
@ -274,6 +278,8 @@ private:
|
|||
|
||||
void checkLastOnline(int friendId);
|
||||
|
||||
void deadifyTox();
|
||||
|
||||
private slots:
|
||||
void onFileTransferFinished(ToxFile file);
|
||||
|
||||
|
@ -294,7 +300,17 @@ private:
|
|||
QMutex fileSendMutex, messageSendMutex;
|
||||
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 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;
|
||||
}
|
||||
|
||||
bool HistoryKeeper::checkPassword()
|
||||
bool HistoryKeeper::checkPassword(int encrypted)
|
||||
{
|
||||
if (Settings::getInstance().getEnableLogging())
|
||||
{
|
||||
if (Settings::getInstance().getEncryptLogs())
|
||||
{
|
||||
QString dbpath = getHistoryPath();
|
||||
return EncryptedDb::check(dbpath);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (!Settings::getInstance().getEnableLogging() && (encrypted == -1))
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((encrypted == 1) || (encrypted == -1 && Settings::getInstance().getEncryptLogs()))
|
||||
return EncryptedDb::check(getHistoryPath(Settings::getInstance().getCurrentProfile(), encrypted));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
HistoryKeeper::HistoryKeeper(GenericDdInterface *db_) :
|
||||
|
@ -132,9 +127,10 @@ HistoryKeeper::HistoryKeeper(GenericDdInterface *db_) :
|
|||
|
||||
setSyncType(Settings::getInstance().getDbSyncType());
|
||||
|
||||
messageID = 0;
|
||||
QSqlQuery sqlAnswer = db->exec("select seq from sqlite_sequence where name=\"history\";");
|
||||
sqlAnswer.first();
|
||||
messageID = sqlAnswer.value(0).toInt();
|
||||
if (sqlAnswer.first())
|
||||
messageID = sqlAnswer.value(0).toLongLong();
|
||||
}
|
||||
|
||||
HistoryKeeper::~HistoryKeeper()
|
||||
|
@ -142,16 +138,13 @@ HistoryKeeper::~HistoryKeeper()
|
|||
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;
|
||||
int sender_id = getAliasID(sender);
|
||||
QList<QString> cmds = generateAddChatEntryCmd(chat, message, sender, dt, isSent);
|
||||
|
||||
db->exec("BEGIN TRANSACTION;");
|
||||
db->exec(QString("INSERT INTO history (timestamp, chat_id, sender, message) ") +
|
||||
QString("VALUES (%1, %2, %3, '%4');")
|
||||
.arg(dt.toMSecsSinceEpoch()).arg(chat_id).arg(sender_id).arg(wrapMessage(message)));
|
||||
db->exec(QString("INSERT INTO sent_status (status) VALUES (%1);").arg(isSent));
|
||||
for (auto &it : cmds)
|
||||
db->exec(it);
|
||||
db->exec("COMMIT TRANSACTION;");
|
||||
|
||||
messageID++;
|
||||
|
@ -190,12 +183,66 @@ QList<HistoryKeeper::HistMessage> HistoryKeeper::getChatHistory(HistoryKeeper::C
|
|||
|
||||
QDateTime time = QDateTime::fromMSecsSinceEpoch(timeInt);
|
||||
|
||||
res.push_back({id, sender,message,time,isSent});
|
||||
res.push_back(HistMessage(id, "", sender, message, time, isSent));
|
||||
}
|
||||
|
||||
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 wrappedMessage(str);
|
||||
|
@ -272,7 +319,7 @@ void HistoryKeeper::resetInstance()
|
|||
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(message)
|
||||
|
@ -342,3 +389,29 @@ void HistoryKeeper::setSyncType(Db::syncType sType)
|
|||
|
||||
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
|
||||
{
|
||||
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;
|
||||
QString chat;
|
||||
QString sender;
|
||||
QString message;
|
||||
QDateTime timestamp;
|
||||
|
@ -44,14 +48,20 @@ public:
|
|||
static void resetInstance();
|
||||
|
||||
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 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);
|
||||
int addGroupChatEntry(const QString& chat, const QString& message, const QString& sender, const QDateTime &dt);
|
||||
qint64 addChatEntry(const QString& chat, const QString& message, const QString& sender, const QDateTime &dt, bool isSent);
|
||||
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);
|
||||
void markAsSent(int m_id);
|
||||
|
||||
QList<HistMessage> exportMessages();
|
||||
void importMessages(const QList<HistoryKeeper::HistMessage> &lst);
|
||||
|
||||
void setSyncType(Db::syncType sType);
|
||||
|
||||
private:
|
||||
|
@ -63,15 +73,16 @@ private:
|
|||
void updateAliases();
|
||||
QPair<int, ChatType> getChatID(const QString &id_str, ChatType ct);
|
||||
int getAliasID(const QString &id_str);
|
||||
QString wrapMessage(const QString &str);
|
||||
QString unWrapMessage(const QString &str);
|
||||
QList<QString> generateAddChatEntryCmd(const QString& chat, const QString& message, const QString& sender, const QDateTime &dt, bool isSent);
|
||||
|
||||
static QString wrapMessage(const QString &str);
|
||||
static QString unWrapMessage(const QString &str);
|
||||
static ChatType convertToChatType(int);
|
||||
ChatType convertToChatType(int);
|
||||
|
||||
GenericDdInterface *db;
|
||||
QMap<QString, int> aliases;
|
||||
QMap<QString, QPair<int, ChatType>> chats;
|
||||
int messageID;
|
||||
qint64 messageID;
|
||||
};
|
||||
|
||||
#endif // HISTORYKEEPER_H
|
||||
|
|
|
@ -76,7 +76,7 @@ int main(int argc, char *argv[])
|
|||
if (QDir(Settings::getSettingsDirPath()).exists(profile + ".tox"))
|
||||
{
|
||||
qDebug() << "Setting profile to" << profile;
|
||||
Settings::getInstance().setCurrentProfile(profile);
|
||||
Settings::getInstance().switchProfile(profile);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -24,28 +24,36 @@
|
|||
#include <QDebug>
|
||||
#include <QSqlError>
|
||||
|
||||
qint64 EncryptedDb::plainChunkSize = 4096;
|
||||
qint64 EncryptedDb::encryptedChunkSize = EncryptedDb::plainChunkSize + tox_pass_encryption_extra_length();
|
||||
qint64 EncryptedDb::encryptedChunkSize = 4096;
|
||||
qint64 EncryptedDb::plainChunkSize = EncryptedDb::encryptedChunkSize - tox_pass_encryption_extra_length();
|
||||
|
||||
EncryptedDb::EncryptedDb(const QString &fname, QList<QString> initList) :
|
||||
PlainDb(":memory:", initList), encrFile(fname)
|
||||
PlainDb(":memory:", initList), fileName(fname)
|
||||
{
|
||||
QByteArray fileContent;
|
||||
if (pullFileContent())
|
||||
if (pullFileContent(fileName, buffer))
|
||||
{
|
||||
chunkPosition = encrFile.size() / encryptedChunkSize;
|
||||
|
||||
encrFile.seek(0);
|
||||
qDebug() << "EncryptedDb: writing old data";
|
||||
encrFile.setFileName(fileName);
|
||||
encrFile.open(QIODevice::ReadOnly);
|
||||
fileContent = encrFile.readAll();
|
||||
} else {
|
||||
qWarning() << "corrupted history log file will be wiped!";
|
||||
chunkPosition = 0;
|
||||
chunkPosition = encrFile.size() / encryptedChunkSize;
|
||||
encrFile.close();
|
||||
}
|
||||
else
|
||||
chunkPosition = 0;
|
||||
|
||||
encrFile.close();
|
||||
encrFile.open(QIODevice::WriteOnly);
|
||||
encrFile.write(fileContent);
|
||||
encrFile.flush();
|
||||
encrFile.setFileName(fileName);
|
||||
|
||||
if (!encrFile.open(QIODevice::WriteOnly))
|
||||
{
|
||||
qWarning() << "can't open file:" << fileName;
|
||||
}
|
||||
else
|
||||
{
|
||||
encrFile.write(fileContent);
|
||||
encrFile.flush();
|
||||
}
|
||||
}
|
||||
|
||||
EncryptedDb::~EncryptedDb()
|
||||
|
@ -57,27 +65,34 @@ EncryptedDb::~EncryptedDb()
|
|||
QSqlQuery EncryptedDb::exec(const QString &query)
|
||||
{
|
||||
QSqlQuery retQSqlQuery = PlainDb::exec(query);
|
||||
if (query.startsWith("INSERT", Qt::CaseInsensitive))
|
||||
if (checkCmd(query))
|
||||
appendToEncrypted(query);
|
||||
|
||||
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;
|
||||
|
||||
while (!encrFile.atEnd())
|
||||
while (!dbFile.atEnd())
|
||||
{
|
||||
QByteArray encrChunk = encrFile.read(encryptedChunkSize);
|
||||
buffer = Core::getInstance()->decryptData(encrChunk, Core::ptHistory);
|
||||
if (buffer.size() > 0)
|
||||
QByteArray encrChunk = dbFile.read(encryptedChunkSize);
|
||||
qDebug() << "EncryptedDb::pullFileContent: got chunk:" << encrChunk.size();
|
||||
buf = Core::getInstance()->decryptData(encrChunk, Core::ptHistory);
|
||||
if (buf.size() > 0)
|
||||
{
|
||||
fileContent += buffer;
|
||||
fileContent += buf;
|
||||
} else {
|
||||
qWarning() << "Encrypted history log is corrupted: can't decrypt";
|
||||
buffer = QByteArray();
|
||||
qWarning() << "EncryptedDb::pullFileContent: Encrypted history log is corrupted: can't decrypt, will be deleted";
|
||||
buf = QByteArray();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -88,31 +103,17 @@ bool EncryptedDb::pullFileContent()
|
|||
for (auto ba_line : splittedBA)
|
||||
{
|
||||
QString line = QByteArray::fromBase64(ba_line);
|
||||
if (line.size() == 0)
|
||||
continue;
|
||||
|
||||
bool isGoodLine = false;
|
||||
if (line.startsWith("CREATE", Qt::CaseInsensitive) || line.startsWith("INSERT", Qt::CaseInsensitive))
|
||||
{
|
||||
if (line.endsWith(");"))
|
||||
{
|
||||
sqlCmds.append(line);
|
||||
isGoodLine = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isGoodLine)
|
||||
{
|
||||
qWarning() << "Encrypted history log is corrupted: errors in content";
|
||||
buffer = QByteArray();
|
||||
return false;
|
||||
}
|
||||
sqlCmds.append(line);
|
||||
}
|
||||
|
||||
PlainDb::exec("BEGIN TRANSACTION;");
|
||||
for (auto line : sqlCmds)
|
||||
{
|
||||
QSqlQuery r = PlainDb::exec(line);
|
||||
}
|
||||
PlainDb::exec("COMMIT TRANSACTION;");
|
||||
|
||||
dbFile.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -133,6 +134,7 @@ void EncryptedDb::appendToEncrypted(const QString &sql)
|
|||
if (encr.size() > 0)
|
||||
{
|
||||
encrFile.write(encr);
|
||||
encrFile.flush();
|
||||
}
|
||||
|
||||
buffer = buffer.right(buffer.size() - plainChunkSize);
|
||||
|
@ -170,3 +172,14 @@ bool EncryptedDb::check(const QString &fname)
|
|||
file.close();
|
||||
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);
|
||||
|
||||
private:
|
||||
bool pullFileContent();
|
||||
bool pullFileContent(const QString& fname, QByteArray &buf);
|
||||
void appendToEncrypted(const QString &sql);
|
||||
bool checkCmd(const QString &cmd);
|
||||
|
||||
QFile encrFile;
|
||||
QString fileName;
|
||||
|
||||
static qint64 plainChunkSize;
|
||||
static qint64 encryptedChunkSize;
|
||||
|
|
|
@ -58,6 +58,13 @@ void Settings::resetInstance()
|
|||
}
|
||||
}
|
||||
|
||||
void Settings::switchProfile(const QString& profile)
|
||||
{
|
||||
setCurrentProfile(profile);
|
||||
save(false);
|
||||
resetInstance();
|
||||
}
|
||||
|
||||
void Settings::load()
|
||||
{
|
||||
if (loaded)
|
||||
|
@ -186,13 +193,6 @@ void Settings::load()
|
|||
splitterState = s.value("splitterState", QByteArray()).toByteArray();
|
||||
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");
|
||||
inDev = s.value("inDev", "").toString();
|
||||
outDev = s.value("outDev", "").toString();
|
||||
|
@ -225,38 +225,45 @@ void Settings::load()
|
|||
|
||||
loaded = true;
|
||||
|
||||
if (currentProfile.isEmpty()) // new profile in Core::switchConfiguration
|
||||
return;
|
||||
if (!currentProfile.isEmpty()) // new profile in Core::switchConfiguration
|
||||
{
|
||||
// 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
|
||||
QString tmp = dir.filePath(currentProfile + ".ini");
|
||||
if (QFile(tmp).exists())
|
||||
filePath = tmp;
|
||||
QSettings ps(filePath, QSettings::IniFormat);
|
||||
friendLst.clear();
|
||||
ps.beginGroup("Friends");
|
||||
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);
|
||||
friendLst.clear();
|
||||
fs.beginGroup("Friends");
|
||||
int size = fs.beginReadArray("Friend");
|
||||
for (int i = 0; i < size; i ++)
|
||||
{
|
||||
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();
|
||||
ps.beginGroup("Privacy");
|
||||
typingNotification = ps.value("typingNotification", false).toBool();
|
||||
enableLogging = ps.value("enableLogging", false).toBool();
|
||||
encryptLogs = ps.value("encryptLogs", false).toBool();
|
||||
encryptTox = ps.value("encryptTox", false).toBool();
|
||||
ps.endGroup();
|
||||
}
|
||||
}
|
||||
|
||||
void Settings::save(bool writeFriends)
|
||||
void Settings::save(bool writePersonal)
|
||||
{
|
||||
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;
|
||||
|
||||
|
@ -336,13 +343,6 @@ void Settings::save(QString path, bool writeFriends)
|
|||
s.setValue("splitterState", splitterState);
|
||||
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.setValue("inDev", inDev);
|
||||
s.setValue("outDev", outDev);
|
||||
|
@ -353,23 +353,30 @@ void Settings::save(QString path, bool writeFriends)
|
|||
s.setValue("camVideoRes",camVideoRes);
|
||||
s.endGroup();
|
||||
|
||||
if (!writeFriends || currentProfile.isEmpty()) // Core::switchConfiguration
|
||||
return;
|
||||
if (writePersonal && !currentProfile.isEmpty()) // Core::switchConfiguration
|
||||
{
|
||||
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);
|
||||
fs.beginGroup("Friends");
|
||||
fs.beginWriteArray("Friend", friendLst.size());
|
||||
int index = 0;
|
||||
for (auto& frnd : friendLst)
|
||||
{
|
||||
fs.setArrayIndex(index);
|
||||
fs.setValue("addr", frnd.addr);
|
||||
fs.setValue("alias", frnd.alias);
|
||||
fs.setValue("autoAcceptDir", frnd.autoAcceptDir);
|
||||
index++;
|
||||
}
|
||||
fs.endArray();
|
||||
fs.endGroup();
|
||||
ps.beginGroup("Privacy");
|
||||
ps.setValue("typingNotification", typingNotification);
|
||||
ps.setValue("enableLogging", enableLogging);
|
||||
ps.setValue("encryptLogs", encryptLogs);
|
||||
ps.setValue("encryptTox", encryptTox);
|
||||
ps.endGroup();
|
||||
}
|
||||
}
|
||||
|
||||
QString Settings::getSettingsDirPath()
|
||||
|
|
|
@ -32,6 +32,7 @@ class Settings : public QObject
|
|||
public:
|
||||
static Settings& getInstance();
|
||||
static void resetInstance();
|
||||
void switchProfile(const QString& profile);
|
||||
~Settings() = default;
|
||||
|
||||
void executeSettingsDialog(QWidget* parent);
|
||||
|
@ -236,8 +237,8 @@ public:
|
|||
void setCompactLayout(bool compact);
|
||||
|
||||
public:
|
||||
void save(bool writeFriends = true);
|
||||
void save(QString path, bool writeFriends = true);
|
||||
void save(bool writePersonal = true);
|
||||
void save(QString path, bool writePersonal = true);
|
||||
void load();
|
||||
|
||||
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 <QPushButton>
|
||||
|
||||
SetPasswordDialog::SetPasswordDialog(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::SetPasswordDialog)
|
||||
const double SetPasswordDialog::reasonablePasswordLength = 8.;
|
||||
|
||||
SetPasswordDialog::SetPasswordDialog(QString body, QString extraButton, QWidget* parent)
|
||||
: QDialog(parent)
|
||||
, ui(new Ui::SetPasswordDialog)
|
||||
, body(body+"\n")
|
||||
{
|
||||
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()));
|
||||
|
||||
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(Tertiary);});
|
||||
}
|
||||
}
|
||||
|
||||
SetPasswordDialog::~SetPasswordDialog()
|
||||
|
@ -35,10 +48,53 @@ SetPasswordDialog::~SetPasswordDialog()
|
|||
|
||||
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->body->setText(body);
|
||||
}
|
||||
else
|
||||
{
|
||||
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()
|
||||
|
|
|
@ -28,7 +28,8 @@ class SetPasswordDialog : public QDialog
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SetPasswordDialog(QWidget *parent = 0);
|
||||
enum ReturnCode {Rejected=QDialog::Rejected, Accepted=QDialog::Accepted, Tertiary};
|
||||
explicit SetPasswordDialog(QString body, QString extraButton, QWidget* parent = 0);
|
||||
~SetPasswordDialog();
|
||||
QString getPassword();
|
||||
|
||||
|
@ -37,6 +38,8 @@ private slots:
|
|||
|
||||
private:
|
||||
Ui::SetPasswordDialog *ui;
|
||||
QString body;
|
||||
static const double reasonablePasswordLength;
|
||||
};
|
||||
|
||||
#endif // SETPASSWORDDIALOG_H
|
||||
|
|
|
@ -6,44 +6,77 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>434</width>
|
||||
<height>175</height>
|
||||
<width>325</width>
|
||||
<height>160</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string notr="true">Dialog</string>
|
||||
<string>Set your password</string>
|
||||
</property>
|
||||
<property name="modal">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Type Password</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="body"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="passwordlineEdit">
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Password</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Repeat Password</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="repasswordlineEdit">
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Password</enum>
|
||||
</property>
|
||||
</widget>
|
||||
<layout class="QGridLayout" name="pswdsLayout">
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight</set>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Repeat password</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight</set>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Type password</string>
|
||||
</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>
|
||||
<spacer name="verticalSpacer">
|
||||
|
@ -73,6 +106,10 @@
|
|||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>passwordlineEdit</tabstop>
|
||||
<tabstop>repasswordlineEdit</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
#include <QClipboard>
|
||||
#include <QInputDialog>
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
|
||||
IdentityForm::IdentityForm() :
|
||||
GenericForm(tr("Identity"), QPixmap(":/img/settings/identity.png"))
|
||||
|
@ -152,10 +151,11 @@ void IdentityForm::onRenameClicked()
|
|||
name = Core::sanitize(name);
|
||||
QDir dir(Settings::getSettingsDirPath());
|
||||
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)))
|
||||
{
|
||||
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);
|
||||
HistoryKeeper::renameHistory(cur, name);
|
||||
Settings::getInstance().setCurrentProfile(name);
|
||||
|
@ -176,8 +176,6 @@ void IdentityForm::onExportClicked()
|
|||
if (QFile::exists(path))
|
||||
{
|
||||
// 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);
|
||||
if (!success)
|
||||
{
|
||||
|
@ -199,8 +197,8 @@ void IdentityForm::onDeleteClicked()
|
|||
}
|
||||
else
|
||||
{
|
||||
if (checkContinue(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")))
|
||||
if (Widget::getInstance()->askQuestion(tr("Deletion imminent!","deletion confirmation title"),
|
||||
tr("Are you sure you want to delete this profile?","deletion confirmation text")))
|
||||
{
|
||||
QString profile = bodyUI->profiles->currentText();
|
||||
QDir dir(Settings::getSettingsDirPath());
|
||||
|
@ -238,7 +236,7 @@ void IdentityForm::onImportClicked()
|
|||
|
||||
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)))
|
||||
return;
|
||||
|
||||
|
@ -251,12 +249,6 @@ void IdentityForm::onNewClicked()
|
|||
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()
|
||||
{
|
||||
bodyUI->loadButton->setEnabled(false);
|
||||
|
|
|
@ -66,7 +66,6 @@ private slots:
|
|||
void onDeleteClicked();
|
||||
void onImportClicked();
|
||||
void onNewClicked();
|
||||
bool checkContinue(const QString& title, const QString& msg);
|
||||
void disableSwitching();
|
||||
void enableSwitching();
|
||||
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
#include "src/widget/widget.h"
|
||||
#include "src/widget/form/setpassworddialog.h"
|
||||
#include <QMessageBox>
|
||||
#include <QFile>
|
||||
#include <QDebug>
|
||||
|
||||
PrivacyForm::PrivacyForm() :
|
||||
GenericForm(tr("Privacy"), QPixmap(":/img/settings/privacy.png"))
|
||||
|
@ -30,10 +32,15 @@ PrivacyForm::PrivacyForm() :
|
|||
bodyUI = new Ui::PrivacySettings;
|
||||
bodyUI->setupUi(this);
|
||||
|
||||
bodyUI->encryptToxHLayout->addStretch();
|
||||
bodyUI->encryptLogsHLayout->addStretch();
|
||||
|
||||
connect(bodyUI->cbTypingNotification, SIGNAL(stateChanged(int)), this, SLOT(onTypingNotificationEnabledUpdated()));
|
||||
connect(bodyUI->cbKeepHistory, SIGNAL(stateChanged(int)), this, SLOT(onEnableLoggingUpdated()));
|
||||
connect(bodyUI->cbEncryptHistory, SIGNAL(clicked()), this, SLOT(onEncryptLogsUpdated()));
|
||||
connect(bodyUI->changeLogsPwButton, &QPushButton::clicked, this, &PrivacyForm::setChatLogsPassword);
|
||||
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->randomNosapamButton, SIGNAL(clicked()), this, SLOT(generateRandomNospam()));
|
||||
connect(bodyUI->nospamLineEdit, SIGNAL(textChanged(QString)), this, SLOT(onNospamEdit()));
|
||||
|
@ -48,7 +55,7 @@ void PrivacyForm::onEnableLoggingUpdated()
|
|||
{
|
||||
Settings::getInstance().setEnableLogging(bodyUI->cbKeepHistory->isChecked());
|
||||
bodyUI->cbEncryptHistory->setEnabled(bodyUI->cbKeepHistory->isChecked());
|
||||
HistoryKeeper::getInstance()->resetInstance();
|
||||
HistoryKeeper::resetInstance();
|
||||
Widget::getInstance()->clearAllReceipts();
|
||||
}
|
||||
|
||||
|
@ -57,79 +64,183 @@ void PrivacyForm::onTypingNotificationEnabledUpdated()
|
|||
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);
|
||||
|
||||
// check if an encrypted history exists because it was disabled earlier, and use it if possible
|
||||
QString path = HistoryKeeper::getHistoryPath(QString(), 1);
|
||||
QByteArray salt = core->getSaltFromFile(path);
|
||||
bool haveEncHist = salt.size() > 0;
|
||||
|
||||
do {
|
||||
int r = dialog->exec();
|
||||
if (r == QDialog::Rejected)
|
||||
break;
|
||||
|
||||
QList<HistoryKeeper::HistMessage> oldMessages = HistoryKeeper::exportMessagesDeleteFile();
|
||||
|
||||
QString newpw = dialog->getPassword();
|
||||
|
||||
if (r == SetPasswordDialog::Tertiary)
|
||||
core->useOtherPassword(Core::ptHistory);
|
||||
else if (haveEncHist)
|
||||
core->setPassword(newpw, Core::ptHistory, reinterpret_cast<uint8_t*>(salt.data()));
|
||||
else
|
||||
core->setPassword(newpw, Core::ptHistory);
|
||||
|
||||
if (!haveEncHist || HistoryKeeper::checkPassword(1))
|
||||
{
|
||||
Settings::getInstance().setEncryptLogs(true);
|
||||
HistoryKeeper::getInstance()->importMessages(oldMessages);
|
||||
Widget::getInstance()->reloadHistory();
|
||||
delete dialog;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!Widget::getInstance()->askQuestion(tr("Old encrypted chat log", "popup title"), tr("There is currently an unused encrypted chat log, but the password you just entered doesn't match.\nWould you like to try again?")))
|
||||
haveEncHist = false; // logically this is really just a `break`, but conceptually this is more accurate
|
||||
}
|
||||
} while (haveEncHist);
|
||||
|
||||
delete dialog;
|
||||
return false;
|
||||
}
|
||||
|
||||
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 (dialog.exec())
|
||||
if (setChatLogsPassword())
|
||||
{
|
||||
QString pswd = dialog.getPassword();
|
||||
if (pswd.size() == 0)
|
||||
encrytionState = false;
|
||||
|
||||
Core::getInstance()->setPassword(pswd, Core::ptHistory);
|
||||
} else {
|
||||
encrytionState = false;
|
||||
Core::getInstance()->clearPassword(Core::ptHistory);
|
||||
bodyUI->cbEncryptHistory->setChecked(true);
|
||||
bodyUI->changeLogsPwButton->setEnabled(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Settings::getInstance().setEncryptLogs(encrytionState);
|
||||
if (encrytionState && !HistoryKeeper::checkPassword())
|
||||
else
|
||||
{
|
||||
if (QMessageBox::Ok != QMessageBox::warning(nullptr, tr("Encrypted log"),
|
||||
tr("You already have history log file encrypted with different password\nDo you want to delete old history file?"),
|
||||
QMessageBox::Ok | QMessageBox::Cancel))
|
||||
QMessageBox::StandardButton button = QMessageBox::warning(
|
||||
Widget::getInstance(),
|
||||
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
|
||||
encrytionState = false;
|
||||
QList<HistoryKeeper::HistMessage> oldMessages = HistoryKeeper::exportMessagesDeleteFile(true);
|
||||
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);
|
||||
bodyUI->cbEncryptHistory->setChecked(encrytionState);
|
||||
core->clearPassword(Core::ptHistory);
|
||||
Settings::getInstance().setEncryptLogs(false);
|
||||
bodyUI->cbEncryptHistory->setChecked(false);
|
||||
bodyUI->changeLogsPwButton->setEnabled(false);
|
||||
HistoryKeeper::resetInstance();
|
||||
}
|
||||
|
||||
if (encrytionState)
|
||||
HistoryKeeper::resetInstance();
|
||||
bool PrivacyForm::setToxPassword()
|
||||
{
|
||||
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())
|
||||
Core::getInstance()->clearPassword(Core::ptHistory);
|
||||
if (int r = dialog->exec())
|
||||
{
|
||||
QString newpw = dialog->getPassword();
|
||||
delete dialog;
|
||||
|
||||
if (r == SetPasswordDialog::Tertiary)
|
||||
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()
|
||||
{
|
||||
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 (dialog.exec())
|
||||
if (setToxPassword())
|
||||
{
|
||||
QString pswd = dialog.getPassword();
|
||||
if (pswd.size() == 0)
|
||||
encrytionState = false;
|
||||
|
||||
Core::getInstance()->setPassword(pswd, Core::ptMain);
|
||||
} else {
|
||||
encrytionState = false;
|
||||
Core::getInstance()->clearPassword(Core::ptMain);
|
||||
bodyUI->cbEncryptTox->setChecked(true);
|
||||
bodyUI->changeToxPwButton->setEnabled(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
Settings::getInstance().setEncryptTox(encrytionState);
|
||||
|
||||
if (!Settings::getInstance().getEncryptTox())
|
||||
Core::getInstance()->clearPassword(Core::ptMain);
|
||||
bodyUI->cbEncryptTox->setChecked(false);
|
||||
Settings::getInstance().setEncryptTox(false);
|
||||
bodyUI->changeToxPwButton->setEnabled(false);
|
||||
core->clearPassword(Core::ptMain);
|
||||
}
|
||||
|
||||
void PrivacyForm::setNospam()
|
||||
|
@ -148,8 +259,10 @@ void PrivacyForm::present()
|
|||
bodyUI->cbTypingNotification->setChecked(Settings::getInstance().isTypingNotificationEnabled());
|
||||
bodyUI->cbKeepHistory->setChecked(Settings::getInstance().getEnableLogging());
|
||||
bodyUI->cbEncryptHistory->setChecked(Settings::getInstance().getEncryptLogs());
|
||||
bodyUI->changeLogsPwButton->setEnabled(Settings::getInstance().getEncryptLogs());
|
||||
bodyUI->cbEncryptHistory->setEnabled(Settings::getInstance().getEnableLogging());
|
||||
bodyUI->cbEncryptTox->setChecked(Settings::getInstance().getEncryptTox());
|
||||
bodyUI->changeToxPwButton->setEnabled(Settings::getInstance().getEncryptTox());
|
||||
}
|
||||
|
||||
void PrivacyForm::generateRandomNospam()
|
||||
|
|
|
@ -39,7 +39,9 @@ private slots:
|
|||
void generateRandomNospam();
|
||||
void onNospamEdit();
|
||||
void onEncryptLogsUpdated();
|
||||
bool setChatLogsPassword();
|
||||
void onEncryptToxUpdated();
|
||||
bool setToxPassword();
|
||||
|
||||
private:
|
||||
Ui::PrivacySettings* bodyUI;
|
||||
|
|
|
@ -58,7 +58,7 @@
|
|||
Save format changes are possible, which may result in data loss.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Keep History (unstable)</string>
|
||||
<string>Keep chat logs (mostly stable)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -68,31 +68,63 @@ Save format changes are possible, which may result in data loss.</string>
|
|||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Encryption</string>
|
||||
<string>Local file encryption</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cbEncryptTox">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<widget class="QLabel" name="encryptStatement">
|
||||
<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>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cbEncryptHistory">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Encrypt History</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<layout class="QHBoxLayout" name="encryptToxHLayout">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cbEncryptTox">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Encrypt Tox data file</string>
|
||||
</property>
|
||||
</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>
|
||||
</layout>
|
||||
</widget>
|
||||
|
|
|
@ -30,12 +30,6 @@ void toxSaveEventHandler(const QByteArray& 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)
|
||||
{
|
||||
Core* core = Core::getInstance();
|
||||
|
@ -67,7 +61,7 @@ void handleToxSave(const QString& path)
|
|||
|
||||
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)))
|
||||
return;
|
||||
|
||||
|
|
|
@ -32,7 +32,6 @@
|
|||
#include "form/chatform.h"
|
||||
#include "maskablepixmapwidget.h"
|
||||
#include "src/historykeeper.h"
|
||||
#include "form/inputpassworddialog.h"
|
||||
#include "src/autoupdate.h"
|
||||
#include "src/audio.h"
|
||||
#include "src/platform/timer.h"
|
||||
|
@ -88,7 +87,7 @@ void Widget::init()
|
|||
icon = new SystemTrayIcon;
|
||||
updateTrayIcon();
|
||||
trayMenu = new QMenu;
|
||||
|
||||
|
||||
statusOnline = new QAction(tr("Online"), this);
|
||||
statusOnline->setIcon(QIcon(":ui/statusButton/dot_online.png"));
|
||||
connect(statusOnline, SIGNAL(triggered()), this, SLOT(setStatusOnline()));
|
||||
|
@ -100,19 +99,19 @@ void Widget::init()
|
|||
connect(statusBusy, SIGNAL(triggered()), this, SLOT(setStatusBusy()));
|
||||
actionQuit = new QAction(tr("&Quit"), this);
|
||||
connect(actionQuit, SIGNAL(triggered()), qApp, SLOT(quit()));
|
||||
|
||||
|
||||
trayMenu->addAction(statusOnline);
|
||||
trayMenu->addAction(statusAway);
|
||||
trayMenu->addAction(statusBusy);
|
||||
trayMenu->addSeparator();
|
||||
trayMenu->addAction(actionQuit);
|
||||
icon->setContextMenu(trayMenu);
|
||||
|
||||
|
||||
connect(icon,
|
||||
SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
|
||||
this,
|
||||
SLOT(onIconClick(QSystemTrayIcon::ActivationReason)));
|
||||
|
||||
|
||||
if (Settings::getInstance().getShowSystemTray())
|
||||
{
|
||||
icon->show();
|
||||
|
@ -242,7 +241,6 @@ void Widget::init()
|
|||
connect(core, &Core::emptyGroupCreated, this, &Widget::onEmptyGroupCreated);
|
||||
connect(core, &Core::avInvite, this, &Widget::playRingtone);
|
||||
connect(core, &Core::blockingClearContacts, this, &Widget::clearContactsList, Qt::BlockingQueuedConnection);
|
||||
connect(core, &Core::blockingGetPassword, this, &Widget::getPassword, Qt::BlockingQueuedConnection);
|
||||
connect(core, &Core::friendTypingChanged, this, &Widget::onFriendTypingChanged);
|
||||
|
||||
connect(core, SIGNAL(messageSentResult(int,QString,int)), this, SLOT(onMessageSendResult(int,QString,int)));
|
||||
|
@ -390,7 +388,7 @@ QString Widget::detectProfile()
|
|||
QString path, profile = Settings::getInstance().getCurrentProfile();
|
||||
path = dir.filePath(profile + Core::TOX_EXT);
|
||||
QFile file(path);
|
||||
if (profile == "" || !file.exists())
|
||||
if (profile.isEmpty() || !file.exists())
|
||||
{
|
||||
Settings::getInstance().setCurrentProfile("");
|
||||
#if 1 // deprecation attempt
|
||||
|
@ -405,10 +403,13 @@ QString Widget::detectProfile()
|
|||
#endif
|
||||
{
|
||||
profile = askProfiles();
|
||||
if (profile != "")
|
||||
return dir.filePath(profile + Core::TOX_EXT);
|
||||
else
|
||||
if (profile.isEmpty())
|
||||
return "";
|
||||
else
|
||||
{
|
||||
Settings::getInstance().switchProfile(profile);
|
||||
return dir.filePath(profile + Core::TOX_EXT);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -439,10 +440,7 @@ QString Widget::askProfiles()
|
|||
false, // if the user can enter their own input
|
||||
&ok);
|
||||
if (!ok) // user cancelled
|
||||
{
|
||||
qApp->quit();
|
||||
return "";
|
||||
}
|
||||
else
|
||||
return profile;
|
||||
}
|
||||
|
@ -688,6 +686,12 @@ void Widget::setStatusMessage(const QString &statusMessage)
|
|||
ui->statusLabel->setToolTip(statusMessage); // for overlength messsages
|
||||
}
|
||||
|
||||
void Widget::reloadHistory()
|
||||
{
|
||||
for (auto f : FriendList::getAllFriends())
|
||||
f->getChatForm()->loadHistory(QDateTime::currentDateTime().addDays(-7), true);
|
||||
}
|
||||
|
||||
void Widget::addFriend(int friendId, const QString &userId)
|
||||
{
|
||||
//qDebug() << "Widget: Adding friend with id" << userId;
|
||||
|
@ -1187,20 +1191,6 @@ void Widget::onGroupSendResult(int groupId, const QString& message, int result)
|
|||
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)
|
||||
{
|
||||
Friend* f = FriendList::findFriend(friendId);
|
||||
|
@ -1231,21 +1221,17 @@ void Widget::onSplitterMoved(int pos, int index)
|
|||
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
|
||||
if (QThread::currentThread() != qApp->thread())
|
||||
{
|
||||
QMessageBox::StandardButton ret;
|
||||
QMetaObject::invokeMethod(this, "showWarningMsgBox", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(QMessageBox::StandardButton, ret),
|
||||
Q_ARG(const QString&, title), Q_ARG(const QString&, msg),
|
||||
Q_ARG(QMessageBox::StandardButtons, buttons));
|
||||
return ret;
|
||||
Q_ARG(const QString&, title), Q_ARG(const QString&, msg));
|
||||
}
|
||||
else
|
||||
{
|
||||
return QMessageBox::warning(this, title, msg, buttons);
|
||||
QMessageBox::warning(this, title, msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1264,20 +1250,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
|
||||
if (QThread::currentThread() != qApp->thread())
|
||||
{
|
||||
bool ret;
|
||||
QMetaObject::invokeMethod(this, "askMsgboxQuestion", Qt::BlockingQueuedConnection,
|
||||
QMetaObject::invokeMethod(this, "askQuestion", Qt::BlockingQueuedConnection,
|
||||
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;
|
||||
}
|
||||
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("Decrypt"));
|
||||
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."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -65,10 +65,12 @@ public:
|
|||
void clearContactsList();
|
||||
void setTranslation();
|
||||
void updateTrayIcon();
|
||||
Q_INVOKABLE QMessageBox::StandardButton showWarningMsgBox(const QString& title, const QString& msg,
|
||||
QMessageBox::StandardButtons buttonss = QMessageBox::Ok);
|
||||
Q_INVOKABLE void showWarningMsgBox(const QString& title, const QString& msg);
|
||||
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();
|
||||
|
||||
virtual void closeEvent(QCloseEvent *event);
|
||||
|
@ -76,6 +78,7 @@ public:
|
|||
virtual void resizeEvent(QResizeEvent *event);
|
||||
|
||||
void clearAllReceipts();
|
||||
void reloadHistory();
|
||||
|
||||
void reloadTheme();
|
||||
|
||||
|
@ -134,7 +137,6 @@ private slots:
|
|||
void playRingtone();
|
||||
void onIconClick(QSystemTrayIcon::ActivationReason);
|
||||
void onUserAwayCheck();
|
||||
void getPassword(QString info, int passtype, uint8_t* salt);
|
||||
void onFriendTypingChanged(int friendId, bool isTyping);
|
||||
void onSetShowSystemTray(bool newValue);
|
||||
void onSplitterMoved(int pos, int index);
|
||||
|
@ -148,7 +150,6 @@ private:
|
|||
void removeGroup(Group* g, bool fake = false);
|
||||
void saveWindowGeometry();
|
||||
void saveSplitterGeometry();
|
||||
QString askProfiles();
|
||||
QString detectProfile();
|
||||
SystemTrayIcon *icon;
|
||||
QMenu *trayMenu;
|
||||
|
|
Loading…
Reference in New Issue
Block a user