From a9fb75b5ed437318bcdb2ad059bc3730eb05d673 Mon Sep 17 00:00:00 2001 From: apprb Date: Tue, 4 Nov 2014 01:44:01 +0900 Subject: [PATCH 01/49] proper encrypted history loading --- src/core.cpp | 34 +++++++--- src/core.h | 2 + src/historykeeper.cpp | 8 +++ src/historykeeper.h | 1 + src/misc/db/encrypteddb.cpp | 2 + src/widget/form/settings/privacyform.cpp | 73 +++++++++++++++------ src/widget/form/settings/privacysettings.ui | 6 +- src/widget/widget.h | 1 - 8 files changed, 95 insertions(+), 32 deletions(-) diff --git a/src/core.cpp b/src/core.cpp index 8cb27e8a8..e5c5f611c 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -1235,13 +1235,9 @@ bool Core::loadConfiguration(QString path) bool error = true; // get salt - QFile file(HistoryKeeper::getHistoryPath()); - file.open(QIODevice::ReadOnly); - QByteArray data = file.read(tox_pass_encryption_extra_length()); - file.close(); - uint8_t salt[tox_pass_salt_length()]; - int err = tox_get_salt(reinterpret_cast(data.data()), salt); - if (err) + QByteArray salt = getSaltFromFile(HistoryKeeper::getHistoryPath()); + + if (salt.size() == 0) { // maybe we should handle this better qWarning() << "Core: history db isn't encrypted, but encryption is set!! No history loaded..."; } @@ -1251,7 +1247,8 @@ bool Core::loadConfiguration(QString path) { while (!pwsaltedkeys[ptHistory]) { - emit blockingGetPassword(tr("History Log decryption password"), Core::ptHistory, salt); + emit blockingGetPassword(tr("History Log decryption password"), Core::ptHistory, + reinterpret_cast(salt.data())); if (!pwsaltedkeys[ptHistory]) Widget::getInstance()->showWarningMsgBox(tr("Password error"), tr("Failed to setup password.\nEmpty password.")); } @@ -1891,3 +1888,24 @@ void Core::resetCallSources() } } } + +QByteArray Core::getSaltFromFile(QString filename) +{ + qDebug() << filename; + QFile file(filename); + file.open(QIODevice::ReadOnly); + QByteArray data = file.read(tox_pass_encryption_extra_length()); + file.close(); + + qDebug() << "data size" << data.size(); + + uint8_t *salt = new uint8_t[tox_pass_salt_length()]; + int err = tox_get_salt(reinterpret_cast(data.data()), salt); + if (err) + { + qWarning() << "Core: can't get salt from" << filename << "header"; + return QByteArray(); + } + + return QByteArray::fromRawData(reinterpret_cast(salt), tox_pass_salt_length()); +} diff --git a/src/core.h b/src/core.h index 2f323bca2..460e11e30 100644 --- a/src/core.h +++ b/src/core.h @@ -49,6 +49,8 @@ public: static QString sanitize(QString name); static QList splitMessage(const QString &message); + 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 diff --git a/src/historykeeper.cpp b/src/historykeeper.cpp index a923f53c5..5a83d6dbe 100644 --- a/src/historykeeper.cpp +++ b/src/historykeeper.cpp @@ -342,3 +342,11 @@ 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(); +} diff --git a/src/historykeeper.h b/src/historykeeper.h index 300232e33..a98431b1a 100644 --- a/src/historykeeper.h +++ b/src/historykeeper.h @@ -45,6 +45,7 @@ public: static QString getHistoryPath(QString currentProfile = QString(), int encrypted = -1); // -1 defaults to checking settings, 0 or 1 to specify static bool checkPassword(); + static bool isFileExist(); static void renameHistory(QString from, QString to); int addChatEntry(const QString& chat, const QString& message, const QString& sender, const QDateTime &dt, bool isSent); diff --git a/src/misc/db/encrypteddb.cpp b/src/misc/db/encrypteddb.cpp index 56ac73c09..43f69a5d3 100644 --- a/src/misc/db/encrypteddb.cpp +++ b/src/misc/db/encrypteddb.cpp @@ -65,12 +65,14 @@ QSqlQuery EncryptedDb::exec(const QString &query) bool EncryptedDb::pullFileContent() { + qDebug() << "EncryptedDb::pullFileContent()"; encrFile.open(QIODevice::ReadOnly); QByteArray fileContent; while (!encrFile.atEnd()) { QByteArray encrChunk = encrFile.read(encryptedChunkSize); + qDebug() << "got chunk:" << encrChunk.size(); buffer = Core::getInstance()->decryptData(encrChunk, Core::ptHistory); if (buffer.size() > 0) { diff --git a/src/widget/form/settings/privacyform.cpp b/src/widget/form/settings/privacyform.cpp index 928ed7538..6d5632628 100644 --- a/src/widget/form/settings/privacyform.cpp +++ b/src/widget/form/settings/privacyform.cpp @@ -23,6 +23,7 @@ #include "src/widget/widget.h" #include "src/widget/form/setpassworddialog.h" #include +#include PrivacyForm::PrivacyForm() : GenericForm(tr("Privacy"), QPixmap(":/img/settings/privacy.png")) @@ -60,43 +61,75 @@ void PrivacyForm::onTypingNotificationEnabledUpdated() void PrivacyForm::onEncryptLogsUpdated() { bool encrytionState = bodyUI->cbEncryptHistory->isChecked(); + bool keepOldFile = false; if (encrytionState) { - if (!Core::getInstance()->isPasswordSet(Core::ptHistory)) + Settings::getInstance().setEncryptLogs(true); + + if (HistoryKeeper::isFileExist()) { - SetPasswordDialog dialog; - if (dialog.exec()) + QByteArray salt = Core::getSaltFromFile(HistoryKeeper::getHistoryPath()); + if (salt.size() != 0) { - QString pswd = dialog.getPassword(); - if (pswd.size() == 0) - encrytionState = false; - - Core::getInstance()->setPassword(pswd, Core::ptHistory); - } else { - encrytionState = false; - Core::getInstance()->clearPassword(Core::ptHistory); + if (QMessageBox::Ok == QMessageBox::warning(nullptr, tr("Encrypted log"), + tr("You already have history file.\nDo you want to try open it?"), + QMessageBox::Ok | QMessageBox::Cancel)) + { + keepOldFile = true; + bool exit = false; + + do + { + Widget::getInstance()->getPassword(tr("Encrypted log"), Core::ptHistory, reinterpret_cast(salt.data())); + exit = HistoryKeeper::checkPassword(); + if (!exit) + { + if (QMessageBox::warning(nullptr, tr("Encrypted log"), tr("Wrong password!\nTry again?"), + QMessageBox::Ok | QMessageBox::Cancel) != QMessageBox::Ok) + { + keepOldFile = false; + encrytionState = false; + exit = true; + QMessageBox::warning(nullptr, tr("Encrypetd log"), tr("Encrypted log will be disabled!")); + } + } + } while (!exit); + } else { + if (QMessageBox::warning(nullptr, tr("Encrypted log"), tr("Do you want to delete encrypted history file?"), + QMessageBox::Ok | QMessageBox::Cancel) != QMessageBox::Ok) + { + keepOldFile = true; + encrytionState = false; + } + } } } } - Settings::getInstance().setEncryptLogs(encrytionState); - if (encrytionState && !HistoryKeeper::checkPassword()) + if (encrytionState && !keepOldFile) { - 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)) + Core::getInstance()->clearPassword(Core::ptHistory); + + SetPasswordDialog dialog; + if (dialog.exec()) { - // TODO: ask user about reencryption with new password + QString pswd = dialog.getPassword(); + if (pswd.size() == 0) + encrytionState = false; + + Core::getInstance()->setPassword(pswd, Core::ptHistory); + } else { encrytionState = false; } } Settings::getInstance().setEncryptLogs(encrytionState); - bodyUI->cbEncryptHistory->setChecked(encrytionState); - if (encrytionState) - HistoryKeeper::resetInstance(); + HistoryKeeper::resetInstance(); + + Settings::getInstance().setEncryptLogs(encrytionState); + bodyUI->cbEncryptHistory->setChecked(encrytionState); if (!Settings::getInstance().getEncryptLogs()) Core::getInstance()->clearPassword(Core::ptHistory); diff --git a/src/widget/form/settings/privacysettings.ui b/src/widget/form/settings/privacysettings.ui index 84b4aecda..87b76d171 100644 --- a/src/widget/form/settings/privacysettings.ui +++ b/src/widget/form/settings/privacysettings.ui @@ -73,7 +73,7 @@ - false + true Encrypt Tox datafile @@ -83,13 +83,13 @@ - false + true Encrypt History - false + true diff --git a/src/widget/widget.h b/src/widget/widget.h index dd6bb3d94..366c5eb60 100644 --- a/src/widget/widget.h +++ b/src/widget/widget.h @@ -131,7 +131,6 @@ private slots: void playRingtone(); void onIconClick(QSystemTrayIcon::ActivationReason); void onUserAwayCheck(); - void getPassword(QString info, int passtype, uint8_t* salt); void onSetShowSystemTray(bool newValue); void onSplitterMoved(int pos, int index); From a432a16e018913d516e8004bc9c48fb3c58c5556 Mon Sep 17 00:00:00 2001 From: apprb Date: Sat, 8 Nov 2014 23:28:19 +0900 Subject: [PATCH 02/49] encrypted history: class constructor: refacroring --- src/core.cpp | 7 ++++-- src/misc/db/encrypteddb.cpp | 44 +++++++++++++++++++++++-------------- src/misc/db/encrypteddb.h | 3 ++- 3 files changed, 35 insertions(+), 19 deletions(-) diff --git a/src/core.cpp b/src/core.cpp index e5c5f611c..58937a51f 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -1811,7 +1811,8 @@ QByteArray Core::decryptData(const QByteArray& data, PasswordType passtype) return QByteArray(); int sz = data.size() - tox_pass_encryption_extra_length(); uint8_t decrypted[sz]; - if (tox_pass_key_decrypt(reinterpret_cast(data.data()), data.size(), pwsaltedkeys[passtype], decrypted) != sz) + int decr_size = tox_pass_key_decrypt(reinterpret_cast(data.data()), data.size(), pwsaltedkeys[passtype], decrypted); + if (decr_size != sz) { qWarning() << "Core::decryptData: decryption failed"; return QByteArray(); @@ -1907,5 +1908,7 @@ QByteArray Core::getSaltFromFile(QString filename) return QByteArray(); } - return QByteArray::fromRawData(reinterpret_cast(salt), tox_pass_salt_length()); + QByteArray res = QByteArray::fromRawData(reinterpret_cast(salt), tox_pass_salt_length()); + qDebug() << res.toBase64(); + return res; } diff --git a/src/misc/db/encrypteddb.cpp b/src/misc/db/encrypteddb.cpp index 43f69a5d3..8ae7ea123 100644 --- a/src/misc/db/encrypteddb.cpp +++ b/src/misc/db/encrypteddb.cpp @@ -28,24 +28,32 @@ qint64 EncryptedDb::plainChunkSize = 4096; qint64 EncryptedDb::encryptedChunkSize = EncryptedDb::plainChunkSize + tox_pass_encryption_extra_length(); EncryptedDb::EncryptedDb(const QString &fname, QList 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() << "writing old data"; + encrFile.setFileName(fileName); + encrFile.open(QIODevice::ReadOnly); fileContent = encrFile.readAll(); + encrFile.close(); } else { qWarning() << "corrupted history log file will be wiped!"; 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() @@ -63,23 +71,25 @@ QSqlQuery EncryptedDb::exec(const QString &query) return retQSqlQuery; } -bool EncryptedDb::pullFileContent() +bool EncryptedDb::pullFileContent(const QString &fname, QByteArray &buf) { qDebug() << "EncryptedDb::pullFileContent()"; - encrFile.open(QIODevice::ReadOnly); + + QFile dbFile(fname); + dbFile.open(QIODevice::ReadOnly); QByteArray fileContent; - while (!encrFile.atEnd()) + while (!dbFile.atEnd()) { - QByteArray encrChunk = encrFile.read(encryptedChunkSize); + QByteArray encrChunk = dbFile.read(encryptedChunkSize); qDebug() << "got chunk:" << encrChunk.size(); - buffer = Core::getInstance()->decryptData(encrChunk, Core::ptHistory); - if (buffer.size() > 0) + 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(); + buf = QByteArray(); return false; } } @@ -106,7 +116,7 @@ bool EncryptedDb::pullFileContent() if (!isGoodLine) { qWarning() << "Encrypted history log is corrupted: errors in content"; - buffer = QByteArray(); + buf = QByteArray(); return false; } } @@ -116,6 +126,8 @@ bool EncryptedDb::pullFileContent() QSqlQuery r = PlainDb::exec(line); } + dbFile.close(); + return true; } diff --git a/src/misc/db/encrypteddb.h b/src/misc/db/encrypteddb.h index 29948c2b4..bbac817c4 100644 --- a/src/misc/db/encrypteddb.h +++ b/src/misc/db/encrypteddb.h @@ -32,10 +32,11 @@ public: static bool check(const QString &fname); private: - bool pullFileContent(); + bool pullFileContent(const QString& fname, QByteArray &buf); void appendToEncrypted(const QString &sql); QFile encrFile; + QString fileName; static qint64 plainChunkSize; static qint64 encryptedChunkSize; From eb0b33be32e54695c8762435b48bfa3fef3d0aad Mon Sep 17 00:00:00 2001 From: apprb Date: Mon, 17 Nov 2014 19:57:23 +0900 Subject: [PATCH 03/49] Fixes --- src/core.cpp | 5 +++-- src/historykeeper.cpp | 7 ++++--- src/misc/db/encrypteddb.cpp | 39 ++++++++++++++++--------------------- src/misc/db/encrypteddb.h | 1 + 4 files changed, 25 insertions(+), 27 deletions(-) diff --git a/src/core.cpp b/src/core.cpp index 58937a51f..4c54494d1 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -482,7 +482,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); - emit core->groupMessageReceived(groupnumber, peernumber, CString::toString(message, length), false); } @@ -1239,7 +1238,9 @@ bool Core::loadConfiguration(QString path) if (salt.size() == 0) { // maybe we should handle this better - qWarning() << "Core: history db isn't encrypted, but encryption is set!! No history loaded..."; + Widget::getInstance()->showWarningMsgBox(tr("Encrypted History"), tr("No encrypted history file found.\nHistory will be disabled!")); + Settings::getInstance().setEncryptLogs(false); + Settings::getInstance().setEnableLogging(false); } else { diff --git a/src/historykeeper.cpp b/src/historykeeper.cpp index 5a83d6dbe..c846ff9d2 100644 --- a/src/historykeeper.cpp +++ b/src/historykeeper.cpp @@ -132,9 +132,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).toInt(); } HistoryKeeper::~HistoryKeeper() @@ -148,7 +149,7 @@ int HistoryKeeper::addChatEntry(const QString& chat, const QString& message, con int sender_id = getAliasID(sender); db->exec("BEGIN TRANSACTION;"); - db->exec(QString("INSERT INTO history (timestamp, chat_id, sender, message) ") + + 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)); diff --git a/src/misc/db/encrypteddb.cpp b/src/misc/db/encrypteddb.cpp index 8ae7ea123..b2e5e0328 100644 --- a/src/misc/db/encrypteddb.cpp +++ b/src/misc/db/encrypteddb.cpp @@ -24,8 +24,8 @@ #include #include -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 initList) : PlainDb(":memory:", initList), fileName(fname) @@ -65,7 +65,7 @@ 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; @@ -100,31 +100,15 @@ bool EncryptedDb::pullFileContent(const QString &fname, QByteArray &buf) 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"; - buf = 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(); @@ -184,3 +168,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; +} diff --git a/src/misc/db/encrypteddb.h b/src/misc/db/encrypteddb.h index bbac817c4..c27fb5c8c 100644 --- a/src/misc/db/encrypteddb.h +++ b/src/misc/db/encrypteddb.h @@ -34,6 +34,7 @@ public: private: bool pullFileContent(const QString& fname, QByteArray &buf); void appendToEncrypted(const QString &sql); + bool checkCmd(const QString &cmd); QFile encrFile; QString fileName; From 4567f8e5c6c24c692d97456743cfd4a5d4d90467 Mon Sep 17 00:00:00 2001 From: dubslow Date: Mon, 17 Nov 2014 22:18:24 -0600 Subject: [PATCH 04/49] move encryption to separate file for sanity --- qtox.pro | 1 + src/core.cpp | 283 ------------------------------------ src/coreencryption.cpp | 315 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 316 insertions(+), 283 deletions(-) create mode 100644 src/coreencryption.cpp diff --git a/qtox.pro b/qtox.pro index cbd4f3dc1..053680e11 100644 --- a/qtox.pro +++ b/qtox.pro @@ -183,6 +183,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 \ diff --git a/src/core.cpp b/src/core.cpp index 4c54494d1..a8b101227 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -1140,148 +1140,6 @@ QString Core::sanitize(QString name) return name; } -bool Core::loadConfiguration(QString path) -{ - // setting the profile is now the responsibility of the caller - QFile configurationFile(path); - qDebug() << "Core::loadConfiguration: reading from " << path; - - if (!configurationFile.exists()) { - qWarning() << "The Tox configuration file was not found"; - return true; - } - - if (!configurationFile.open(QIODevice::ReadOnly)) { - qCritical() << "File " << path << " cannot be opened"; - return true; - } - - qint64 fileSize = configurationFile.size(); - if (fileSize > 0) { - QByteArray data = configurationFile.readAll(); - int error = tox_load(tox, reinterpret_cast(data.data()), data.size()); - if (error < 0) - { - qWarning() << "Core: tox_load failed with error "<< error; - } - 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(data.data()), salt); - do - { - while (!pwsaltedkeys[ptMain]) - { - 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.")); - } - - error = tox_encrypted_key_load(tox, reinterpret_cast(data.data()), data.size(), pwsaltedkeys[ptMain]); - if (error != 0) - { - QMessageBox msgb; - msgb.moveToThread(qApp->thread()); - QPushButton *tryAgain = msgb.addButton(tr("Try Again"), QMessageBox::AcceptRole); - QPushButton *cancel = msgb.addButton(tr("Change profile"), QMessageBox::RejectRole); - QPushButton *wipe = msgb.addButton(tr("Reinit current profile"), QMessageBox::ActionRole); - msgb.setDefaultButton(tryAgain); - msgb.setWindowTitle(tr("Password error")); - msgb.setText(tr("Wrong password has been entered")); - // msgb.setInformativeText(tr("")); - - msgb.exec(); - - if (msgb.clickedButton() == tryAgain) - clearPassword(ptMain); - else if (msgb.clickedButton() == cancel) - { - configurationFile.close(); - return false; - } - else if (msgb.clickedButton() == wipe) - { - clearPassword(ptMain); - Settings::getInstance().setEncryptTox(false); - error = 0; - } - } - else - Settings::getInstance().setEncryptTox(true); - } while (error != 0); - } - } - configurationFile.close(); - - // set GUI with user and statusmsg - QString name = getUsername(); - if (!name.isEmpty()) - emit usernameSet(name); - - QString msg = getStatusMessage(); - if (!msg.isEmpty()) - emit statusMessageSet(msg); - - QString id = getSelfId().toString(); - if (!id.isEmpty()) - emit idSet(id); - - // tox core is already decrypted - if (Settings::getInstance().getEnableLogging() && Settings::getInstance().getEncryptLogs()) - { - bool error = true; - - // get salt - QByteArray salt = getSaltFromFile(HistoryKeeper::getHistoryPath()); - - if (salt.size() == 0) - { // maybe we should handle this better - Widget::getInstance()->showWarningMsgBox(tr("Encrypted History"), tr("No encrypted history file found.\nHistory will be disabled!")); - Settings::getInstance().setEncryptLogs(false); - Settings::getInstance().setEnableLogging(false); - } - else - { - do - { - while (!pwsaltedkeys[ptHistory]) - { - emit blockingGetPassword(tr("History Log decryption password"), Core::ptHistory, - reinterpret_cast(salt.data())); - if (!pwsaltedkeys[ptHistory]) - Widget::getInstance()->showWarningMsgBox(tr("Password error"), tr("Failed to setup password.\nEmpty password.")); - } - - if (!HistoryKeeper::checkPassword()) - { - if (QMessageBox::Ok == Widget::getInstance()->showWarningMsgBox(tr("Encrypted log"), - tr("Your history is encrypted with different password.\nDo you want to try another password?"), - QMessageBox::Ok | QMessageBox::Cancel)) - { - error = true; - clearPassword(ptHistory); - } - else - { - error = false; - clearPassword(ptHistory); - Widget::getInstance()->showWarningMsgBox(tr("History"), tr("Due to incorret password history will be disabled.")); - Settings::getInstance().setEncryptLogs(false); - Settings::getInstance().setEnableLogging(false); - } - } else { - error = false; - } - } while (error); - } - } - - loadFriends(); - return true; -} - void Core::saveConfiguration() { QString dir = Settings::getSettingsDirPath(); @@ -1308,60 +1166,6 @@ void Core::saveConfiguration() saveConfiguration(path); } -void Core::saveConfiguration(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]) - { - // probably zero chance event - 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(data), fileSize); - configurationFile.commit(); - delete[] data; - } -} - void Core::switchConfiguration(const QString& profile) { if (profile.isEmpty()) @@ -1765,70 +1569,6 @@ QList Core::splitMessage(const QString &message) 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(data.data()), data.size(), pwsaltedkeys[passtype], encrypted) == -1) - { - qWarning() << "Core::encryptData: encryption failed"; - return QByteArray(); - } - return QByteArray(reinterpret_cast(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(data.data()), data.size(), pwsaltedkeys[passtype], decrypted); - if (decr_size != sz) - { - qWarning() << "Core::decryptData: decryption failed"; - return QByteArray(); - } - return QByteArray(reinterpret_cast(decrypted), sz); -} - -bool Core::isPasswordSet(PasswordType passtype) -{ - if (pwsaltedkeys[passtype]) - return true; - - return false; -} - QString Core::getPeerName(const ToxID& id) const { QString name; @@ -1890,26 +1630,3 @@ void Core::resetCallSources() } } } - -QByteArray Core::getSaltFromFile(QString filename) -{ - qDebug() << filename; - QFile file(filename); - file.open(QIODevice::ReadOnly); - QByteArray data = file.read(tox_pass_encryption_extra_length()); - file.close(); - - qDebug() << "data size" << data.size(); - - uint8_t *salt = new uint8_t[tox_pass_salt_length()]; - int err = tox_get_salt(reinterpret_cast(data.data()), salt); - if (err) - { - qWarning() << "Core: can't get salt from" << filename << "header"; - return QByteArray(); - } - - QByteArray res = QByteArray::fromRawData(reinterpret_cast(salt), tox_pass_salt_length()); - qDebug() << res.toBase64(); - return res; -} diff --git a/src/coreencryption.cpp b/src/coreencryption.cpp new file mode 100644 index 000000000..b30f210ba --- /dev/null +++ b/src/coreencryption.cpp @@ -0,0 +1,315 @@ +/* + Copyright (C) 2013 by Maxim Biro + + This file is part of Tox Qt GUI. + + This program is free 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 */ +/* At least temporarily, the save and load functions are here as well */ + +#include "core.h" +#include "src/widget/widget.h" +#include +#include +#include "src/misc/settings.h" +#include "misc/cstring.h" +#include "historykeeper.h" +#include + +#include +#include +#include +#include + +bool Core::loadConfiguration(QString path) +{ + // setting the profile is now the responsibility of the caller + QFile configurationFile(path); + qDebug() << "Core::loadConfiguration: reading from " << path; + + if (!configurationFile.exists()) { + qWarning() << "The Tox configuration file was not found"; + return true; + } + + if (!configurationFile.open(QIODevice::ReadOnly)) { + qCritical() << "File " << path << " cannot be opened"; + return true; + } + + qint64 fileSize = configurationFile.size(); + if (fileSize > 0) { + QByteArray data = configurationFile.readAll(); + int error = tox_load(tox, reinterpret_cast(data.data()), data.size()); + if (error < 0) + { + qWarning() << "Core: tox_load failed with error "<< error; + } + 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(data.data()), salt); + do + { + while (!pwsaltedkeys[ptMain]) + { + 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.")); + } + + error = tox_encrypted_key_load(tox, reinterpret_cast(data.data()), data.size(), pwsaltedkeys[ptMain]); + if (error != 0) + { + QMessageBox msgb; + msgb.moveToThread(qApp->thread()); + QPushButton *tryAgain = msgb.addButton(tr("Try Again"), QMessageBox::AcceptRole); + QPushButton *cancel = msgb.addButton(tr("Change profile"), QMessageBox::RejectRole); + QPushButton *wipe = msgb.addButton(tr("Reinit current profile"), QMessageBox::ActionRole); + msgb.setDefaultButton(tryAgain); + msgb.setWindowTitle(tr("Password error")); + msgb.setText(tr("Wrong password has been entered")); + // msgb.setInformativeText(tr("")); + + msgb.exec(); + + if (msgb.clickedButton() == tryAgain) + clearPassword(ptMain); + else if (msgb.clickedButton() == cancel) + { + configurationFile.close(); + return false; + } + else if (msgb.clickedButton() == wipe) + { + clearPassword(ptMain); + Settings::getInstance().setEncryptTox(false); + error = 0; + } + } + else + Settings::getInstance().setEncryptTox(true); + } while (error != 0); + } + } + configurationFile.close(); + + // set GUI with user and statusmsg + QString name = getUsername(); + if (!name.isEmpty()) + emit usernameSet(name); + + QString msg = getStatusMessage(); + if (!msg.isEmpty()) + emit statusMessageSet(msg); + + QString id = getSelfId().toString(); + if (!id.isEmpty()) + emit idSet(id); + + // tox core is already decrypted + if (Settings::getInstance().getEnableLogging() && Settings::getInstance().getEncryptLogs()) + { + bool error = true; + + // get salt + QByteArray salt = getSaltFromFile(HistoryKeeper::getHistoryPath()); + + if (salt.size() == 0) + { // maybe we should handle this better + Widget::getInstance()->showWarningMsgBox(tr("Encrypted History"), tr("No encrypted history file found.\nHistory will be disabled!")); + Settings::getInstance().setEncryptLogs(false); + Settings::getInstance().setEnableLogging(false); + } + else + { + do + { + while (!pwsaltedkeys[ptHistory]) + { + emit blockingGetPassword(tr("History Log decryption password"), Core::ptHistory, + reinterpret_cast(salt.data())); + 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("Loggin"), tr("Due to incorret password history will be disabled")); + Settings::getInstance().setEncryptLogs(false); + Settings::getInstance().setEnableLogging(false); + } + } else { + error = false; + } + } while (error); + } + } + + loadFriends(); + return true; +} + +void Core::saveConfiguration(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]) + { + // probably zero chance event + 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(data), fileSize); + configurationFile.commit(); + delete[] data; + } +} + +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(data.data()), data.size(), pwsaltedkeys[passtype], encrypted) == -1) + { + qWarning() << "Core::encryptData: encryption failed"; + return QByteArray(); + } + return QByteArray(reinterpret_cast(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(data.data()), data.size(), pwsaltedkeys[passtype], decrypted); + if (decr_size != sz) + { + qWarning() << "Core::decryptData: decryption failed"; + return QByteArray(); + } + return QByteArray(reinterpret_cast(decrypted), sz); +} + +bool Core::isPasswordSet(PasswordType passtype) +{ + if (pwsaltedkeys[passtype]) + return true; + + return false; +} + +QByteArray Core::getSaltFromFile(QString filename) +{ + qDebug() << filename; + QFile file(filename); + file.open(QIODevice::ReadOnly); + QByteArray data = file.read(tox_pass_encryption_extra_length()); + file.close(); + + qDebug() << "data size" << data.size(); + + uint8_t *salt = new uint8_t[tox_pass_salt_length()]; + int err = tox_get_salt(reinterpret_cast(data.data()), salt); + if (err) + { + qWarning() << "Core: can't get salt from" << filename << "header"; + return QByteArray(); + } + + QByteArray res = QByteArray::fromRawData(reinterpret_cast(salt), tox_pass_salt_length()); + qDebug() << res.toBase64(); + return res; +} From 8985466ed367a6df672a9dbe434a16d043cc6dda Mon Sep 17 00:00:00 2001 From: dubslow Date: Tue, 18 Nov 2014 00:34:31 -0600 Subject: [PATCH 05/49] core encryption file is basically complete, I need to verify privacy tab --- src/core.cpp | 56 +++++++ src/core.h | 3 +- src/coreencryption.cpp | 372 +++++++++++++++++++---------------------- src/widget/widget.cpp | 1 - 4 files changed, 229 insertions(+), 203 deletions(-) diff --git a/src/core.cpp b/src/core.cpp index a8b101227..f162f59b6 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -1140,6 +1140,62 @@ QString Core::sanitize(QString name) return name; } +bool Core::loadConfiguration(QString path) +{ + // setting the profile is now the responsibility of the caller + QFile configurationFile(path); + qDebug() << "Core::loadConfiguration: reading from " << path; + + if (!configurationFile.exists()) { + qWarning() << "The Tox configuration file was not found"; + return true; + } + + if (!configurationFile.open(QIODevice::ReadOnly)) { + qCritical() << "File " << path << " cannot be opened"; + return true; + } + + qint64 fileSize = configurationFile.size(); + if (fileSize > 0) { + QByteArray data = configurationFile.readAll(); + int error = tox_load(tox, reinterpret_cast(data.data()), data.size()); + if (error < 0) + { + qWarning() << "Core: tox_load failed with error "<< error; + } + else if (error == 1) // Encrypted data save + { + if (!loadEncryptedSave(data)) + { + configurationFile.close(); + return false; + } + } + } + configurationFile.close(); + + // set GUI with user and statusmsg + QString name = getUsername(); + if (!name.isEmpty()) + emit usernameSet(name); + + QString msg = getStatusMessage(); + if (!msg.isEmpty()) + emit statusMessageSet(msg); + + QString id = getSelfId().toString(); + if (!id.isEmpty()) + emit idSet(id); + + // tox core is already decrypted + if (Settings::getInstance().getEnableLogging() && Settings::getInstance().getEncryptLogs()) + checkEncryptedHistory(); + + loadFriends(); + return true; +} + void Core::saveConfiguration() { QString dir = Settings::getSettingsDirPath(); diff --git a/src/core.h b/src/core.h index 460e11e30..af5a40d41 100644 --- a/src/core.h +++ b/src/core.h @@ -143,7 +143,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); @@ -264,6 +263,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(); diff --git a/src/coreencryption.cpp b/src/coreencryption.cpp index b30f210ba..3855f6270 100644 --- a/src/coreencryption.cpp +++ b/src/coreencryption.cpp @@ -29,203 +29,7 @@ #include #include #include -#include - -bool Core::loadConfiguration(QString path) -{ - // setting the profile is now the responsibility of the caller - QFile configurationFile(path); - qDebug() << "Core::loadConfiguration: reading from " << path; - - if (!configurationFile.exists()) { - qWarning() << "The Tox configuration file was not found"; - return true; - } - - if (!configurationFile.open(QIODevice::ReadOnly)) { - qCritical() << "File " << path << " cannot be opened"; - return true; - } - - qint64 fileSize = configurationFile.size(); - if (fileSize > 0) { - QByteArray data = configurationFile.readAll(); - int error = tox_load(tox, reinterpret_cast(data.data()), data.size()); - if (error < 0) - { - qWarning() << "Core: tox_load failed with error "<< error; - } - 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(data.data()), salt); - do - { - while (!pwsaltedkeys[ptMain]) - { - 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.")); - } - - error = tox_encrypted_key_load(tox, reinterpret_cast(data.data()), data.size(), pwsaltedkeys[ptMain]); - if (error != 0) - { - QMessageBox msgb; - msgb.moveToThread(qApp->thread()); - QPushButton *tryAgain = msgb.addButton(tr("Try Again"), QMessageBox::AcceptRole); - QPushButton *cancel = msgb.addButton(tr("Change profile"), QMessageBox::RejectRole); - QPushButton *wipe = msgb.addButton(tr("Reinit current profile"), QMessageBox::ActionRole); - msgb.setDefaultButton(tryAgain); - msgb.setWindowTitle(tr("Password error")); - msgb.setText(tr("Wrong password has been entered")); - // msgb.setInformativeText(tr("")); - - msgb.exec(); - - if (msgb.clickedButton() == tryAgain) - clearPassword(ptMain); - else if (msgb.clickedButton() == cancel) - { - configurationFile.close(); - return false; - } - else if (msgb.clickedButton() == wipe) - { - clearPassword(ptMain); - Settings::getInstance().setEncryptTox(false); - error = 0; - } - } - else - Settings::getInstance().setEncryptTox(true); - } while (error != 0); - } - } - configurationFile.close(); - - // set GUI with user and statusmsg - QString name = getUsername(); - if (!name.isEmpty()) - emit usernameSet(name); - - QString msg = getStatusMessage(); - if (!msg.isEmpty()) - emit statusMessageSet(msg); - - QString id = getSelfId().toString(); - if (!id.isEmpty()) - emit idSet(id); - - // tox core is already decrypted - if (Settings::getInstance().getEnableLogging() && Settings::getInstance().getEncryptLogs()) - { - bool error = true; - - // get salt - QByteArray salt = getSaltFromFile(HistoryKeeper::getHistoryPath()); - - if (salt.size() == 0) - { // maybe we should handle this better - Widget::getInstance()->showWarningMsgBox(tr("Encrypted History"), tr("No encrypted history file found.\nHistory will be disabled!")); - Settings::getInstance().setEncryptLogs(false); - Settings::getInstance().setEnableLogging(false); - } - else - { - do - { - while (!pwsaltedkeys[ptHistory]) - { - emit blockingGetPassword(tr("History Log decryption password"), Core::ptHistory, - reinterpret_cast(salt.data())); - 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("Loggin"), tr("Due to incorret password history will be disabled")); - Settings::getInstance().setEncryptLogs(false); - Settings::getInstance().setEnableLogging(false); - } - } else { - error = false; - } - } while (error); - } - } - - loadFriends(); - return true; -} - -void Core::saveConfiguration(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]) - { - // probably zero chance event - 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(data), fileSize); - configurationFile.commit(); - delete[] data; - } -} +#include void Core::setPassword(QString& password, PasswordType passtype, uint8_t* salt) { @@ -293,14 +97,11 @@ bool Core::isPasswordSet(PasswordType passtype) QByteArray Core::getSaltFromFile(QString filename) { - qDebug() << filename; QFile file(filename); file.open(QIODevice::ReadOnly); QByteArray data = file.read(tox_pass_encryption_extra_length()); file.close(); - qDebug() << "data size" << data.size(); - uint8_t *salt = new uint8_t[tox_pass_salt_length()]; int err = tox_get_salt(reinterpret_cast(data.data()), salt); if (err) @@ -310,6 +111,175 @@ QByteArray Core::getSaltFromFile(QString filename) } QByteArray res = QByteArray::fromRawData(reinterpret_cast(salt), tox_pass_salt_length()); - qDebug() << res.toBase64(); 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 this profile:", "used in load() when no pw is already set")); + 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(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(data.data()), salt); + QInputDialog dialog; + dialog.moveToThread(qApp->thread()); + dialog.setOkButtonText(tr("Set password")); + dialog.setCancelButtonText(tr("Change profile")); + dialog.setWindowTitle(tr("Enter your password")); + dialog.setInputMode(QInputDialog::TextInput); + dialog.setTextEchoMode(QLineEdit::Password); + do + { + dialog.setTextValue(QString()); + dialog.setLabelText(dialogtxt); + + int val = dialog.exec(); + + if (val == QDialog::Accepted) + { + QString pw = dialog.textValue(); + setPassword(pw, ptMain, salt); + } + else + { + clearPassword(ptMain); + return false; + } + + error = tox_encrypted_key_load(tox, reinterpret_cast(data.data()), data.size(), pwsaltedkeys[ptMain]); + dialogtxt = a + " " + b; + } while (error != 0); + + Settings::getInstance().setEncryptTox(true); + return true; +} + +void Core::checkEncryptedHistory() +{ + QByteArray salt = getSaltFromFile(HistoryKeeper::getHistoryPath()); + + if (salt.size() == 0) + { // maybe we should handle this better + Widget::getInstance()->showWarningMsgBox(tr("Encrypted History"), tr("No encrypted history file found.\nHistory will be disabled!")); + Settings::getInstance().setEncryptLogs(false); + Settings::getInstance().setEnableLogging(false); + return; + } + + QString a(tr("Please enter the password for the chat logs.", "used in load() when no hist pw set")); + QString b(tr("The previous password is incorrect; please try again:", "used on retries in load()")); + QString dialogtxt; + + if (pwsaltedkeys[ptHistory]) + { + if (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; + + QInputDialog dialog; + dialog.moveToThread(qApp->thread()); + dialog.setOkButtonText(tr("Set password")); + dialog.setCancelButtonText(tr("Disable history")); + dialog.setWindowTitle(tr("Enter your password")); + dialog.setInputMode(QInputDialog::TextInput); + dialog.setTextEchoMode(QLineEdit::Password); + bool error = true; + do + { + dialog.setLabelText(dialogtxt); + dialog.setTextValue(QString()); + + int val = dialog.exec(); + + if (val == QDialog::Accepted) + { + QString pw = dialog.textValue(); + setPassword(pw, ptHistory, reinterpret_cast(salt.data())); + } + else + { + clearPassword(ptHistory); + Settings::getInstance().setEncryptLogs(false); + Settings::getInstance().setEnableLogging(false); + return; + } + + error = !HistoryKeeper::checkPassword(); + dialogtxt = a + " " + b; + } while (error); +} + +void Core::saveConfiguration(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]) + { + // probably zero chance event + 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(data), fileSize); + configurationFile.commit(); + delete[] data; + } +} diff --git a/src/widget/widget.cpp b/src/widget/widget.cpp index 4736f3642..95b2c710a 100644 --- a/src/widget/widget.cpp +++ b/src/widget/widget.cpp @@ -241,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, SIGNAL(messageSentResult(int,QString,int)), this, SLOT(onMessageSendResult(int,QString,int))); connect(core, SIGNAL(groupSentResult(int,QString,int)), this, SLOT(onGroupSendResult(int,QString,int))); From dde410bb9ce5f4b426ef561d7fb88b5af2cca46a Mon Sep 17 00:00:00 2001 From: dubslow Date: Sat, 22 Nov 2014 02:46:22 -0600 Subject: [PATCH 06/49] incremental --- qtox.pro | 3 - src/widget/form/inputpassworddialog.cpp | 38 ---------- src/widget/form/inputpassworddialog.h | 40 ---------- src/widget/form/inputpassworddialog.ui | 97 ------------------------ src/widget/form/settings/privacyform.cpp | 85 +++------------------ src/widget/widget.cpp | 15 ---- 6 files changed, 11 insertions(+), 267 deletions(-) delete mode 100644 src/widget/form/inputpassworddialog.cpp delete mode 100644 src/widget/form/inputpassworddialog.h delete mode 100644 src/widget/form/inputpassworddialog.ui diff --git a/qtox.pro b/qtox.pro index 053680e11..cbeef8096 100644 --- a/qtox.pro +++ b/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 @@ -153,7 +152,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 \ @@ -220,7 +218,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 \ diff --git a/src/widget/form/inputpassworddialog.cpp b/src/widget/form/inputpassworddialog.cpp deleted file mode 100644 index 9aed8f54b..000000000 --- a/src/widget/form/inputpassworddialog.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - Copyright (C) 2014 by Project Tox - - 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(); -} diff --git a/src/widget/form/inputpassworddialog.h b/src/widget/form/inputpassworddialog.h deleted file mode 100644 index 00ce6fc99..000000000 --- a/src/widget/form/inputpassworddialog.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - Copyright (C) 2014 by Project Tox - - 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 - -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 diff --git a/src/widget/form/inputpassworddialog.ui b/src/widget/form/inputpassworddialog.ui deleted file mode 100644 index 69ff9a2ea..000000000 --- a/src/widget/form/inputpassworddialog.ui +++ /dev/null @@ -1,97 +0,0 @@ - - - InputPasswordDialog - - - - 0 - 0 - 400 - 123 - - - - Password Dialog - - - true - - - - - - Input password: - - - - - - - IBeamCursor - - - QLineEdit::Password - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - - buttonBox - accepted() - InputPasswordDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - InputPasswordDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff --git a/src/widget/form/settings/privacyform.cpp b/src/widget/form/settings/privacyform.cpp index 6d5632628..5eb5e3a24 100644 --- a/src/widget/form/settings/privacyform.cpp +++ b/src/widget/form/settings/privacyform.cpp @@ -60,86 +60,23 @@ void PrivacyForm::onTypingNotificationEnabledUpdated() void PrivacyForm::onEncryptLogsUpdated() { - bool encrytionState = bodyUI->cbEncryptHistory->isChecked(); - bool keepOldFile = false; + bool encryption = bodyUI->cbEncryptHistory->isChecked(); - if (encrytionState) + if (encryption) { - Settings::getInstance().setEncryptLogs(true); - - if (HistoryKeeper::isFileExist()) - { - QByteArray salt = Core::getSaltFromFile(HistoryKeeper::getHistoryPath()); - if (salt.size() != 0) - { - if (QMessageBox::Ok == QMessageBox::warning(nullptr, tr("Encrypted log"), - tr("You already have history file.\nDo you want to try open it?"), - QMessageBox::Ok | QMessageBox::Cancel)) - { - keepOldFile = true; - bool exit = false; - - do - { - Widget::getInstance()->getPassword(tr("Encrypted log"), Core::ptHistory, reinterpret_cast(salt.data())); - exit = HistoryKeeper::checkPassword(); - if (!exit) - { - if (QMessageBox::warning(nullptr, tr("Encrypted log"), tr("Wrong password!\nTry again?"), - QMessageBox::Ok | QMessageBox::Cancel) != QMessageBox::Ok) - { - keepOldFile = false; - encrytionState = false; - exit = true; - QMessageBox::warning(nullptr, tr("Encrypetd log"), tr("Encrypted log will be disabled!")); - } - } - } while (!exit); - } else { - if (QMessageBox::warning(nullptr, tr("Encrypted log"), tr("Do you want to delete encrypted history file?"), - QMessageBox::Ok | QMessageBox::Cancel) != QMessageBox::Ok) - { - keepOldFile = true; - encrytionState = false; - } - } - } - } + } - - if (encrytionState && !keepOldFile) + else { - Core::getInstance()->clearPassword(Core::ptHistory); - - SetPasswordDialog dialog; - if (dialog.exec()) - { - QString pswd = dialog.getPassword(); - if (pswd.size() == 0) - encrytionState = false; - - Core::getInstance()->setPassword(pswd, Core::ptHistory); - } else { - encrytionState = false; - } + } - - Settings::getInstance().setEncryptLogs(encrytionState); - - HistoryKeeper::resetInstance(); - - Settings::getInstance().setEncryptLogs(encrytionState); - bodyUI->cbEncryptHistory->setChecked(encrytionState); - - if (!Settings::getInstance().getEncryptLogs()) - Core::getInstance()->clearPassword(Core::ptHistory); } void PrivacyForm::onEncryptToxUpdated() { - bool encrytionState = bodyUI->cbEncryptTox->isChecked(); + bool encryptionState = bodyUI->cbEncryptTox->isChecked(); - if (encrytionState) + if (encryptionState) { if (!Core::getInstance()->isPasswordSet(Core::ptMain)) { @@ -148,18 +85,18 @@ void PrivacyForm::onEncryptToxUpdated() { QString pswd = dialog.getPassword(); if (pswd.size() == 0) - encrytionState = false; + encryptionState = false; Core::getInstance()->setPassword(pswd, Core::ptMain); } else { - encrytionState = false; + encryptionState = false; Core::getInstance()->clearPassword(Core::ptMain); } } } - bodyUI->cbEncryptTox->setChecked(encrytionState); - Settings::getInstance().setEncryptTox(encrytionState); + bodyUI->cbEncryptTox->setChecked(encryptionState); + Settings::getInstance().setEncryptTox(encryptionState); if (!Settings::getInstance().getEncryptTox()) Core::getInstance()->clearPassword(Core::ptMain); diff --git a/src/widget/widget.cpp b/src/widget/widget.cpp index 95b2c710a..b89ef096f 100644 --- a/src/widget/widget.cpp +++ b/src/widget/widget.cpp @@ -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" @@ -1165,20 +1164,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(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::onSetShowSystemTray(bool newValue){ icon->setVisible(newValue); } From bafca96554da062b7cccc40ae22c44b4828061a5 Mon Sep 17 00:00:00 2001 From: dubslow Date: Thu, 27 Nov 2014 10:38:23 -0500 Subject: [PATCH 07/49] Incremental work, starting to take shape --- qtox.pro | 8 ++-- src/core.h | 1 + src/coreencryption.cpp | 9 ++++ src/historykeeper.cpp | 18 ++++--- src/historykeeper.h | 1 + src/widget/form/checkcontinue.cpp | 23 +++++++++ src/widget/form/checkcontinue.h | 22 +++++++++ src/widget/form/setpassworddialog.cpp | 15 ++++-- src/widget/form/setpassworddialog.h | 2 +- src/widget/form/setpassworddialog.ui | 57 +++++++++++++--------- src/widget/form/settings/identityform.cpp | 16 ++----- src/widget/form/settings/identityform.h | 1 - src/widget/form/settings/privacyform.cpp | 58 +++++++++++++++++++++-- src/widget/toxsave.cpp | 7 +-- 14 files changed, 176 insertions(+), 62 deletions(-) create mode 100644 src/widget/form/checkcontinue.cpp create mode 100644 src/widget/form/checkcontinue.h diff --git a/qtox.pro b/qtox.pro index cbeef8096..7211d468d 100644 --- a/qtox.pro +++ b/qtox.pro @@ -83,7 +83,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 @@ -163,7 +163,8 @@ HEADERS += src/widget/form/addfriendform.h \ src/autoupdate.h \ src/misc/serialize.h \ src/widget/form/settings/advancedform.h \ - src/audio.h + src/audio.h \ + src/widget/form/checkcontinue.h SOURCES += \ src/widget/form/addfriendform.cpp \ @@ -230,7 +231,8 @@ SOURCES += \ src/autoupdate.cpp \ src/misc/serialize.cpp \ src/widget/form/settings/advancedform.cpp \ - src/audio.cpp + src/audio.cpp \ + src/widget/form/checkcontinue.cpp contains(DEFINES, QTOX_PLATFORM_EXT) { HEADERS += src/platform/timer.h diff --git a/src/core.h b/src/core.h index af5a40d41..ed50b1033 100644 --- a/src/core.h +++ b/src/core.h @@ -135,6 +135,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); diff --git a/src/coreencryption.cpp b/src/coreencryption.cpp index 3855f6270..b2d9e2c0d 100644 --- a/src/coreencryption.cpp +++ b/src/coreencryption.cpp @@ -50,6 +50,15 @@ void Core::setPassword(QString& password, PasswordType passtype, uint8_t* salt) password.clear(); } +void Core::useOtherPassword(PasswordType type) +{ + clearPassword(type); + if (type == ptHistory) + pwsaltedkeys[ptHistory] = pwsaltedkeys[ptMain]; + else if (type == ptMain) + pwsaltedkeys[ptMain] = pwsaltedkeys[ptHistory]; +} + void Core::clearPassword(PasswordType passtype) { if (pwsaltedkeys[passtype]) diff --git a/src/historykeeper.cpp b/src/historykeeper.cpp index c846ff9d2..9324a52e8 100644 --- a/src/historykeeper.cpp +++ b/src/historykeeper.cpp @@ -71,17 +71,9 @@ HistoryKeeper *HistoryKeeper::getInstance() bool HistoryKeeper::checkPassword() { if (Settings::getInstance().getEnableLogging()) - { if (Settings::getInstance().getEncryptLogs()) - { - QString dbpath = getHistoryPath(); - return EncryptedDb::check(dbpath); - } else { - return true; - } - } else { - return true; - } + return EncryptedDb::check(getHistoryPath()); + return true; } HistoryKeeper::HistoryKeeper(GenericDdInterface *db_) : @@ -143,6 +135,12 @@ HistoryKeeper::~HistoryKeeper() delete db; } +void HistoryKeeper::reencrypt(QString newpw) +{ + // this needs to appropriately set the core password as well + // if newpw.isEmpty(), then use the other core password +} + int HistoryKeeper::addChatEntry(const QString& chat, const QString& message, const QString& sender, const QDateTime &dt, bool isSent) { int chat_id = getChatID(chat, ctSingle).first; diff --git a/src/historykeeper.h b/src/historykeeper.h index a98431b1a..9c67957b6 100644 --- a/src/historykeeper.h +++ b/src/historykeeper.h @@ -52,6 +52,7 @@ public: int addGroupChatEntry(const QString& chat, const QString& message, const QString& sender, const QDateTime &dt); QList getChatHistory(ChatType ct, const QString &chat, const QDateTime &time_from, const QDateTime &time_to); void markAsSent(int m_id); + void reencrypt(QString newpw); void setSyncType(Db::syncType sType); diff --git a/src/widget/form/checkcontinue.cpp b/src/widget/form/checkcontinue.cpp new file mode 100644 index 000000000..d42afb327 --- /dev/null +++ b/src/widget/form/checkcontinue.cpp @@ -0,0 +1,23 @@ +/* + Copyright (C) 2014 by Project Tox + + 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 + +bool checkContinue(const QString& title, const QString& msg, QWidget* parent = nullptr) +{ + QMessageBox::StandardButton resp = QMessageBox::question(parent, title, msg, QMessageBox::Yes | QMessageBox::No, QMessageBox::No); + return resp == QMessageBox::Yes; +} diff --git a/src/widget/form/checkcontinue.h b/src/widget/form/checkcontinue.h new file mode 100644 index 000000000..46c182bfa --- /dev/null +++ b/src/widget/form/checkcontinue.h @@ -0,0 +1,22 @@ +/* + Copyright (C) 2014 by Project Tox + + 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 CHECKCONTINUE_H +#define CHECKCONTINUE_H + +bool checkContinue(const QString& title, const QString& msg, QWidget* parent = nullptr); + +#endif diff --git a/src/widget/form/setpassworddialog.cpp b/src/widget/form/setpassworddialog.cpp index 402ff0fa7..f8624dc84 100644 --- a/src/widget/form/setpassworddialog.cpp +++ b/src/widget/form/setpassworddialog.cpp @@ -18,14 +18,23 @@ #include "ui_setpassworddialog.h" #include -SetPasswordDialog::SetPasswordDialog(QWidget *parent) : - QDialog(parent), - ui(new Ui::SetPasswordDialog) +SetPasswordDialog::SetPasswordDialog(QString body, QString extraButton, QWidget* parent) + : QDialog(parent) + , ui(new Ui::SetPasswordDialog) { ui->setupUi(this); connect(ui->passwordlineEdit, SIGNAL(textChanged(QString)), this, SLOT(onPasswordEdit())); connect(ui->repasswordlineEdit, SIGNAL(textChanged(QString)), this, SLOT(onPasswordEdit())); + + ui->body->setText(body); + + if (!extraButton.isEmpty()) + { + QPushButton* third = new QPushButton(extraButton); + ui->buttonBox->addButton(third, QDialogButtonBox::YesRole); + connect(third, &QPushButton::clicked, this, [=](){this->done(2);}); + } } SetPasswordDialog::~SetPasswordDialog() diff --git a/src/widget/form/setpassworddialog.h b/src/widget/form/setpassworddialog.h index a0d388f33..967b7d3e0 100644 --- a/src/widget/form/setpassworddialog.h +++ b/src/widget/form/setpassworddialog.h @@ -28,7 +28,7 @@ class SetPasswordDialog : public QDialog Q_OBJECT public: - explicit SetPasswordDialog(QWidget *parent = 0); + explicit SetPasswordDialog(QString body, QString extraButton, QWidget* parent = 0); ~SetPasswordDialog(); QString getPassword(); diff --git a/src/widget/form/setpassworddialog.ui b/src/widget/form/setpassworddialog.ui index 6dfe441a8..02d803bb5 100644 --- a/src/widget/form/setpassworddialog.ui +++ b/src/widget/form/setpassworddialog.ui @@ -11,39 +11,50 @@ - Dialog + Set your password true - - - Type Password - - - - - - - QLineEdit::Password - - + - - - Repeat Password - - + + + + + Type password: + + + + + + + QLineEdit::Password + + + + - - - QLineEdit::Password - - + + + + + Repeat Password + + + + + + + QLineEdit::Password + + + + diff --git a/src/widget/form/settings/identityform.cpp b/src/widget/form/settings/identityform.cpp index e1f46d7e4..3690f6ab6 100644 --- a/src/widget/form/settings/identityform.cpp +++ b/src/widget/form/settings/identityform.cpp @@ -20,6 +20,7 @@ #include "src/widget/form/settingswidget.h" #include "src/misc/settings.h" #include "src/widget/croppinglabel.h" +#include "src/widget/form/checkcontinue.h" #include "src/widget/widget.h" #include "src/historykeeper.h" #include "src/misc/style.h" @@ -29,7 +30,6 @@ #include #include #include -#include IdentityForm::IdentityForm() : GenericForm(tr("Identity"), QPixmap(":/img/settings/identity.png")) @@ -149,7 +149,7 @@ void IdentityForm::onRenameClicked() QDir dir(Settings::getSettingsDirPath()); QString file = dir.filePath(name+Core::TOX_EXT); if (!QFile::exists(file) || checkContinue(tr("Profile already exists", "rename confirm title"), - tr("A profile named \"%1\" already exists. Do you want to erase it?", "rename confirm text").arg(cur))) + tr("A profile named \"%1\" already exists. Do you want to erase it?", "rename confirm text").arg(cur)), this) { QFile::rename(dir.filePath(cur+Core::TOX_EXT), file); bodyUI->profiles->setItemText(bodyUI->profiles->currentIndex(), name); @@ -172,7 +172,7 @@ 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))) + // if (!checkContinue(tr("Overwriting a file"), tr("Are you sure you want to overwrite %1?").arg(path)), this) // return; success = QFile::remove(path); if (!success) @@ -196,7 +196,7 @@ 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"))) + tr("Are you sure you want to delete this profile?","deletion confirmation text"), this)) { QString profile = bodyUI->profiles->currentText(); QDir dir(Settings::getSettingsDirPath()); @@ -235,7 +235,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"), - tr("A profile named \"%1\" already exists. Do you want to erase it?", "import confirm text").arg(profile))) + tr("A profile named \"%1\" already exists. Do you want to erase it?", "import confirm text").arg(profile), this)) return; QFile::copy(path, profilePath); @@ -247,12 +247,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); diff --git a/src/widget/form/settings/identityform.h b/src/widget/form/settings/identityform.h index b30ec8070..5bb48230e 100644 --- a/src/widget/form/settings/identityform.h +++ b/src/widget/form/settings/identityform.h @@ -66,7 +66,6 @@ private slots: void onDeleteClicked(); void onImportClicked(); void onNewClicked(); - bool checkContinue(const QString& title, const QString& msg); void disableSwitching(); void enableSwitching(); diff --git a/src/widget/form/settings/privacyform.cpp b/src/widget/form/settings/privacyform.cpp index 5eb5e3a24..961a700cf 100644 --- a/src/widget/form/settings/privacyform.cpp +++ b/src/widget/form/settings/privacyform.cpp @@ -22,6 +22,7 @@ #include "src/core.h" #include "src/widget/widget.h" #include "src/widget/form/setpassworddialog.h" +#include "src/widget/form/checkcontinue.h" #include #include @@ -60,16 +61,65 @@ void PrivacyForm::onTypingNotificationEnabledUpdated() void PrivacyForm::onEncryptLogsUpdated() { - bool encryption = bodyUI->cbEncryptHistory->isChecked(); + Core* core = Core::getInstance(); - if (encryption) + if (bodyUI->cbEncryptHistory->isChecked()) { - + if (!core->isPasswordSet(Core::ptHistory)) + { + SetPasswordDialog* dialog; + QString body = tr("Please set your new chat log password:"); + if (core->isPasswordSet(Core::ptMain)) + dialog = new SetPasswordDialog(body, tr("Use datafile password", "pushbutton text")); + else + dialog = new SetPasswordDialog(body, QString()); + + if (int r = dialog->exec()) + { + QString newpw; + if (r != 2) + newpw = dialog->getPassword(); + delete dialog; + if (r != 2 && newpw.isEmpty()) + goto fail; + + Settings::getInstance().setEncryptLogs(true); + bodyUI->cbEncryptHistory->setChecked(true); + // not logically necessary, but more consistent (esp. if the logic changes) + if (!HistoryKeeper::checkPassword()) + if (checkContinue(tr("Old encrypted chat logs", "title"), + tr("Would you like to re-encrypt your old chat logs?\nOtherwise they will be deleted.", "body"))) + { + HistoryKeeper::getInstance()->reencrypt(newpw); + // will set core and reset itself + return; + } + // @apprb you resetInstance() in the old code but wouldn't that wipe out the current unencrypted history? + // that should of course just become encrypted + if (newpw.isEmpty()) + core->useOtherPassword(Core::ptHistory); + else + core->setPassword(newpw, Core::ptHistory); + return; + } + else + delete dialog; + } } else { - + if (checkContinue(tr("Old encrypted chat logs", "title"), tr("Would you like to un-encrypt your chat logs?\nOtherwise they will be deleted."))) + { + // TODO: how to unencrypt current encrypted logs + } + else + HistoryKeeper::resetInstance(); } + + fail: + core->clearPassword(Core::ptHistory); + Settings::getInstance().setEncryptLogs(false); + bodyUI->cbEncryptHistory->setChecked(false); } void PrivacyForm::onEncryptToxUpdated() diff --git a/src/widget/toxsave.cpp b/src/widget/toxsave.cpp index 1b4a38fa9..f9ef24a9e 100644 --- a/src/widget/toxsave.cpp +++ b/src/widget/toxsave.cpp @@ -18,6 +18,7 @@ #include "widget.h" #include "src/core.h" #include "src/misc/settings.h" +#include "src/widget/form/checkcontinue.h" #include #include #include @@ -30,12 +31,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(); From 4eb30364147086d46edc44c6afe058959dc8c3c1 Mon Sep 17 00:00:00 2001 From: dubslow Date: Thu, 27 Nov 2014 11:22:14 -0500 Subject: [PATCH 08/49] Ah, much cleaner --- src/coreencryption.cpp | 6 + src/widget/form/setpassworddialog.cpp | 3 +- src/widget/form/settings/privacyform.cpp | 139 ++++++++++++-------- src/widget/form/settings/privacyform.h | 2 + src/widget/form/settings/privacysettings.ui | 6 +- 5 files changed, 95 insertions(+), 61 deletions(-) diff --git a/src/coreencryption.cpp b/src/coreencryption.cpp index b2d9e2c0d..0ee3906e9 100644 --- a/src/coreencryption.cpp +++ b/src/coreencryption.cpp @@ -48,6 +48,9 @@ void Core::setPassword(QString& password, PasswordType passtype, uint8_t* salt) tox_derive_key_from_pass(str.data(), str.size(), pwsaltedkeys[passtype]); password.clear(); + + if (passtype == ptMain) + saveConfiguration(); } void Core::useOtherPassword(PasswordType type) @@ -56,7 +59,10 @@ void Core::useOtherPassword(PasswordType type) if (type == ptHistory) pwsaltedkeys[ptHistory] = pwsaltedkeys[ptMain]; else if (type == ptMain) + { pwsaltedkeys[ptMain] = pwsaltedkeys[ptHistory]; + saveConfiguration(); + } } void Core::clearPassword(PasswordType passtype) diff --git a/src/widget/form/setpassworddialog.cpp b/src/widget/form/setpassworddialog.cpp index f8624dc84..2e401b79f 100644 --- a/src/widget/form/setpassworddialog.cpp +++ b/src/widget/form/setpassworddialog.cpp @@ -44,7 +44,8 @@ SetPasswordDialog::~SetPasswordDialog() void SetPasswordDialog::onPasswordEdit() { - if (ui->passwordlineEdit->text() == ui->repasswordlineEdit->text()) + if ( !ui->passwordlineEdit->text().isEmpty() + && ui->passwordlineEdit->text() == ui->repasswordlineEdit->text()) ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); else ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); diff --git a/src/widget/form/settings/privacyform.cpp b/src/widget/form/settings/privacyform.cpp index 961a700cf..158f03683 100644 --- a/src/widget/form/settings/privacyform.cpp +++ b/src/widget/form/settings/privacyform.cpp @@ -59,6 +59,44 @@ void PrivacyForm::onTypingNotificationEnabledUpdated() Settings::getInstance().setTypingNotification(bodyUI->cbTypingNotification->isChecked()); } +bool PrivacyForm::setChatLogsPassword() +{ + SetPasswordDialog* dialog; + QString body = tr("Please set your new chat log password:"); + if (core->isPasswordSet(Core::ptMain)) + dialog = new SetPasswordDialog(body, tr("Use data file password", "pushbutton text"), this); + else + dialog = new SetPasswordDialog(body, QString(), this); + + if (int r = dialog->exec()) + { + QString newpw = dialog->getPassword(); + delete dialog; + + if (!HistoryKeeper::checkPassword()) + if (checkContinue(tr("Old encrypted chat logs", "title"), + tr("Would you like to re-encrypt your old chat logs?\nOtherwise they will be deleted.", "body"))) + { + HistoryKeeper::getInstance()->reencrypt(r == 2 ? QString() : newpw); + // will set core and reset itself + return true; + } + // @apprb you resetInstance() in the old code but wouldn't that wipe out the current unencrypted history? + // current history should of course just become encrypted + if (r == 2) + core->useOtherPassword(Core::ptHistory); + else + core->setPassword(newpw, Core::ptHistory); + // HistoryKeeper::encryptPlain(); + return true; + } + else + { + delete dialog; + return false; + } +} + void PrivacyForm::onEncryptLogsUpdated() { Core* core = Core::getInstance(); @@ -67,43 +105,13 @@ void PrivacyForm::onEncryptLogsUpdated() { if (!core->isPasswordSet(Core::ptHistory)) { - SetPasswordDialog* dialog; - QString body = tr("Please set your new chat log password:"); - if (core->isPasswordSet(Core::ptMain)) - dialog = new SetPasswordDialog(body, tr("Use datafile password", "pushbutton text")); - else - dialog = new SetPasswordDialog(body, QString()); - - if (int r = dialog->exec()) + if (setChatLogsPassword()) { - QString newpw; - if (r != 2) - newpw = dialog->getPassword(); - delete dialog; - if (r != 2 && newpw.isEmpty()) - goto fail; - Settings::getInstance().setEncryptLogs(true); bodyUI->cbEncryptHistory->setChecked(true); // not logically necessary, but more consistent (esp. if the logic changes) - if (!HistoryKeeper::checkPassword()) - if (checkContinue(tr("Old encrypted chat logs", "title"), - tr("Would you like to re-encrypt your old chat logs?\nOtherwise they will be deleted.", "body"))) - { - HistoryKeeper::getInstance()->reencrypt(newpw); - // will set core and reset itself - return; - } - // @apprb you resetInstance() in the old code but wouldn't that wipe out the current unencrypted history? - // that should of course just become encrypted - if (newpw.isEmpty()) - core->useOtherPassword(Core::ptHistory); - else - core->setPassword(newpw, Core::ptHistory); - return; + // enable change pw button } - else - delete dialog; } } else @@ -116,40 +124,57 @@ void PrivacyForm::onEncryptLogsUpdated() HistoryKeeper::resetInstance(); } - fail: - core->clearPassword(Core::ptHistory); - Settings::getInstance().setEncryptLogs(false); - bodyUI->cbEncryptHistory->setChecked(false); + core->clearPassword(Core::ptHistory); + Settings::getInstance().setEncryptLogs(false); + bodyUI->cbEncryptHistory->setChecked(false); + // disable change pw button } -void PrivacyForm::onEncryptToxUpdated() +bool PrivacyForm::setToxPassword() { - bool encryptionState = bodyUI->cbEncryptTox->isChecked(); + 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 (encryptionState) + if (int r = dialog->exec()) { + QString newpw = dialog->getPassword(); + delete dialog; + + if (r == 2) + core->useOtherPassword(Core::ptMain); + else + core->setPassword(newpw, Core::ptMain); + return true; + } + else + { + delete dialog; + return false; + } +} + +void PrivacyForm::onEncryptToxUpdated() +{ + Core* core = Core::getInstance(); + + if (bodyUI->cbEncryptTox->isChecked()) if (!Core::getInstance()->isPasswordSet(Core::ptMain)) - { - SetPasswordDialog dialog; - if (dialog.exec()) + if (setToxPassword()) { - QString pswd = dialog.getPassword(); - if (pswd.size() == 0) - encryptionState = false; - - Core::getInstance()->setPassword(pswd, Core::ptMain); - } else { - encryptionState = false; - Core::getInstance()->clearPassword(Core::ptMain); + bodyUI->cbEncryptTox->setChecked(true); + Settings::getInstance().setEncryptTox(true); + // enable change pw button + return; } - } - } - - bodyUI->cbEncryptTox->setChecked(encryptionState); - Settings::getInstance().setEncryptTox(encryptionState); - if (!Settings::getInstance().getEncryptTox()) - Core::getInstance()->clearPassword(Core::ptMain); + bodyUI->cbEncryptTox->setChecked(false); + Settings::getInstance().setEncryptTox(false); + // disable change pw button + core->clearPassword(Core::ptMain); } void PrivacyForm::setNospam() diff --git a/src/widget/form/settings/privacyform.h b/src/widget/form/settings/privacyform.h index fc0e4eb0d..e0b7c70fc 100644 --- a/src/widget/form/settings/privacyform.h +++ b/src/widget/form/settings/privacyform.h @@ -39,7 +39,9 @@ private slots: void generateRandomNospam(); void onNospamEdit(); void onEncryptLogsUpdated(); + bool setChatLogsPassword(); void onEncryptToxUpdated(); + bool setToxPassword(); private: Ui::PrivacySettings* bodyUI; diff --git a/src/widget/form/settings/privacysettings.ui b/src/widget/form/settings/privacysettings.ui index 87b76d171..0491dab15 100644 --- a/src/widget/form/settings/privacysettings.ui +++ b/src/widget/form/settings/privacysettings.ui @@ -57,7 +57,7 @@ - Keep History (unstable) + Keep chat logs (mostly stable) @@ -76,7 +76,7 @@ true - Encrypt Tox datafile + Encrypt Tox data file @@ -86,7 +86,7 @@ true - Encrypt History + Encrypt chat logs true From fc30d342bc753635dcbe3b016ac6b8e8a8456ddf Mon Sep 17 00:00:00 2001 From: dubslow Date: Thu, 27 Nov 2014 11:58:33 -0500 Subject: [PATCH 09/49] Added change pw buttons; encryption is very nearly done. @apprb there is one question and one TODO that you should look over in privacyform.cpp, as well as a TODO in historykeeper.cpp Also both the encryption settings box and the pw dialog lineedits should probably be in a QGridLayout... but I don't really want to do that by hand :P --- src/coreencryption.cpp | 3 ++ src/historykeeper.cpp | 2 +- src/widget/form/setpassworddialog.cpp | 5 +- src/widget/form/setpassworddialog.ui | 15 +++++- src/widget/form/settings/privacyform.cpp | 24 ++++++--- src/widget/form/settings/privacysettings.ui | 60 ++++++++++++++------- 6 files changed, 79 insertions(+), 30 deletions(-) diff --git a/src/coreencryption.cpp b/src/coreencryption.cpp index 0ee3906e9..7ad296ab4 100644 --- a/src/coreencryption.cpp +++ b/src/coreencryption.cpp @@ -72,6 +72,9 @@ void Core::clearPassword(PasswordType passtype) delete[] pwsaltedkeys[passtype]; pwsaltedkeys[passtype] = nullptr; } + + if (passtype == ptMain) + saveConfiguration(); } QByteArray Core::encryptData(const QByteArray& data, PasswordType passtype) diff --git a/src/historykeeper.cpp b/src/historykeeper.cpp index 9324a52e8..808d0bfc4 100644 --- a/src/historykeeper.cpp +++ b/src/historykeeper.cpp @@ -137,7 +137,7 @@ HistoryKeeper::~HistoryKeeper() void HistoryKeeper::reencrypt(QString newpw) { - // this needs to appropriately set the core password as well + // TODO: this needs to appropriately set the core password as well // if newpw.isEmpty(), then use the other core password } diff --git a/src/widget/form/setpassworddialog.cpp b/src/widget/form/setpassworddialog.cpp index 2e401b79f..c8d08cfef 100644 --- a/src/widget/form/setpassworddialog.cpp +++ b/src/widget/form/setpassworddialog.cpp @@ -27,7 +27,8 @@ SetPasswordDialog::SetPasswordDialog(QString body, QString extraButton, QWidget* connect(ui->passwordlineEdit, SIGNAL(textChanged(QString)), this, SLOT(onPasswordEdit())); connect(ui->repasswordlineEdit, SIGNAL(textChanged(QString)), this, SLOT(onPasswordEdit())); - ui->body->setText(body); + ui->body->setText(body + tr("\nTo encourage good habits, qTox requires at least 8 characters.")); + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); if (!extraButton.isEmpty()) { @@ -44,7 +45,7 @@ SetPasswordDialog::~SetPasswordDialog() void SetPasswordDialog::onPasswordEdit() { - if ( !ui->passwordlineEdit->text().isEmpty() + if ( ui->passwordlineEdit->text().length() >= 8 && ui->passwordlineEdit->text() == ui->repasswordlineEdit->text()) ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); else diff --git a/src/widget/form/setpassworddialog.ui b/src/widget/form/setpassworddialog.ui index 02d803bb5..3d6229d79 100644 --- a/src/widget/form/setpassworddialog.ui +++ b/src/widget/form/setpassworddialog.ui @@ -20,6 +20,19 @@ + + + + Qt::Vertical + + + + 20 + 12 + + + + @@ -64,7 +77,7 @@ 20 - 40 + 12 diff --git a/src/widget/form/settings/privacyform.cpp b/src/widget/form/settings/privacyform.cpp index 158f03683..3c994013e 100644 --- a/src/widget/form/settings/privacyform.cpp +++ b/src/widget/form/settings/privacyform.cpp @@ -32,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())); @@ -61,8 +66,9 @@ void PrivacyForm::onTypingNotificationEnabledUpdated() bool PrivacyForm::setChatLogsPassword() { + Core* core = Core::getInstance(); SetPasswordDialog* dialog; - QString body = tr("Please set your new chat log password:"); + 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 @@ -110,7 +116,8 @@ void PrivacyForm::onEncryptLogsUpdated() Settings::getInstance().setEncryptLogs(true); bodyUI->cbEncryptHistory->setChecked(true); // not logically necessary, but more consistent (esp. if the logic changes) - // enable change pw button + bodyUI->changeLogsPwButton->setEnabled(true); + return; } } } @@ -127,13 +134,14 @@ void PrivacyForm::onEncryptLogsUpdated() core->clearPassword(Core::ptHistory); Settings::getInstance().setEncryptLogs(false); bodyUI->cbEncryptHistory->setChecked(false); - // disable change pw button + bodyUI->changeLogsPwButton->setEnabled(false); } bool PrivacyForm::setToxPassword() { + Core* core = Core::getInstance(); SetPasswordDialog* dialog; - QString body = tr("Please set your new data file password:"); + 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 @@ -162,18 +170,18 @@ void PrivacyForm::onEncryptToxUpdated() Core* core = Core::getInstance(); if (bodyUI->cbEncryptTox->isChecked()) - if (!Core::getInstance()->isPasswordSet(Core::ptMain)) + if (!core->isPasswordSet(Core::ptMain)) if (setToxPassword()) { bodyUI->cbEncryptTox->setChecked(true); Settings::getInstance().setEncryptTox(true); - // enable change pw button + bodyUI->changeToxPwButton->setEnabled(true); return; } bodyUI->cbEncryptTox->setChecked(false); Settings::getInstance().setEncryptTox(false); - // disable change pw button + bodyUI->changeToxPwButton->setEnabled(false); core->clearPassword(Core::ptMain); } @@ -193,8 +201,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() diff --git a/src/widget/form/settings/privacysettings.ui b/src/widget/form/settings/privacysettings.ui index 0491dab15..452725c2e 100644 --- a/src/widget/form/settings/privacysettings.ui +++ b/src/widget/form/settings/privacysettings.ui @@ -71,27 +71,49 @@ - - - true - - - Encrypt Tox data file - - + + + + + true + + + Encrypt Tox data file + + + + + + + Change password + + + + - - - true - - - Encrypt chat logs - - - true - - + + + + + true + + + Encrypt chat logs + + + true + + + + + + + Change password + + + + From 9a92e6c98091382ee675bbd514667f131d5c14ef Mon Sep 17 00:00:00 2001 From: Dubslow Date: Sun, 30 Nov 2014 11:22:18 -0600 Subject: [PATCH 10/49] Clarification --- src/widget/form/settings/privacysettings.ui | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/widget/form/settings/privacysettings.ui b/src/widget/form/settings/privacysettings.ui index 452725c2e..d2e235918 100644 --- a/src/widget/form/settings/privacysettings.ui +++ b/src/widget/form/settings/privacysettings.ui @@ -67,9 +67,16 @@ true - Encryption + Local file encryption + + + + All internet communication is encrypted, and this cannot be disabled. However, you may optionally password protect your local Tox files. + + + From 7a59e7b853fbab7a09f3731904bac964a2afa064 Mon Sep 17 00:00:00 2001 From: Dubslow Date: Wed, 3 Dec 2014 12:00:37 -0600 Subject: [PATCH 11/49] fix copyright statement, update comment --- src/coreencryption.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/coreencryption.cpp b/src/coreencryption.cpp index 7ad296ab4..02b30b080 100644 --- a/src/coreencryption.cpp +++ b/src/coreencryption.cpp @@ -1,9 +1,9 @@ /* - Copyright (C) 2013 by Maxim Biro + Copyright (C) 2014 by Project Tox - This file is part of Tox Qt GUI. + This file is part of qTox, a Qt-based graphical interface for Tox. - This program is free software: you can redistribute it and/or modify + 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. @@ -15,7 +15,8 @@ */ /* This file is part of the Core class, but was separated for my sanity */ -/* At least temporarily, the save and load functions are here as well */ +/* 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" From 1d24eb14b487588ddc30b333924246a754a6e3e8 Mon Sep 17 00:00:00 2001 From: Dubslow Date: Wed, 3 Dec 2014 18:52:10 -0600 Subject: [PATCH 12/49] Fix not setting tox null after cleanup also reordered functions for super extra safety --- src/core.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/core.cpp b/src/core.cpp index f162f59b6..fee037b68 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -90,9 +90,15 @@ Core::Core(Camera* cam, QThread *CoreThread, QString loadPath) : Core::~Core() { - if (tox) { + clearPassword(Core::ptMain); + clearPassword(Core::ptHistory); + + if (tox) + { toxav_kill(toxav); + toxav = nullptr; tox_kill(tox); + tox = nullptr; } if (videobuf) @@ -103,9 +109,6 @@ Core::~Core() Audio::closeInput(); Audio::closeOutput(); - - clearPassword(Core::ptMain); - clearPassword(Core::ptHistory); } Core* Core::getInstance() From 000eb8977d0a09d6aa04a29f30827dc2912b0006 Mon Sep 17 00:00:00 2001 From: apprb Date: Thu, 4 Dec 2014 21:22:17 +0600 Subject: [PATCH 13/49] history: encrypted <-> plain convertion --- src/historykeeper.cpp | 92 ++++++++++++++++++++---- src/historykeeper.h | 17 +++-- src/misc/db/encrypteddb.cpp | 4 +- src/widget/form/settings/privacyform.cpp | 25 ++++++- 4 files changed, 116 insertions(+), 22 deletions(-) diff --git a/src/historykeeper.cpp b/src/historykeeper.cpp index 808d0bfc4..78a2a05c0 100644 --- a/src/historykeeper.cpp +++ b/src/historykeeper.cpp @@ -68,11 +68,14 @@ HistoryKeeper *HistoryKeeper::getInstance() return historyInstance; } -bool HistoryKeeper::checkPassword() +bool HistoryKeeper::checkPassword(int encrypted) { - if (Settings::getInstance().getEnableLogging()) - if (Settings::getInstance().getEncryptLogs()) - return EncryptedDb::check(getHistoryPath()); + 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; } @@ -127,7 +130,7 @@ HistoryKeeper::HistoryKeeper(GenericDdInterface *db_) : messageID = 0; QSqlQuery sqlAnswer = db->exec("select seq from sqlite_sequence where name=\"history\";"); if (sqlAnswer.first()) - messageID = sqlAnswer.value(0).toInt(); + messageID = sqlAnswer.value(0).toLongLong(); } HistoryKeeper::~HistoryKeeper() @@ -141,16 +144,13 @@ void HistoryKeeper::reencrypt(QString newpw) // if newpw.isEmpty(), then use the other core password } -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 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++; @@ -189,12 +189,66 @@ QList 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::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 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 &lst) +{ + db->exec("BEGIN TRANSACTION;"); + for (const HistMessage &msg : lst) + { + QList 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 HistoryKeeper::generateAddChatEntryCmd(const QString& chat, const QString& message, const QString& sender, const QDateTime &dt, bool isSent) +{ + QList 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); @@ -271,7 +325,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) @@ -349,3 +403,13 @@ bool HistoryKeeper::isFileExist() return file.exists(); } + +bool HistoryKeeper::removeHistory(int encrypted) +{ + Q_UNUSED(encrypted); + resetInstance(); + + QString path = getHistoryPath(); + QFile DbFile(path); + return DbFile.remove(); +} diff --git a/src/historykeeper.h b/src/historykeeper.h index 9c67957b6..96b92212b 100644 --- a/src/historykeeper.h +++ b/src/historykeeper.h @@ -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,16 +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); - 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 getChatHistory(ChatType ct, const QString &chat, const QDateTime &time_from, const QDateTime &time_to); void markAsSent(int m_id); void reencrypt(QString newpw); + QList exportMessages(); + void importMessages(const QList &lst); + void setSyncType(Db::syncType sType); private: @@ -67,13 +75,14 @@ private: int getAliasID(const QString &id_str); QString wrapMessage(const QString &str); QString unWrapMessage(const QString &str); + QList generateAddChatEntryCmd(const QString& chat, const QString& message, const QString& sender, const QDateTime &dt, bool isSent); ChatType convertToChatType(int); GenericDdInterface *db; QMap aliases; QMap> chats; - int messageID; + qint64 messageID; }; #endif // HISTORYKEEPER_H diff --git a/src/misc/db/encrypteddb.cpp b/src/misc/db/encrypteddb.cpp index b2e5e0328..d2315e594 100644 --- a/src/misc/db/encrypteddb.cpp +++ b/src/misc/db/encrypteddb.cpp @@ -33,12 +33,11 @@ EncryptedDb::EncryptedDb(const QString &fname, QList initList) : QByteArray fileContent; if (pullFileContent(fileName, buffer)) { - chunkPosition = encrFile.size() / encryptedChunkSize; - qDebug() << "writing old data"; encrFile.setFileName(fileName); encrFile.open(QIODevice::ReadOnly); fileContent = encrFile.readAll(); + chunkPosition = encrFile.size() / encryptedChunkSize; encrFile.close(); } else { qWarning() << "corrupted history log file will be wiped!"; @@ -131,6 +130,7 @@ void EncryptedDb::appendToEncrypted(const QString &sql) if (encr.size() > 0) { encrFile.write(encr); + encrFile.flush(); } buffer = buffer.right(buffer.size() - plainChunkSize); diff --git a/src/widget/form/settings/privacyform.cpp b/src/widget/form/settings/privacyform.cpp index 3c994013e..1eb0f373f 100644 --- a/src/widget/form/settings/privacyform.cpp +++ b/src/widget/form/settings/privacyform.cpp @@ -25,6 +25,7 @@ #include "src/widget/form/checkcontinue.h" #include #include +#include PrivacyForm::PrivacyForm() : GenericForm(tr("Privacy"), QPixmap(":/img/settings/privacy.png")) @@ -79,7 +80,7 @@ bool PrivacyForm::setChatLogsPassword() QString newpw = dialog->getPassword(); delete dialog; - if (!HistoryKeeper::checkPassword()) + if (!HistoryKeeper::checkPassword(true)) if (checkContinue(tr("Old encrypted chat logs", "title"), tr("Would you like to re-encrypt your old chat logs?\nOtherwise they will be deleted.", "body"))) { @@ -106,6 +107,7 @@ bool PrivacyForm::setChatLogsPassword() void PrivacyForm::onEncryptLogsUpdated() { Core* core = Core::getInstance(); + QList oldMessages; if (bodyUI->cbEncryptHistory->isChecked()) { @@ -113,10 +115,19 @@ void PrivacyForm::onEncryptLogsUpdated() { if (setChatLogsPassword()) { + oldMessages = HistoryKeeper::getInstance()->exportMessages(); + qDebug() << "Loaded messages:" << oldMessages.size(); + bool delRet = HistoryKeeper::removeHistory(); + if (!delRet) + qWarning() << "HistoryKeeper::removeHistory() returned FALSE"; + HistoryKeeper::resetInstance(); // HistoryKeeper::removeHistory() invokes HistoryKeeper::removeHistory() but logic may be changed + Settings::getInstance().setEncryptLogs(true); bodyUI->cbEncryptHistory->setChecked(true); // not logically necessary, but more consistent (esp. if the logic changes) bodyUI->changeLogsPwButton->setEnabled(true); + + HistoryKeeper::getInstance()->importMessages(oldMessages); return; } } @@ -125,10 +136,20 @@ void PrivacyForm::onEncryptLogsUpdated() { if (checkContinue(tr("Old encrypted chat logs", "title"), tr("Would you like to un-encrypt your chat logs?\nOtherwise they will be deleted."))) { - // TODO: how to unencrypt current encrypted logs + oldMessages = HistoryKeeper::getInstance()->exportMessages(); + qDebug() << "Loaded messages:" << oldMessages.size(); + bool delRet = HistoryKeeper::removeHistory(); + if (!delRet) + qWarning() << "HistoryKeeper::removeHistory() returned FALSE"; + HistoryKeeper::resetInstance(); // HistoryKeeper::removeHistory() invokes HistoryKeeper::removeHistory() but logic may be changed + + Settings::getInstance().setEncryptLogs(false); + HistoryKeeper::getInstance()->importMessages(oldMessages); } else + { HistoryKeeper::resetInstance(); + } } core->clearPassword(Core::ptHistory); From bac485400e8ff100e495576d136ac4b7c269c069 Mon Sep 17 00:00:00 2001 From: apprb Date: Thu, 4 Dec 2014 22:07:16 +0600 Subject: [PATCH 14/49] small code unification --- src/widget/form/settings/privacyform.cpp | 25 ++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/widget/form/settings/privacyform.cpp b/src/widget/form/settings/privacyform.cpp index 1eb0f373f..d5f34bb29 100644 --- a/src/widget/form/settings/privacyform.cpp +++ b/src/widget/form/settings/privacyform.cpp @@ -106,6 +106,17 @@ bool PrivacyForm::setChatLogsPassword() void PrivacyForm::onEncryptLogsUpdated() { + auto getOldMessages = []() + { + auto msgs = HistoryKeeper::getInstance()->exportMessages(); + qDebug() << "Loaded messages:" << msgs.size(); + bool delRet = HistoryKeeper::removeHistory(); + if (!delRet) + qWarning() << "HistoryKeeper::removeHistory() returned FALSE"; + HistoryKeeper::resetInstance(); // HistoryKeeper::removeHistory() invokes HistoryKeeper::removeHistory() but logic may be changed + return msgs; + }; + Core* core = Core::getInstance(); QList oldMessages; @@ -115,12 +126,7 @@ void PrivacyForm::onEncryptLogsUpdated() { if (setChatLogsPassword()) { - oldMessages = HistoryKeeper::getInstance()->exportMessages(); - qDebug() << "Loaded messages:" << oldMessages.size(); - bool delRet = HistoryKeeper::removeHistory(); - if (!delRet) - qWarning() << "HistoryKeeper::removeHistory() returned FALSE"; - HistoryKeeper::resetInstance(); // HistoryKeeper::removeHistory() invokes HistoryKeeper::removeHistory() but logic may be changed + oldMessages = getOldMessages(); Settings::getInstance().setEncryptLogs(true); bodyUI->cbEncryptHistory->setChecked(true); @@ -136,12 +142,7 @@ void PrivacyForm::onEncryptLogsUpdated() { if (checkContinue(tr("Old encrypted chat logs", "title"), tr("Would you like to un-encrypt your chat logs?\nOtherwise they will be deleted."))) { - oldMessages = HistoryKeeper::getInstance()->exportMessages(); - qDebug() << "Loaded messages:" << oldMessages.size(); - bool delRet = HistoryKeeper::removeHistory(); - if (!delRet) - qWarning() << "HistoryKeeper::removeHistory() returned FALSE"; - HistoryKeeper::resetInstance(); // HistoryKeeper::removeHistory() invokes HistoryKeeper::removeHistory() but logic may be changed + oldMessages = getOldMessages(); Settings::getInstance().setEncryptLogs(false); HistoryKeeper::getInstance()->importMessages(oldMessages); From dccb0a99515b2f5d4c0b0c4d1a0c53be3b35887c Mon Sep 17 00:00:00 2001 From: Dubslow Date: Thu, 4 Dec 2014 10:55:14 -0600 Subject: [PATCH 15/49] finishing touches --- src/historykeeper.cpp | 18 +++++----- src/historykeeper.h | 2 +- src/widget/form/settings/privacyform.cpp | 42 ++++++++---------------- 3 files changed, 24 insertions(+), 38 deletions(-) diff --git a/src/historykeeper.cpp b/src/historykeeper.cpp index 78a2a05c0..d72f6a09d 100644 --- a/src/historykeeper.cpp +++ b/src/historykeeper.cpp @@ -138,12 +138,6 @@ HistoryKeeper::~HistoryKeeper() delete db; } -void HistoryKeeper::reencrypt(QString newpw) -{ - // TODO: this needs to appropriately set the core password as well - // if newpw.isEmpty(), then use the other core password -} - qint64 HistoryKeeper::addChatEntry(const QString& chat, const QString& message, const QString& sender, const QDateTime &dt, bool isSent) { QList cmds = generateAddChatEntryCmd(chat, message, sender, dt, isSent); @@ -406,10 +400,18 @@ bool HistoryKeeper::isFileExist() bool HistoryKeeper::removeHistory(int encrypted) { - Q_UNUSED(encrypted); resetInstance(); - QString path = getHistoryPath(); + QString path = getHistoryPath(QString(), encrypted); QFile DbFile(path); return DbFile.remove(); } + +QList 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; +} diff --git a/src/historykeeper.h b/src/historykeeper.h index 96b92212b..ad80b536f 100644 --- a/src/historykeeper.h +++ b/src/historykeeper.h @@ -52,12 +52,12 @@ public: static bool isFileExist(); static void renameHistory(QString from, QString to); static bool removeHistory(int encrypted = -1); + static QList exportMessagesDeleteFile(int encrypted = -1); 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 getChatHistory(ChatType ct, const QString &chat, const QDateTime &time_from, const QDateTime &time_to); void markAsSent(int m_id); - void reencrypt(QString newpw); QList exportMessages(); void importMessages(const QList &lst); diff --git a/src/widget/form/settings/privacyform.cpp b/src/widget/form/settings/privacyform.cpp index d5f34bb29..6ee6624bb 100644 --- a/src/widget/form/settings/privacyform.cpp +++ b/src/widget/form/settings/privacyform.cpp @@ -77,24 +77,23 @@ bool PrivacyForm::setChatLogsPassword() if (int r = dialog->exec()) { + QList oldMessages = HistoryKeeper::exportMessagesDeleteFile(); + QString newpw = dialog->getPassword(); delete dialog; - if (!HistoryKeeper::checkPassword(true)) - if (checkContinue(tr("Old encrypted chat logs", "title"), - tr("Would you like to re-encrypt your old chat logs?\nOtherwise they will be deleted.", "body"))) - { - HistoryKeeper::getInstance()->reencrypt(r == 2 ? QString() : newpw); - // will set core and reset itself - return true; - } - // @apprb you resetInstance() in the old code but wouldn't that wipe out the current unencrypted history? - // current history should of course just become encrypted if (r == 2) core->useOtherPassword(Core::ptHistory); else core->setPassword(newpw, Core::ptHistory); - // HistoryKeeper::encryptPlain(); + + //if (!HistoryKeeper::checkPassword(true)) + // if (checkContinue(tr("Old encrypted chat logs", "title"), + // tr("Would you like to re-encrypt your old chat logs?\nOtherwise they will be deleted.", "body"))) + // for now, don't bother asking. Why wouldn't you want to reencrypt? + + HistoryKeeper::getInstance()->importMessages(oldMessages); + return true; } else @@ -106,19 +105,7 @@ bool PrivacyForm::setChatLogsPassword() void PrivacyForm::onEncryptLogsUpdated() { - auto getOldMessages = []() - { - auto msgs = HistoryKeeper::getInstance()->exportMessages(); - qDebug() << "Loaded messages:" << msgs.size(); - bool delRet = HistoryKeeper::removeHistory(); - if (!delRet) - qWarning() << "HistoryKeeper::removeHistory() returned FALSE"; - HistoryKeeper::resetInstance(); // HistoryKeeper::removeHistory() invokes HistoryKeeper::removeHistory() but logic may be changed - return msgs; - }; - Core* core = Core::getInstance(); - QList oldMessages; if (bodyUI->cbEncryptHistory->isChecked()) { @@ -126,14 +113,11 @@ void PrivacyForm::onEncryptLogsUpdated() { if (setChatLogsPassword()) { - oldMessages = getOldMessages(); - Settings::getInstance().setEncryptLogs(true); bodyUI->cbEncryptHistory->setChecked(true); // not logically necessary, but more consistent (esp. if the logic changes) bodyUI->changeLogsPwButton->setEnabled(true); - HistoryKeeper::getInstance()->importMessages(oldMessages); return; } } @@ -142,14 +126,14 @@ void PrivacyForm::onEncryptLogsUpdated() { if (checkContinue(tr("Old encrypted chat logs", "title"), tr("Would you like to un-encrypt your chat logs?\nOtherwise they will be deleted."))) { - oldMessages = getOldMessages(); - + QList oldMessages = HistoryKeeper::exportMessagesDeleteFile(true); + core->clearPassword(Core::ptHistory); Settings::getInstance().setEncryptLogs(false); HistoryKeeper::getInstance()->importMessages(oldMessages); } else { - HistoryKeeper::resetInstance(); + HistoryKeeper::removeHistory(true); } } From 4c85f92e7d20c62ec11232911a28ba79a89d2b8c Mon Sep 17 00:00:00 2001 From: apprb Date: Thu, 4 Dec 2014 23:44:45 +0600 Subject: [PATCH 16/49] plain db encryption --- src/widget/form/settings/privacyform.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/widget/form/settings/privacyform.cpp b/src/widget/form/settings/privacyform.cpp index 6ee6624bb..25d384496 100644 --- a/src/widget/form/settings/privacyform.cpp +++ b/src/widget/form/settings/privacyform.cpp @@ -92,6 +92,7 @@ bool PrivacyForm::setChatLogsPassword() // tr("Would you like to re-encrypt your old chat logs?\nOtherwise they will be deleted.", "body"))) // for now, don't bother asking. Why wouldn't you want to reencrypt? + Settings::getInstance().setEncryptLogs(true); HistoryKeeper::getInstance()->importMessages(oldMessages); return true; From 2ca3269761cd0ee9c1ee1b87741d3a81d72183d5 Mon Sep 17 00:00:00 2001 From: Dubslow Date: Thu, 4 Dec 2014 12:50:52 -0600 Subject: [PATCH 17/49] just remove clearing password in dtor which is only called on exit solves the bug found by apprb --- src/core.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/core.cpp b/src/core.cpp index fee037b68..aaba1320a 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -90,9 +90,6 @@ Core::Core(Camera* cam, QThread *CoreThread, QString loadPath) : Core::~Core() { - clearPassword(Core::ptMain); - clearPassword(Core::ptHistory); - if (tox) { toxav_kill(toxav); From 7f12c267e1f70b02428249e6fe4a527220c7dc98 Mon Sep 17 00:00:00 2001 From: Dubslow Date: Thu, 4 Dec 2014 13:31:43 -0600 Subject: [PATCH 18/49] turns out the password code saving Configuration was useless anyways would have avoided the bug I just "fixed" --- src/coreencryption.cpp | 6 ------ src/widget/form/settings/privacyform.cpp | 7 +++---- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/coreencryption.cpp b/src/coreencryption.cpp index 02b30b080..e8947f3ec 100644 --- a/src/coreencryption.cpp +++ b/src/coreencryption.cpp @@ -49,9 +49,6 @@ void Core::setPassword(QString& password, PasswordType passtype, uint8_t* salt) tox_derive_key_from_pass(str.data(), str.size(), pwsaltedkeys[passtype]); password.clear(); - - if (passtype == ptMain) - saveConfiguration(); } void Core::useOtherPassword(PasswordType type) @@ -73,9 +70,6 @@ void Core::clearPassword(PasswordType passtype) delete[] pwsaltedkeys[passtype]; pwsaltedkeys[passtype] = nullptr; } - - if (passtype == ptMain) - saveConfiguration(); } QByteArray Core::encryptData(const QByteArray& data, PasswordType passtype) diff --git a/src/widget/form/settings/privacyform.cpp b/src/widget/form/settings/privacyform.cpp index 25d384496..eb838dcff 100644 --- a/src/widget/form/settings/privacyform.cpp +++ b/src/widget/form/settings/privacyform.cpp @@ -114,11 +114,8 @@ void PrivacyForm::onEncryptLogsUpdated() { if (setChatLogsPassword()) { - Settings::getInstance().setEncryptLogs(true); bodyUI->cbEncryptHistory->setChecked(true); - // not logically necessary, but more consistent (esp. if the logic changes) bodyUI->changeLogsPwButton->setEnabled(true); - return; } } @@ -163,6 +160,9 @@ bool PrivacyForm::setToxPassword() core->useOtherPassword(Core::ptMain); else core->setPassword(newpw, Core::ptMain); + + Settings::getInstance().setEncryptTox(true); + core->saveConfiguration(); return true; } else @@ -181,7 +181,6 @@ void PrivacyForm::onEncryptToxUpdated() if (setToxPassword()) { bodyUI->cbEncryptTox->setChecked(true); - Settings::getInstance().setEncryptTox(true); bodyUI->changeToxPwButton->setEnabled(true); return; } From 21327fdaba3614218684651d9a97434992851d0f Mon Sep 17 00:00:00 2001 From: Dubslow Date: Thu, 4 Dec 2014 14:21:48 -0600 Subject: [PATCH 19/49] warn only once on unecrypted file with encryption set --- src/coreencryption.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/coreencryption.cpp b/src/coreencryption.cpp index e8947f3ec..b9de04ca6 100644 --- a/src/coreencryption.cpp +++ b/src/coreencryption.cpp @@ -275,7 +275,8 @@ void Core::saveConfiguration(const QString& path) if (!pwsaltedkeys[ptMain]) { // probably zero chance event - Widget::getInstance()->showWarningMsgBox(tr("NO Password"), tr("Will be saved without encryption!")); + 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 From 4e1a204bc07b9df18a8c00faf5f00160e78592c9 Mon Sep 17 00:00:00 2001 From: Dubslow Date: Fri, 5 Dec 2014 18:18:43 -0600 Subject: [PATCH 20/49] Fix threading issue with startup pw popup The GUI is slow to update after accepting a password, but a cursory ten minute investigation didn't yield why I inserted a processEvents() call before ready = true; at the end of Core::start, but that didn't help. Define the total time between the password dialog disappearing and the UI updating with your own status is T: Then my debug statement indicated that this processEvents call happened around 1/3T, raising two questions: 1) What the hell is happening between 0 and 1/3 T? Decryption doesn't take that long... Note that bad passwords are immediately rejected with a new dialog, so I highly doubt it's the decryption cpu time 2) The remaining 2/3rds: processEvents has been called after the avatar and username signals, yet they don't update in the UI till time T when everything updates after bootstrapping... Oh well, like I said, only a cursory investigation --- src/coreencryption.cpp | 45 ++++++++++-------------------------------- src/widget/widget.cpp | 28 ++++++++++++++++++++++++++ src/widget/widget.h | 2 ++ 3 files changed, 40 insertions(+), 35 deletions(-) diff --git a/src/coreencryption.cpp b/src/coreencryption.cpp index b9de04ca6..07d0a80c5 100644 --- a/src/coreencryption.cpp +++ b/src/coreencryption.cpp @@ -133,7 +133,7 @@ bool Core::loadEncryptedSave(QByteArray& data) 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 this profile:", "used in load() when no pw is already set")); + QString a(tr("Please enter the password for this profile.", "used in load() when no pw is already set")); QString b(tr("The previous password is incorrect; please try again:", "used on retries in load()")); QString dialogtxt; @@ -152,30 +152,18 @@ bool Core::loadEncryptedSave(QByteArray& data) uint8_t salt[tox_pass_salt_length()]; tox_get_salt(reinterpret_cast(data.data()), salt); - QInputDialog dialog; - dialog.moveToThread(qApp->thread()); - dialog.setOkButtonText(tr("Set password")); - dialog.setCancelButtonText(tr("Change profile")); - dialog.setWindowTitle(tr("Enter your password")); - dialog.setInputMode(QInputDialog::TextInput); - dialog.setTextEchoMode(QLineEdit::Password); + do { - dialog.setTextValue(QString()); - dialog.setLabelText(dialogtxt); - - int val = dialog.exec(); + QString pw = Widget::getInstance()->passwordDialog(tr("Change profile"), dialogtxt); - if (val == QDialog::Accepted) - { - QString pw = dialog.textValue(); - setPassword(pw, ptMain, salt); - } - else + if (pw.isEmpty()) { clearPassword(ptMain); return false; } + else + setPassword(pw, ptMain, salt); error = tox_encrypted_key_load(tox, reinterpret_cast(data.data()), data.size(), pwsaltedkeys[ptMain]); dialogtxt = a + " " + b; @@ -210,33 +198,20 @@ void Core::checkEncryptedHistory() else dialogtxt = a; - QInputDialog dialog; - dialog.moveToThread(qApp->thread()); - dialog.setOkButtonText(tr("Set password")); - dialog.setCancelButtonText(tr("Disable history")); - dialog.setWindowTitle(tr("Enter your password")); - dialog.setInputMode(QInputDialog::TextInput); - dialog.setTextEchoMode(QLineEdit::Password); bool error = true; do { - dialog.setLabelText(dialogtxt); - dialog.setTextValue(QString()); + QString pw = Widget::getInstance()->passwordDialog(tr("Disable history"), dialogtxt); - int val = dialog.exec(); - - if (val == QDialog::Accepted) - { - QString pw = dialog.textValue(); - setPassword(pw, ptHistory, reinterpret_cast(salt.data())); - } - else + if (pw.isEmpty()) { clearPassword(ptHistory); Settings::getInstance().setEncryptLogs(false); Settings::getInstance().setEnableLogging(false); return; } + else + setPassword(pw, ptHistory, reinterpret_cast(salt.data())); error = !HistoryKeeper::checkPassword(); dialogtxt = a + " " + b; diff --git a/src/widget/widget.cpp b/src/widget/widget.cpp index b89ef096f..77f20f6a2 100644 --- a/src/widget/widget.cpp +++ b/src/widget/widget.cpp @@ -1236,6 +1236,34 @@ bool Widget::askMsgboxQuestion(const QString& title, const QString& msg) } } +QString Widget::passwordDialog(const QString& cancel, const QString& body) +{ + // We can only display widgets from the GUI thread + if (QThread::currentThread() != qApp->thread()) + { + QString ret; + QMetaObject::invokeMethod(this, "passwordDialog", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(QString, ret), + Q_ARG(const QString&, cancel), Q_ARG(const QString&, body)); + return ret; + } + else + { + QString ret; + QInputDialog dialog; + dialog.setWindowTitle(tr("Enter your password")); + dialog.setOkButtonText(tr("Set password")); + dialog.setCancelButtonText(cancel); + dialog.setInputMode(QInputDialog::TextInput); + dialog.setTextEchoMode(QLineEdit::Password); + dialog.setLabelText(body); + int val = dialog.exec(); + if (val == QDialog::Accepted) + ret = dialog.textValue(); + return ret; + } +} + void Widget::clearAllReceipts() { QList frnds = FriendList::getAllFriends(); diff --git a/src/widget/widget.h b/src/widget/widget.h index 366c5eb60..272ff2f79 100644 --- a/src/widget/widget.h +++ b/src/widget/widget.h @@ -68,6 +68,8 @@ public: QMessageBox::StandardButtons buttonss = QMessageBox::Ok); Q_INVOKABLE void setEnabledThreadsafe(bool enabled); Q_INVOKABLE bool askMsgboxQuestion(const QString& title, const QString& msg); + Q_INVOKABLE QString passwordDialog(const QString& cancel, const QString& body); + // hooray for threading hacks ~Widget(); virtual void closeEvent(QCloseEvent *event); From 2e6b0f7b2b8516a5426331f1ca5b65b734555c8a Mon Sep 17 00:00:00 2001 From: Dubslow Date: Fri, 5 Dec 2014 19:17:02 -0600 Subject: [PATCH 21/49] remove files that I shouldn't have added in the first place --- qtox.pro | 6 ++---- src/autoupdate.cpp | 4 ++-- src/widget/form/checkcontinue.cpp | 23 ----------------------- src/widget/form/checkcontinue.h | 22 ---------------------- src/widget/form/settings/identityform.cpp | 15 ++++++--------- src/widget/form/settings/privacyform.cpp | 3 +-- src/widget/toxsave.cpp | 3 +-- src/widget/widget.cpp | 19 +++++++++---------- src/widget/widget.h | 5 ++--- 9 files changed, 23 insertions(+), 77 deletions(-) delete mode 100644 src/widget/form/checkcontinue.cpp delete mode 100644 src/widget/form/checkcontinue.h diff --git a/qtox.pro b/qtox.pro index 7211d468d..6cf51185e 100644 --- a/qtox.pro +++ b/qtox.pro @@ -163,8 +163,7 @@ HEADERS += src/widget/form/addfriendform.h \ src/autoupdate.h \ src/misc/serialize.h \ src/widget/form/settings/advancedform.h \ - src/audio.h \ - src/widget/form/checkcontinue.h + src/audio.h SOURCES += \ src/widget/form/addfriendform.cpp \ @@ -231,8 +230,7 @@ SOURCES += \ src/autoupdate.cpp \ src/misc/serialize.cpp \ src/widget/form/settings/advancedform.cpp \ - src/audio.cpp \ - src/widget/form/checkcontinue.cpp + src/audio.cpp contains(DEFINES, QTOX_PLATFORM_EXT) { HEADERS += src/platform/timer.h diff --git a/src/autoupdate.cpp b/src/autoupdate.cpp index 8c2a2ef02..a36e4c63d 100644 --- a/src/autoupdate.cpp +++ b/src/autoupdate.cpp @@ -427,8 +427,8 @@ void AutoUpdater::checkUpdatesAsyncInteractiveWorker() if (!isUpdateAvailable()) return; - if (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."))) + if (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."), false)) { downloadUpdate(); } diff --git a/src/widget/form/checkcontinue.cpp b/src/widget/form/checkcontinue.cpp deleted file mode 100644 index d42afb327..000000000 --- a/src/widget/form/checkcontinue.cpp +++ /dev/null @@ -1,23 +0,0 @@ -/* - Copyright (C) 2014 by Project Tox - - 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 - -bool checkContinue(const QString& title, const QString& msg, QWidget* parent = nullptr) -{ - QMessageBox::StandardButton resp = QMessageBox::question(parent, title, msg, QMessageBox::Yes | QMessageBox::No, QMessageBox::No); - return resp == QMessageBox::Yes; -} diff --git a/src/widget/form/checkcontinue.h b/src/widget/form/checkcontinue.h deleted file mode 100644 index 46c182bfa..000000000 --- a/src/widget/form/checkcontinue.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - Copyright (C) 2014 by Project Tox - - 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 CHECKCONTINUE_H -#define CHECKCONTINUE_H - -bool checkContinue(const QString& title, const QString& msg, QWidget* parent = nullptr); - -#endif diff --git a/src/widget/form/settings/identityform.cpp b/src/widget/form/settings/identityform.cpp index 3690f6ab6..852798377 100644 --- a/src/widget/form/settings/identityform.cpp +++ b/src/widget/form/settings/identityform.cpp @@ -20,7 +20,6 @@ #include "src/widget/form/settingswidget.h" #include "src/misc/settings.h" #include "src/widget/croppinglabel.h" -#include "src/widget/form/checkcontinue.h" #include "src/widget/widget.h" #include "src/historykeeper.h" #include "src/misc/style.h" @@ -148,8 +147,8 @@ 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"), - tr("A profile named \"%1\" already exists. Do you want to erase it?", "rename confirm text").arg(cur)), this) + 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); bodyUI->profiles->setItemText(bodyUI->profiles->currentIndex(), name); @@ -172,8 +171,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)), this) - // return; success = QFile::remove(path); if (!success) { @@ -195,8 +192,8 @@ void IdentityForm::onDeleteClicked() } else { - if (checkContinue(tr("Deletion imminent!","deletion confirmation title"), - tr("Are you sure you want to delete this profile?","deletion confirmation text"), this)) + 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()); @@ -234,8 +231,8 @@ 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"), - tr("A profile named \"%1\" already exists. Do you want to erase it?", "import confirm text").arg(profile), this)) + 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; QFile::copy(path, profilePath); diff --git a/src/widget/form/settings/privacyform.cpp b/src/widget/form/settings/privacyform.cpp index eb838dcff..64af76b2e 100644 --- a/src/widget/form/settings/privacyform.cpp +++ b/src/widget/form/settings/privacyform.cpp @@ -22,7 +22,6 @@ #include "src/core.h" #include "src/widget/widget.h" #include "src/widget/form/setpassworddialog.h" -#include "src/widget/form/checkcontinue.h" #include #include #include @@ -122,7 +121,7 @@ void PrivacyForm::onEncryptLogsUpdated() } else { - if (checkContinue(tr("Old encrypted chat logs", "title"), tr("Would you like to un-encrypt your chat logs?\nOtherwise they will be deleted."))) + if (Widget::getInstance()->askQuestion(tr("Old encrypted chat logs", "title"), tr("Would you like to un-encrypt your chat logs?\nOtherwise they will be deleted."), false)) { QList oldMessages = HistoryKeeper::exportMessagesDeleteFile(true); core->clearPassword(Core::ptHistory); diff --git a/src/widget/toxsave.cpp b/src/widget/toxsave.cpp index f9ef24a9e..9d14879db 100644 --- a/src/widget/toxsave.cpp +++ b/src/widget/toxsave.cpp @@ -18,7 +18,6 @@ #include "widget.h" #include "src/core.h" #include "src/misc/settings.h" -#include "src/widget/form/checkcontinue.h" #include #include #include @@ -62,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; diff --git a/src/widget/widget.cpp b/src/widget/widget.cpp index 77f20f6a2..3fa740cc8 100644 --- a/src/widget/widget.cpp +++ b/src/widget/widget.cpp @@ -1186,21 +1186,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); } } @@ -1219,7 +1215,7 @@ void Widget::setEnabledThreadsafe(bool enabled) } } -bool Widget::askMsgboxQuestion(const QString& title, const QString& msg) +bool Widget::askQuestion(const QString& title, const QString& msg, bool warning) { // We can only display widgets from the GUI thread if (QThread::currentThread() != qApp->thread()) @@ -1227,12 +1223,15 @@ bool Widget::askMsgboxQuestion(const QString& title, const QString& msg) bool ret; QMetaObject::invokeMethod(this, "askMsgboxQuestion", 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, warning)); return ret; } else { - return QMessageBox::question(this, title, msg) == QMessageBox::StandardButton::Yes; + if (warning) + return QMessageBox::warning(this, title, msg, QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::StandardButton::Ok; + else + return QMessageBox::question(this, title, msg) == QMessageBox::StandardButton::Yes; } } diff --git a/src/widget/widget.h b/src/widget/widget.h index 272ff2f79..f061f1b23 100644 --- a/src/widget/widget.h +++ b/src/widget/widget.h @@ -64,10 +64,9 @@ 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 warning = true); Q_INVOKABLE QString passwordDialog(const QString& cancel, const QString& body); // hooray for threading hacks ~Widget(); From 67027814e58d34fd3e3062cb2c2ccc6914a6ccb0 Mon Sep 17 00:00:00 2001 From: Dubslow Date: Fri, 5 Dec 2014 19:26:34 -0600 Subject: [PATCH 22/49] tweak to popup questions --- src/autoupdate.cpp | 2 +- src/coreencryption.cpp | 1 - src/widget/form/settings/privacyform.cpp | 2 +- src/widget/widget.cpp | 19 +++++++++++++++---- src/widget/widget.h | 2 +- 5 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/autoupdate.cpp b/src/autoupdate.cpp index a36e4c63d..faea4a0cb 100644 --- a/src/autoupdate.cpp +++ b/src/autoupdate.cpp @@ -428,7 +428,7 @@ void AutoUpdater::checkUpdatesAsyncInteractiveWorker() return; if (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."), false)) + QObject::tr("An update is available, do you want to download it now?\nIt will be installed when qTox restarts."), true, false)) { downloadUpdate(); } diff --git a/src/coreencryption.cpp b/src/coreencryption.cpp index 07d0a80c5..f4efbfb45 100644 --- a/src/coreencryption.cpp +++ b/src/coreencryption.cpp @@ -30,7 +30,6 @@ #include #include #include -#include void Core::setPassword(QString& password, PasswordType passtype, uint8_t* salt) { diff --git a/src/widget/form/settings/privacyform.cpp b/src/widget/form/settings/privacyform.cpp index 64af76b2e..6efca0ba7 100644 --- a/src/widget/form/settings/privacyform.cpp +++ b/src/widget/form/settings/privacyform.cpp @@ -121,7 +121,7 @@ void PrivacyForm::onEncryptLogsUpdated() } else { - if (Widget::getInstance()->askQuestion(tr("Old encrypted chat logs", "title"), tr("Would you like to un-encrypt your chat logs?\nOtherwise they will be deleted."), false)) + if (Widget::getInstance()->askQuestion(tr("Old encrypted chat logs", "title"), tr("Would you like to un-encrypt your chat logs?\nOtherwise they will be deleted."), true, false)) { QList oldMessages = HistoryKeeper::exportMessagesDeleteFile(true); core->clearPassword(Core::ptHistory); diff --git a/src/widget/widget.cpp b/src/widget/widget.cpp index 3fa740cc8..4fe4ecbe1 100644 --- a/src/widget/widget.cpp +++ b/src/widget/widget.cpp @@ -1215,7 +1215,7 @@ void Widget::setEnabledThreadsafe(bool enabled) } } -bool Widget::askQuestion(const QString& title, const QString& msg, bool warning) +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()) @@ -1223,15 +1223,26 @@ bool Widget::askQuestion(const QString& title, const QString& msg, bool warning) bool ret; QMetaObject::invokeMethod(this, "askMsgboxQuestion", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, ret), - Q_ARG(const QString&, title), Q_ARG(const QString&, msg), Q_ARG(bool, warning)); + Q_ARG(const QString&, title), Q_ARG(const QString&, msg), + Q_ARG(bool, defaultAns), Q_ARG(bool, warning)); return ret; } else { if (warning) - return QMessageBox::warning(this, title, msg, QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::StandardButton::Ok; + { + QMessageBox::StandardButton def = QMessageBox::Cancel; + if (defaultAns) + def = QMessageBox::Ok; + return QMessageBox::warning(this, title, msg, QMessageBox::Ok | QMessageBox::Cancel, def) == QMessageBox::Ok; + } else - return QMessageBox::question(this, title, msg) == QMessageBox::StandardButton::Yes; + { + QMessageBox::StandardButton def = QMessageBox::No; + if (defaultAns) + def = QMessageBox::Yes; + return QMessageBox::question(this, title, msg, QMessageBox::Yes | QMessageBox::No, def) == QMessageBox::Yes; + } } } diff --git a/src/widget/widget.h b/src/widget/widget.h index f061f1b23..46edf7ce9 100644 --- a/src/widget/widget.h +++ b/src/widget/widget.h @@ -66,7 +66,7 @@ public: void updateTrayIcon(); Q_INVOKABLE void showWarningMsgBox(const QString& title, const QString& msg); Q_INVOKABLE void setEnabledThreadsafe(bool enabled); - Q_INVOKABLE bool askQuestion(const QString& title, const QString& msg, bool warning = true); + 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); // hooray for threading hacks ~Widget(); From db2d9321e488b90e1cfd106b9117adc14f142ac5 Mon Sep 17 00:00:00 2001 From: Dubslow Date: Fri, 5 Dec 2014 20:58:44 -0600 Subject: [PATCH 23/49] fix change profiles button --- src/core.cpp | 50 +++++++++++++++++++++++++++---------------- src/core.h | 2 ++ src/widget/widget.cpp | 8 +++---- src/widget/widget.h | 2 +- 4 files changed, 39 insertions(+), 23 deletions(-) diff --git a/src/core.cpp b/src/core.cpp index aaba1320a..3241969eb 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -88,15 +88,23 @@ Core::Core(Camera* cam, QThread *CoreThread, QString loadPath) : Audio::openInput(inDevDescr); } -Core::~Core() +void Core::deadifyTox() { - if (tox) + if (toxav) { toxav_kill(toxav); toxav = nullptr; + } + if (tox) + { tox_kill(tox); tox = nullptr; } +} + +Core::~Core() +{ + deadifyTox(); if (videobuf) { @@ -164,9 +172,6 @@ void Core::make_tox() { if (toxOptions.proxy_enabled) { - //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(); } @@ -210,14 +215,17 @@ 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()) + { + qCritical() << "Core: loadConfiguration failed, exiting now"; + deadifyTox(); + emit failedToStart(); + return; + } } + // loadPath is meaningless after this loadPath = ""; } else // new ID @@ -1142,6 +1150,7 @@ QString Core::sanitize(QString name) bool Core::loadConfiguration(QString path) { + loadPath = ""; // if not empty, then user forgot a password // setting the profile is now the responsibility of the caller QFile configurationFile(path); qDebug() << "Core::loadConfiguration: reading from " << path; @@ -1169,12 +1178,22 @@ bool Core::loadConfiguration(QString path) if (!loadEncryptedSave(data)) { configurationFile.close(); + + QString profile; + QMetaObject::invokeMethod(Widget::getInstance(), "askProfiles", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QString, profile)); + + if (!profile.isEmpty()) + loadPath = QDir(Settings::getSettingsDirPath()).filePath(profile + TOX_EXT); return false; } } } configurationFile.close(); + Settings::getInstance().setCurrentProfile(QFileInfo(path).completeBaseName()); + // this is necessary for anything that doesn't call switchConfiguration, i.e. + // forgetting a password and choosing a different profile + // set GUI with user and statusmsg QString name = getUsername(); if (!name.isEmpty()) @@ -1237,12 +1256,7 @@ void Core::switchConfiguration(const QString& profile) 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 diff --git a/src/core.h b/src/core.h index ed50b1033..77bc92be9 100644 --- a/src/core.h +++ b/src/core.h @@ -274,6 +274,8 @@ private: void checkLastOnline(int friendId); + void deadifyTox(); + private slots: void onFileTransferFinished(ToxFile file); diff --git a/src/widget/widget.cpp b/src/widget/widget.cpp index 4fe4ecbe1..cc38323e5 100644 --- a/src/widget/widget.cpp +++ b/src/widget/widget.cpp @@ -384,7 +384,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 @@ -399,10 +399,10 @@ QString Widget::detectProfile() #endif { profile = askProfiles(); - if (profile != "") - return dir.filePath(profile + Core::TOX_EXT); - else + if (profile.isEmpty()) return ""; + else + return dir.filePath(profile + Core::TOX_EXT); } } else diff --git a/src/widget/widget.h b/src/widget/widget.h index 46edf7ce9..7623c0de3 100644 --- a/src/widget/widget.h +++ b/src/widget/widget.h @@ -69,6 +69,7 @@ public: 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); // hooray for threading hacks + Q_INVOKABLE QString askProfiles(); ~Widget(); virtual void closeEvent(QCloseEvent *event); @@ -144,7 +145,6 @@ private: void removeGroup(Group* g, bool fake = false); void saveWindowGeometry(); void saveSplitterGeometry(); - QString askProfiles(); QString detectProfile(); QSystemTrayIcon *icon; QMenu *trayMenu; From e90dfd30834a59aa48956300a8230b4bce9f2092 Mon Sep 17 00:00:00 2001 From: Dubslow Date: Fri, 5 Dec 2014 21:13:04 -0600 Subject: [PATCH 24/49] Move privacy tab settings to per-profile settings file This may lose the current settings in the tab, but currently only keep history is enabled anyways --- src/misc/settings.cpp | 110 +++++++++++++++++++++--------------------- src/misc/settings.h | 4 +- 2 files changed, 57 insertions(+), 57 deletions(-) diff --git a/src/misc/settings.cpp b/src/misc/settings.cpp index c5e13b1e7..257f9cefe 100644 --- a/src/misc/settings.cpp +++ b/src/misc/settings.cpp @@ -188,13 +188,6 @@ void Settings::load() splitterState = s.value("splitterState", QByteArray()).toByteArray(); s.endGroup(); - s.beginGroup("Privacy"); - typingNotification = s.value("typingNotification", false).toBool(); - enableLogging = s.value("enableLogging", false).toBool(); - encryptLogs = s.value("encryptLogs", false).toBool(); - encryptTox = s.value("encryptTox", false).toBool(); - s.endGroup(); - s.beginGroup("Audio"); inDev = s.value("inDev", "").toString(); outDev = s.value("outDev", "").toString(); @@ -222,38 +215,45 @@ void Settings::load() loaded = true; - if (currentProfile.isEmpty()) // new profile in Core::switchConfiguration - return; - - // load from a profile specific friend data list if possible - QString tmp = dir.filePath(currentProfile + ".ini"); - if (QFile(tmp).exists()) - filePath = tmp; + 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; + + 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 "< Date: Fri, 5 Dec 2014 21:21:10 -0600 Subject: [PATCH 25/49] word wrap the privacy tab string --- src/widget/form/settings/privacysettings.ui | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/widget/form/settings/privacysettings.ui b/src/widget/form/settings/privacysettings.ui index d2e235918..8843be443 100644 --- a/src/widget/form/settings/privacysettings.ui +++ b/src/widget/form/settings/privacysettings.ui @@ -75,6 +75,9 @@ All internet communication is encrypted, and this cannot be disabled. However, you may optionally password protect your local Tox files. + + true + From c0303e7a0ff28a22451b24e3b8af298941b64f27 Mon Sep 17 00:00:00 2001 From: apprb Date: Sat, 6 Dec 2014 12:17:13 +0600 Subject: [PATCH 26/49] password strength meter --- src/widget/form/setpassworddialog.cpp | 42 +++++++++++++++++- src/widget/form/setpassworddialog.h | 1 + src/widget/form/setpassworddialog.ui | 62 +++++++++++++++------------ 3 files changed, 76 insertions(+), 29 deletions(-) diff --git a/src/widget/form/setpassworddialog.cpp b/src/widget/form/setpassworddialog.cpp index c8d08cfef..553070dc7 100644 --- a/src/widget/form/setpassworddialog.cpp +++ b/src/widget/form/setpassworddialog.cpp @@ -18,6 +18,8 @@ #include "ui_setpassworddialog.h" #include +const double SetPasswordDialog::reasonablePasswordLength = 8.; + SetPasswordDialog::SetPasswordDialog(QString body, QString extraButton, QWidget* parent) : QDialog(parent) , ui(new Ui::SetPasswordDialog) @@ -45,11 +47,47 @@ SetPasswordDialog::~SetPasswordDialog() void SetPasswordDialog::onPasswordEdit() { - if ( ui->passwordlineEdit->text().length() >= 8 - && 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); else ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + + // 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() diff --git a/src/widget/form/setpassworddialog.h b/src/widget/form/setpassworddialog.h index 967b7d3e0..d877fff59 100644 --- a/src/widget/form/setpassworddialog.h +++ b/src/widget/form/setpassworddialog.h @@ -37,6 +37,7 @@ private slots: private: Ui::SetPasswordDialog *ui; + static const double reasonablePasswordLength; }; #endif // SETPASSWORDDIALOG_H diff --git a/src/widget/form/setpassworddialog.ui b/src/widget/form/setpassworddialog.ui index 3d6229d79..8d7e7c01d 100644 --- a/src/widget/form/setpassworddialog.ui +++ b/src/widget/form/setpassworddialog.ui @@ -6,8 +6,8 @@ 0 0 - 434 - 175 + 493 + 160 @@ -18,52 +18,56 @@ - + - - - Qt::Vertical - - - - 20 - 12 - - - - - - - + + + + + Repeat Password + + + + Type password: - + + + + QLineEdit::Password + + + + QLineEdit::Password - + - + - + - Repeat Password + Password strength meter - - - QLineEdit::Password + + + 0 + + + @@ -77,7 +81,7 @@ 20 - 12 + 40 @@ -97,6 +101,10 @@ + + passwordlineEdit + repasswordlineEdit + From 7fc7b57934e6dabcf3fb72f25e852f1d8777066b Mon Sep 17 00:00:00 2001 From: Dubslow Date: Mon, 8 Dec 2014 16:03:51 -0600 Subject: [PATCH 27/49] refactor/fix switching profile with separate settings --- src/core.cpp | 20 ++++++++------------ src/misc/settings.cpp | 7 +++++++ src/misc/settings.h | 1 + src/widget/widget.cpp | 3 +++ 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/core.cpp b/src/core.cpp index 3241969eb..440ee952a 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -1150,8 +1150,8 @@ QString Core::sanitize(QString name) bool Core::loadConfiguration(QString path) { - loadPath = ""; // if not empty, then user forgot a password - // 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; @@ -1183,17 +1183,17 @@ bool Core::loadConfiguration(QString path) QMetaObject::invokeMethod(Widget::getInstance(), "askProfiles", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QString, profile)); if (!profile.isEmpty()) + { loadPath = QDir(Settings::getSettingsDirPath()).filePath(profile + TOX_EXT); + Settings::getInstance().switchProfile(profile); + HistoryKeeper::getInstance()->resetInstance(); + } return false; } } } configurationFile.close(); - Settings::getInstance().setCurrentProfile(QFileInfo(path).completeBaseName()); - // this is necessary for anything that doesn't call switchConfiguration, i.e. - // forgetting a password and choosing a different profile - // set GUI with user and statusmsg QString name = getUsername(); if (!name.isEmpty()) @@ -1233,7 +1233,7 @@ 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); @@ -1266,11 +1266,7 @@ 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(); + Settings::getInstance().switchProfile(profile); HistoryKeeper::getInstance()->resetInstance(); start(); diff --git a/src/misc/settings.cpp b/src/misc/settings.cpp index 257f9cefe..d90080b6d 100644 --- a/src/misc/settings.cpp +++ b/src/misc/settings.cpp @@ -63,6 +63,13 @@ void Settings::resetInstance() } } +void Settings::switchProfile(const QString& profile) +{ + setCurrentProfile(profile); + save(false); + resetInstance(); +} + void Settings::load() { if (loaded) diff --git a/src/misc/settings.h b/src/misc/settings.h index 1ed006f8d..c99fea311 100644 --- a/src/misc/settings.h +++ b/src/misc/settings.h @@ -30,6 +30,7 @@ class Settings : public QObject public: static Settings& getInstance(); static void resetInstance(); + void switchProfile(const QString& profile); ~Settings() = default; void executeSettingsDialog(QWidget* parent); diff --git a/src/widget/widget.cpp b/src/widget/widget.cpp index cc38323e5..4b468c89d 100644 --- a/src/widget/widget.cpp +++ b/src/widget/widget.cpp @@ -402,7 +402,10 @@ QString Widget::detectProfile() if (profile.isEmpty()) return ""; else + { + Settings::getInstance().setCurrentProfile(profile); return dir.filePath(profile + Core::TOX_EXT); + } } } else From c5b945bbd5963e06d4edfc859d4127dd1f4a25f1 Mon Sep 17 00:00:00 2001 From: Dubslow Date: Mon, 8 Dec 2014 16:40:20 -0600 Subject: [PATCH 28/49] minor tweaks --- src/core.cpp | 4 +--- src/coreencryption.cpp | 11 +++-------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/core.cpp b/src/core.cpp index 440ee952a..b4c1d6736 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -23,7 +23,6 @@ #include "src/audio.h" #include -#include #include #include @@ -39,7 +38,6 @@ #include #include #include -#include #include const QString Core::CONFIG_FILE_NAME = "data"; @@ -1251,11 +1249,11 @@ void Core::switchConfiguration(const QString& profile) saveConfiguration(); ready = false; + Widget::getInstance()->setEnabledThreadsafe(false); clearPassword(ptMain); clearPassword(ptHistory); toxTimer->stop(); - Widget::getInstance()->setEnabledThreadsafe(false); deadifyTox(); emit selfAvatarChanged(QPixmap(":/img/contact_dark.png")); diff --git a/src/coreencryption.cpp b/src/coreencryption.cpp index f4efbfb45..c1c15649f 100644 --- a/src/coreencryption.cpp +++ b/src/coreencryption.cpp @@ -33,13 +33,11 @@ void Core::setPassword(QString& password, PasswordType passtype, uint8_t* salt) { + clearPassword(passtype); if (password.isEmpty()) - { - clearPassword(passtype); return; - } - if (!pwsaltedkeys[passtype]) - pwsaltedkeys[passtype] = new uint8_t[tox_pass_key_length()]; + + pwsaltedkeys[passtype] = new uint8_t[tox_pass_key_length()]; CString str(password); if (salt) @@ -56,10 +54,7 @@ void Core::useOtherPassword(PasswordType type) if (type == ptHistory) pwsaltedkeys[ptHistory] = pwsaltedkeys[ptMain]; else if (type == ptMain) - { pwsaltedkeys[ptMain] = pwsaltedkeys[ptHistory]; - saveConfiguration(); - } } void Core::clearPassword(PasswordType passtype) From 0e21a8d497668781695528b15abdbe1286fe02ef Mon Sep 17 00:00:00 2001 From: Dubslow Date: Tue, 9 Dec 2014 17:44:20 -0600 Subject: [PATCH 29/49] check for non-existent file --- src/misc/db/encrypteddb.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/misc/db/encrypteddb.cpp b/src/misc/db/encrypteddb.cpp index d2315e594..5b3e46749 100644 --- a/src/misc/db/encrypteddb.cpp +++ b/src/misc/db/encrypteddb.cpp @@ -33,23 +33,24 @@ EncryptedDb::EncryptedDb(const QString &fname, QList initList) : QByteArray fileContent; if (pullFileContent(fileName, buffer)) { - qDebug() << "writing old data"; + qDebug() << "EncryptedDb: writing old data"; encrFile.setFileName(fileName); encrFile.open(QIODevice::ReadOnly); fileContent = encrFile.readAll(); chunkPosition = encrFile.size() / encryptedChunkSize; encrFile.close(); - } else { - qWarning() << "corrupted history log file will be wiped!"; - chunkPosition = 0; } + else + chunkPosition = 0; encrFile.setFileName(fileName); if (!encrFile.open(QIODevice::WriteOnly)) { qWarning() << "can't open file:" << fileName; - } else { + } + else + { encrFile.write(fileContent); encrFile.flush(); } @@ -75,7 +76,12 @@ bool EncryptedDb::pullFileContent(const QString &fname, QByteArray &buf) qDebug() << "EncryptedDb::pullFileContent()"; QFile dbFile(fname); - dbFile.open(QIODevice::ReadOnly); + if (!dbFile.open(QIODevice::ReadOnly)) + { + qDebug() << "EncryptedDb::pullFileContent: file doesn't exist"; + buf = QByteArray(); + return false; + } QByteArray fileContent; while (!dbFile.atEnd()) @@ -87,7 +93,7 @@ bool EncryptedDb::pullFileContent(const QString &fname, QByteArray &buf) { fileContent += buf; } else { - qWarning() << "Encrypted history log is corrupted: can't decrypt"; + qWarning() << "Encrypted history log is corrupted: can't decrypt, will be deleted"; buf = QByteArray(); return false; } From 31c072d29658dad1adb3b3cb3909fc1d18835a9b Mon Sep 17 00:00:00 2001 From: Dubslow Date: Tue, 9 Dec 2014 18:08:38 -0600 Subject: [PATCH 30/49] more file open checking --- src/coreencryption.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/coreencryption.cpp b/src/coreencryption.cpp index c1c15649f..5760a8ba6 100644 --- a/src/coreencryption.cpp +++ b/src/coreencryption.cpp @@ -105,7 +105,11 @@ bool Core::isPasswordSet(PasswordType passtype) QByteArray Core::getSaltFromFile(QString filename) { QFile file(filename); - file.open(QIODevice::ReadOnly); + if (!file.open(QIODevice::ReadOnly)) + { + qWarning() << "Core: encrypted history file doesn't exist"; + return QByteArray(); + } QByteArray data = file.read(tox_pass_encryption_extra_length()); file.close(); @@ -173,7 +177,7 @@ void Core::checkEncryptedHistory() if (salt.size() == 0) { // maybe we should handle this better - Widget::getInstance()->showWarningMsgBox(tr("Encrypted History"), tr("No encrypted history file found.\nHistory will be disabled!")); + 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); return; From 09602731f79c3e41124c7470807240386cbf3cfb Mon Sep 17 00:00:00 2001 From: Dubslow Date: Tue, 9 Dec 2014 18:40:05 -0600 Subject: [PATCH 31/49] when loading, try using main password for history --- src/coreencryption.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/coreencryption.cpp b/src/coreencryption.cpp index 5760a8ba6..c3d5dc883 100644 --- a/src/coreencryption.cpp +++ b/src/coreencryption.cpp @@ -196,6 +196,14 @@ void Core::checkEncryptedHistory() else dialogtxt = a; + if (pwsaltedkeys[ptMain]) + { + useOtherPassword(ptHistory); + if (HistoryKeeper::checkPassword()) + return; + clearPassword(ptHistory); + } + bool error = true; do { From a1a1a6f989b9bbcc3e28dae2f3e19b65089abb45 Mon Sep 17 00:00:00 2001 From: Dubslow Date: Tue, 9 Dec 2014 19:06:09 -0600 Subject: [PATCH 32/49] fix clearing duplicate passwords fixes dubslow/qTox#2 --- src/coreencryption.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/coreencryption.cpp b/src/coreencryption.cpp index c3d5dc883..561b38aaa 100644 --- a/src/coreencryption.cpp +++ b/src/coreencryption.cpp @@ -59,11 +59,11 @@ void Core::useOtherPassword(PasswordType type) void Core::clearPassword(PasswordType passtype) { - if (pwsaltedkeys[passtype]) - { - delete[] pwsaltedkeys[passtype]; - pwsaltedkeys[passtype] = nullptr; - } + PasswordType other = (passtype == ptMain) ? ptHistory : ptMain; + if (pwsaltedkeys[passtype] == pwsaltedkeys[other]) + pwsaltedkeys[other] = nullptr; + delete[] pwsaltedkeys[passtype]; + pwsaltedkeys[passtype] = nullptr; } QByteArray Core::encryptData(const QByteArray& data, PasswordType passtype) From de6a443f3739336a5c67db489c53d8ef3c38a708 Mon Sep 17 00:00:00 2001 From: Dubslow Date: Tue, 9 Dec 2014 19:09:28 -0600 Subject: [PATCH 33/49] fix loading an encrypted chat log failing --- src/core.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core.cpp b/src/core.cpp index b4c1d6736..a05a87676 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -1184,7 +1184,7 @@ bool Core::loadConfiguration(QString path) { loadPath = QDir(Settings::getSettingsDirPath()).filePath(profile + TOX_EXT); Settings::getInstance().switchProfile(profile); - HistoryKeeper::getInstance()->resetInstance(); + HistoryKeeper::resetInstance(); } return false; } @@ -1265,7 +1265,7 @@ void Core::switchConfiguration(const QString& profile) loadPath = QDir(Settings::getSettingsDirPath()).filePath(profile + TOX_EXT); Settings::getInstance().switchProfile(profile); - HistoryKeeper::getInstance()->resetInstance(); + HistoryKeeper::resetInstance(); start(); Widget::getInstance()->setEnabledThreadsafe(true); From 826d60c8767ac1cc860d13677999a1a36c4a72be Mon Sep 17 00:00:00 2001 From: Dubslow Date: Thu, 15 Jan 2015 21:41:10 -0600 Subject: [PATCH 34/49] fix bug with "use other password" It's more trouble than it's worth to shallow copy; changed to deep copy with STL --- src/coreencryption.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/coreencryption.cpp b/src/coreencryption.cpp index 561b38aaa..1ccca7f49 100644 --- a/src/coreencryption.cpp +++ b/src/coreencryption.cpp @@ -48,20 +48,19 @@ void Core::setPassword(QString& password, PasswordType passtype, uint8_t* salt) password.clear(); } +#include void Core::useOtherPassword(PasswordType type) { clearPassword(type); - if (type == ptHistory) - pwsaltedkeys[ptHistory] = pwsaltedkeys[ptMain]; - else if (type == ptMain) - pwsaltedkeys[ptMain] = pwsaltedkeys[ptHistory]; + 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) { - PasswordType other = (passtype == ptMain) ? ptHistory : ptMain; - if (pwsaltedkeys[passtype] == pwsaltedkeys[other]) - pwsaltedkeys[other] = nullptr; delete[] pwsaltedkeys[passtype]; pwsaltedkeys[passtype] = nullptr; } From b1180a20380d9f45623e4c51123e9184126fe433 Mon Sep 17 00:00:00 2001 From: Dubslow Date: Mon, 19 Jan 2015 21:16:28 -0600 Subject: [PATCH 35/49] Improve dialogs around unencrypting history, per zetok's suggestions --- src/widget/form/settings/privacyform.cpp | 32 ++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/src/widget/form/settings/privacyform.cpp b/src/widget/form/settings/privacyform.cpp index 6efca0ba7..9a326d5a1 100644 --- a/src/widget/form/settings/privacyform.cpp +++ b/src/widget/form/settings/privacyform.cpp @@ -121,16 +121,44 @@ void PrivacyForm::onEncryptLogsUpdated() } else { - if (Widget::getInstance()->askQuestion(tr("Old encrypted chat logs", "title"), tr("Would you like to un-encrypt your chat logs?\nOtherwise they will be deleted."), true, false)) + QMessageBox::StandardButton button = QMessageBox::warning( + Widget::getInstance(), + tr("Old encrypted chat logs", "title"), + tr("Would you like to un-encrypt your chat logs?\nOtherwise they will be deleted."), + QMessageBox::Ok | QMessageBox::No | QMessageBox::Cancel, + QMessageBox::Ok + ); + + if (button == QMessageBox::Ok) { QList 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::No | QMessageBox::Cancel, + QMessageBox::Cancel + ) + == QMessageBox::No) + { + HistoryKeeper::removeHistory(true); + } + else + { + bodyUI->cbEncryptHistory->setChecked(true); + return; + } + } else { - HistoryKeeper::removeHistory(true); + bodyUI->cbEncryptHistory->setChecked(true); + return; } } From f645faff51c3949caea53a08b8803cef45822880 Mon Sep 17 00:00:00 2001 From: Dubslow Date: Mon, 19 Jan 2015 21:49:41 -0600 Subject: [PATCH 36/49] Ignore empty history files on start This fixes an esoteric issue of enabling chat log encryption and then restarting before adding any messages --- src/coreencryption.cpp | 17 +++++++++++------ src/misc/db/encrypteddb.cpp | 6 ++---- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/coreencryption.cpp b/src/coreencryption.cpp index 1ccca7f49..7e7ff5f1e 100644 --- a/src/coreencryption.cpp +++ b/src/coreencryption.cpp @@ -106,7 +106,7 @@ QByteArray Core::getSaltFromFile(QString filename) QFile file(filename); if (!file.open(QIODevice::ReadOnly)) { - qWarning() << "Core: encrypted history file doesn't exist"; + qWarning() << "Core: file" << filename << "doesn't exist"; return QByteArray(); } QByteArray data = file.read(tox_pass_encryption_extra_length()); @@ -172,9 +172,11 @@ bool Core::loadEncryptedSave(QByteArray& data) void Core::checkEncryptedHistory() { - QByteArray salt = getSaltFromFile(HistoryKeeper::getHistoryPath()); + QString path = HistoryKeeper::getHistoryPath(); + bool exists = QFile::exists(path); - if (salt.size() == 0) + 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); @@ -188,7 +190,7 @@ void Core::checkEncryptedHistory() if (pwsaltedkeys[ptHistory]) { - if (HistoryKeeper::checkPassword()) + 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"); } @@ -198,8 +200,11 @@ void Core::checkEncryptedHistory() if (pwsaltedkeys[ptMain]) { useOtherPassword(ptHistory); - if (HistoryKeeper::checkPassword()) + if (!exists || HistoryKeeper::checkPassword()) + { + qDebug() << "Core: using main password for history"; return; + } clearPassword(ptHistory); } @@ -218,7 +223,7 @@ void Core::checkEncryptedHistory() else setPassword(pw, ptHistory, reinterpret_cast(salt.data())); - error = !HistoryKeeper::checkPassword(); + error = exists && !HistoryKeeper::checkPassword(); dialogtxt = a + " " + b; } while (error); } diff --git a/src/misc/db/encrypteddb.cpp b/src/misc/db/encrypteddb.cpp index 5b3e46749..709608667 100644 --- a/src/misc/db/encrypteddb.cpp +++ b/src/misc/db/encrypteddb.cpp @@ -73,8 +73,6 @@ QSqlQuery EncryptedDb::exec(const QString &query) bool EncryptedDb::pullFileContent(const QString &fname, QByteArray &buf) { - qDebug() << "EncryptedDb::pullFileContent()"; - QFile dbFile(fname); if (!dbFile.open(QIODevice::ReadOnly)) { @@ -87,13 +85,13 @@ bool EncryptedDb::pullFileContent(const QString &fname, QByteArray &buf) while (!dbFile.atEnd()) { QByteArray encrChunk = dbFile.read(encryptedChunkSize); - qDebug() << "got chunk:" << encrChunk.size(); + qDebug() << "EncryptedDb::pullFileContent: got chunk:" << encrChunk.size(); buf = Core::getInstance()->decryptData(encrChunk, Core::ptHistory); if (buf.size() > 0) { fileContent += buf; } else { - qWarning() << "Encrypted history log is corrupted: can't decrypt, will be deleted"; + qWarning() << "EncryptedDb::pullFileContent: Encrypted history log is corrupted: can't decrypt, will be deleted"; buf = QByteArray(); return false; } From 5ea6e412f82ad3492afaa705a838f757ec13ca40 Mon Sep 17 00:00:00 2001 From: Dubslow Date: Mon, 19 Jan 2015 21:59:27 -0600 Subject: [PATCH 37/49] Slightly more useful initial password dialogs --- src/coreencryption.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreencryption.cpp b/src/coreencryption.cpp index 7e7ff5f1e..524eb12a2 100644 --- a/src/coreencryption.cpp +++ b/src/coreencryption.cpp @@ -130,7 +130,7 @@ bool Core::loadEncryptedSave(QByteArray& data) 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 this profile.", "used in load() when no pw is already set")); + 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; @@ -184,7 +184,7 @@ void Core::checkEncryptedHistory() return; } - QString a(tr("Please enter the password for the chat logs.", "used in load() when no hist pw set")); + 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; From 7aa52ff754ed78b324e59cf5643a80f70afd7524 Mon Sep 17 00:00:00 2001 From: Dubslow Date: Mon, 19 Jan 2015 22:23:13 -0600 Subject: [PATCH 38/49] Fix profile settings issue from command line, default unencrypt history is Cancel --- src/main.cpp | 2 +- src/widget/form/settings/privacyform.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index ef378bfbc..fb2ccee12 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -71,7 +71,7 @@ int main(int argc, char *argv[]) Settings::getInstance(); // Build our Settings singleton as soon as QApplication is ready, not before if (parser.isSet("P")) - Settings::getInstance().setCurrentProfile(parser.value("P")); + Settings::getInstance().switchProfile(parser.value("P")); sodium_init(); // For the auto-updater diff --git a/src/widget/form/settings/privacyform.cpp b/src/widget/form/settings/privacyform.cpp index 9a326d5a1..e8b603872 100644 --- a/src/widget/form/settings/privacyform.cpp +++ b/src/widget/form/settings/privacyform.cpp @@ -126,7 +126,7 @@ void PrivacyForm::onEncryptLogsUpdated() tr("Old encrypted chat logs", "title"), tr("Would you like to un-encrypt your chat logs?\nOtherwise they will be deleted."), QMessageBox::Ok | QMessageBox::No | QMessageBox::Cancel, - QMessageBox::Ok + QMessageBox::Cancel ); if (button == QMessageBox::Ok) From dea2ccf0b79fe5760dff7c489d799c27d58e3d80 Mon Sep 17 00:00:00 2001 From: Dubslow Date: Fri, 23 Jan 2015 05:48:55 -0600 Subject: [PATCH 39/49] Fix segfault, minor cleanup --- src/core.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/core.cpp b/src/core.cpp index 56a6fb542..44b6c2e31 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -224,7 +224,6 @@ void Core::start() if (loadPath.isEmpty()) { qCritical() << "Core: loadConfiguration failed, exiting now"; - deadifyTox(); emit failedToStart(); return; } @@ -302,7 +301,7 @@ void Core::start() void Core::process() { - if (!tox) + if (!isReady()) return; static int tolerance = CORE_DISCONNECT_TOLERANCE; @@ -1004,7 +1003,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); @@ -1016,7 +1015,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); @@ -1673,7 +1672,7 @@ QString Core::getPeerName(const ToxID& id) const bool Core::isReady() { - return ready; + return toxav && tox && ready; } void Core::setNospam(uint32_t nospam) From 18e875ef207cfc5a6d5c2c43fe2a9998dba9614d Mon Sep 17 00:00:00 2001 From: Dubslow Date: Fri, 23 Jan 2015 06:29:24 -0600 Subject: [PATCH 40/49] Fix bug with initial password dialogs accepting empty passwords --- src/widget/widget.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/widget/widget.cpp b/src/widget/widget.cpp index 07f50e64d..362a8bfdc 100644 --- a/src/widget/widget.cpp +++ b/src/widget/widget.cpp @@ -1269,10 +1269,19 @@ QString Widget::passwordDialog(const QString& cancel, const QString& body) dialog.setInputMode(QInputDialog::TextInput); dialog.setTextEchoMode(QLineEdit::Password); dialog.setLabelText(body); - int val = dialog.exec(); - if (val == QDialog::Accepted) - ret = dialog.textValue(); - return ret; + 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.")); + } } } From 358a56c555bb7e2dd2d9abe42aa23d358ad407e7 Mon Sep 17 00:00:00 2001 From: Dubslow Date: Fri, 23 Jan 2015 07:05:46 -0600 Subject: [PATCH 41/49] Confirm decrypt data, various ui/txt tweaks --- src/coreencryption.cpp | 4 ++-- src/widget/form/setpassworddialog.cpp | 9 ++++++++- src/widget/form/setpassworddialog.h | 1 + src/widget/form/setpassworddialog.ui | 25 ++++++++++++++---------- src/widget/form/settings/privacyform.cpp | 15 +++++++++++++- src/widget/widget.h | 2 +- 6 files changed, 41 insertions(+), 15 deletions(-) diff --git a/src/coreencryption.cpp b/src/coreencryption.cpp index 524eb12a2..540f4ae86 100644 --- a/src/coreencryption.cpp +++ b/src/coreencryption.cpp @@ -163,7 +163,7 @@ bool Core::loadEncryptedSave(QByteArray& data) setPassword(pw, ptMain, salt); error = tox_encrypted_key_load(tox, reinterpret_cast(data.data()), data.size(), pwsaltedkeys[ptMain]); - dialogtxt = a + " " + b; + dialogtxt = a + "\n" + b; } while (error != 0); Settings::getInstance().setEncryptTox(true); @@ -224,7 +224,7 @@ void Core::checkEncryptedHistory() setPassword(pw, ptHistory, reinterpret_cast(salt.data())); error = exists && !HistoryKeeper::checkPassword(); - dialogtxt = a + " " + b; + dialogtxt = a + "\n" + b; } while (error); } diff --git a/src/widget/form/setpassworddialog.cpp b/src/widget/form/setpassworddialog.cpp index 553070dc7..9e63f8f99 100644 --- a/src/widget/form/setpassworddialog.cpp +++ b/src/widget/form/setpassworddialog.cpp @@ -23,13 +23,14 @@ 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->repasswordlineEdit, SIGNAL(textChanged(QString)), this, SLOT(onPasswordEdit())); - ui->body->setText(body + tr("\nTo encourage good habits, qTox requires at least 8 characters.")); + ui->body->setText(body + "\n" + tr("The passwords don't match.")); ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); if (!extraButton.isEmpty()) @@ -50,9 +51,15 @@ void SetPasswordDialog::onPasswordEdit() 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 diff --git a/src/widget/form/setpassworddialog.h b/src/widget/form/setpassworddialog.h index d877fff59..e264b79d9 100644 --- a/src/widget/form/setpassworddialog.h +++ b/src/widget/form/setpassworddialog.h @@ -37,6 +37,7 @@ private slots: private: Ui::SetPasswordDialog *ui; + QString body; static const double reasonablePasswordLength; }; diff --git a/src/widget/form/setpassworddialog.ui b/src/widget/form/setpassworddialog.ui index 8d7e7c01d..35a7deeeb 100644 --- a/src/widget/form/setpassworddialog.ui +++ b/src/widget/form/setpassworddialog.ui @@ -6,7 +6,7 @@ 0 0 - 493 + 325 160 @@ -24,15 +24,21 @@ + + Qt::AlignRight + - Repeat Password + Repeat password + + Qt::AlignRight + - Type password: + Type password @@ -50,18 +56,17 @@ - - - - - + + + Qt::AlignRight + - Password strength meter + Password strength - + 0 diff --git a/src/widget/form/settings/privacyform.cpp b/src/widget/form/settings/privacyform.cpp index e8b603872..de8c33678 100644 --- a/src/widget/form/settings/privacyform.cpp +++ b/src/widget/form/settings/privacyform.cpp @@ -124,7 +124,7 @@ void PrivacyForm::onEncryptLogsUpdated() QMessageBox::StandardButton button = QMessageBox::warning( Widget::getInstance(), tr("Old encrypted chat logs", "title"), - tr("Would you like to un-encrypt your chat logs?\nOtherwise they will be deleted."), + tr("Would you like to decrypt your chat logs?\nOtherwise they will be deleted."), QMessageBox::Ok | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Cancel ); @@ -204,13 +204,26 @@ void PrivacyForm::onEncryptToxUpdated() Core* core = Core::getInstance(); if (bodyUI->cbEncryptTox->isChecked()) + { if (!core->isPasswordSet(Core::ptMain)) + { if (setToxPassword()) { 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(false); Settings::getInstance().setEncryptTox(false); diff --git a/src/widget/widget.h b/src/widget/widget.h index a236463a2..c849476f4 100644 --- a/src/widget/widget.h +++ b/src/widget/widget.h @@ -68,8 +68,8 @@ public: Q_INVOKABLE void setEnabledThreadsafe(bool enabled); 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); - // hooray for threading hacks Q_INVOKABLE QString askProfiles(); + // hooray for threading hacks ~Widget(); virtual void closeEvent(QCloseEvent *event); From 7fb7ae1fc8aa85f6eda2fe121a0b33e2538d6a99 Mon Sep 17 00:00:00 2001 From: Dubslow Date: Fri, 23 Jan 2015 08:00:50 -0600 Subject: [PATCH 42/49] oops, fortunately this wasn't a bug yet... --- src/widget/widget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widget/widget.cpp b/src/widget/widget.cpp index 362a8bfdc..457f843ed 100644 --- a/src/widget/widget.cpp +++ b/src/widget/widget.cpp @@ -1223,7 +1223,7 @@ bool Widget::askQuestion(const QString& title, const QString& msg, bool defaultA 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(bool, defaultAns), Q_ARG(bool, warning)); From 7552fc0ceb7ec20cf9ce622422a4196e87372434 Mon Sep 17 00:00:00 2001 From: Dubslow Date: Fri, 23 Jan 2015 08:18:46 -0600 Subject: [PATCH 43/49] Fix the segfault fix --- src/core.cpp | 3 +++ src/coreencryption.cpp | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/core.cpp b/src/core.cpp index 44b6c2e31..2c9f9e0c9 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -1220,6 +1220,9 @@ bool Core::loadConfiguration(QString path) void Core::saveConfiguration() { + if (!isReady()) + return; + QString dir = Settings::getSettingsDirPath(); QDir directory(dir); if (!directory.exists() && !directory.mkpath(directory.absolutePath())) { diff --git a/src/coreencryption.cpp b/src/coreencryption.cpp index 540f4ae86..7eb238163 100644 --- a/src/coreencryption.cpp +++ b/src/coreencryption.cpp @@ -230,7 +230,7 @@ void Core::checkEncryptedHistory() void Core::saveConfiguration(const QString& path) { - if (!tox) + if (!isReady()) { qWarning() << "Core::saveConfiguration: Tox not started, aborting!"; return; From f6ff421aff87d33cf0edf2e28c9b575bad9ff122 Mon Sep 17 00:00:00 2001 From: Dubslow Date: Fri, 23 Jan 2015 08:31:13 -0600 Subject: [PATCH 44/49] Fix two minor bugs --- src/widget/form/settings/identityform.cpp | 1 + src/widget/widget.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/widget/form/settings/identityform.cpp b/src/widget/form/settings/identityform.cpp index 86bb5aaa2..8d067b606 100644 --- a/src/widget/form/settings/identityform.cpp +++ b/src/widget/form/settings/identityform.cpp @@ -155,6 +155,7 @@ void IdentityForm::onRenameClicked() 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); diff --git a/src/widget/widget.cpp b/src/widget/widget.cpp index 457f843ed..82da4332f 100644 --- a/src/widget/widget.cpp +++ b/src/widget/widget.cpp @@ -403,7 +403,7 @@ QString Widget::detectProfile() return ""; else { - Settings::getInstance().setCurrentProfile(profile); + Settings::getInstance().switchProfile(profile); return dir.filePath(profile + Core::TOX_EXT); } } From c31192abee8eeb767c594028c5ea5815d014952e Mon Sep 17 00:00:00 2001 From: Dubslow Date: Fri, 23 Jan 2015 08:52:25 -0600 Subject: [PATCH 45/49] Fix threading issue while widget's killing itself --- src/core.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core.cpp b/src/core.cpp index 2c9f9e0c9..e4fdd7529 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -1276,7 +1276,8 @@ void Core::switchConfiguration(const QString& profile) HistoryKeeper::resetInstance(); start(); - Widget::getInstance()->setEnabledThreadsafe(true); + if (isReady()) + Widget::getInstance()->setEnabledThreadsafe(true); } void Core::loadFriends() From a1239747ac101abc65cdb7f81bdf1f3e2ffe0676 Mon Sep 17 00:00:00 2001 From: Dubslow Date: Mon, 26 Jan 2015 19:12:36 -0600 Subject: [PATCH 46/49] fix button typo, closes dubslow/qTox#7 --- src/widget/form/settings/privacyform.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/widget/form/settings/privacyform.cpp b/src/widget/form/settings/privacyform.cpp index de8c33678..d704278e6 100644 --- a/src/widget/form/settings/privacyform.cpp +++ b/src/widget/form/settings/privacyform.cpp @@ -142,10 +142,10 @@ void PrivacyForm::onEncryptLogsUpdated() Widget::getInstance(), tr("Old encrypted chat logs", "title"), tr("Are you sure you want to lose your entire chat history?"), - QMessageBox::No | QMessageBox::Cancel, + QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel ) - == QMessageBox::No) + == QMessageBox::Yes) { HistoryKeeper::removeHistory(true); } From ed22dc9ca582d590e17036c53365c2b5d12e2f9d Mon Sep 17 00:00:00 2001 From: Dubslow Date: Mon, 26 Jan 2015 19:22:38 -0600 Subject: [PATCH 47/49] write settings *after* changing them... closes dubslow/qTox#3 --- src/coreencryption.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreencryption.cpp b/src/coreencryption.cpp index 7eb238163..42ee93c90 100644 --- a/src/coreencryption.cpp +++ b/src/coreencryption.cpp @@ -236,8 +236,6 @@ void Core::saveConfiguration(const QString& path) return; } - Settings::getInstance().save(); - QSaveFile configurationFile(path); if (!configurationFile.open(QIODevice::WriteOnly)) { qCritical() << "File " << path << " cannot be opened"; @@ -281,4 +279,6 @@ void Core::saveConfiguration(const QString& path) configurationFile.commit(); delete[] data; } + + Settings::getInstance().save(); } From 60e819baeed7ad2ef7a3097169d2daeefa6f2e60 Mon Sep 17 00:00:00 2001 From: Dubslow Date: Tue, 27 Jan 2015 14:16:28 -0600 Subject: [PATCH 48/49] Hacky fix to cancelling a switch to an encrypted profile closing It's either this or some fairly major refactoring, which is preferable, but I've got another even bigger refactoring in mind, and fixing this will be rolled into that. Closes dubslow/qTox#4 --- src/core.cpp | 29 ++++++++++++++++++++--------- src/core.h | 18 ++++++++++++++---- src/coreencryption.cpp | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 13 deletions(-) diff --git a/src/core.cpp b/src/core.cpp index e4fdd7529..df3f9f562 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -178,7 +178,7 @@ void Core::make_tox() { qCritical() << "Core: bad proxy! no toxcore!"; emit badProxy(); - } + } else { qCritical() << "Tox core failed to start"; @@ -223,9 +223,19 @@ void Core::start() { if (loadPath.isEmpty()) { - qCritical() << "Core: loadConfiguration failed, exiting now"; - emit failedToStart(); - return; + 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 @@ -284,7 +294,7 @@ void Core::start() } else qDebug() << "Core: Error loading self avatar"; - + ready = true; process(); // starts its own timer @@ -1201,7 +1211,7 @@ bool Core::loadConfiguration(QString path) QString name = getUsername(); if (!name.isEmpty()) emit usernameSet(name); - + QString msg = getStatusMessage(); if (!msg.isEmpty()) emit statusMessageSet(msg); @@ -1229,7 +1239,7 @@ void Core::saveConfiguration() qCritical() << "Error while creating directory " << dir; return; } - + QString profile = Settings::getInstance().getCurrentProfile(); if (profile == "") @@ -1241,9 +1251,9 @@ void Core::saveConfiguration() Settings::getInstance().switchProfile(profile); } - + QString path = directory.filePath(profile + TOX_EXT); - + saveConfiguration(path); } @@ -1255,6 +1265,7 @@ 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); diff --git a/src/core.h b/src/core.h index e4166c572..a3389e023 100644 --- a/src/core.h +++ b/src/core.h @@ -46,7 +46,7 @@ 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); @@ -69,9 +69,9 @@ public: void saveConfiguration(); void saveConfiguration(const QString& path); - + 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 @@ -299,7 +299,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; diff --git a/src/coreencryption.cpp b/src/coreencryption.cpp index 42ee93c90..f47ada5f8 100644 --- a/src/coreencryption.cpp +++ b/src/coreencryption.cpp @@ -65,6 +65,43 @@ void Core::clearPassword(PasswordType 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]) From 0470b5a6af984964ebc9f7429dce77b8cfe3e172 Mon Sep 17 00:00:00 2001 From: Dubslow Date: Tue, 27 Jan 2015 17:25:41 -0600 Subject: [PATCH 49/49] Potential fix for history issues, both dubslow/qTox#5 and dubslow/qTox#6 --- src/coreencryption.cpp | 2 ++ src/widget/form/settings/privacyform.cpp | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/coreencryption.cpp b/src/coreencryption.cpp index f47ada5f8..ff1eb58d4 100644 --- a/src/coreencryption.cpp +++ b/src/coreencryption.cpp @@ -218,6 +218,7 @@ void Core::checkEncryptedHistory() 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; } @@ -255,6 +256,7 @@ void Core::checkEncryptedHistory() clearPassword(ptHistory); Settings::getInstance().setEncryptLogs(false); Settings::getInstance().setEnableLogging(false); + HistoryKeeper::resetInstance(); return; } else diff --git a/src/widget/form/settings/privacyform.cpp b/src/widget/form/settings/privacyform.cpp index d704278e6..3582cf82d 100644 --- a/src/widget/form/settings/privacyform.cpp +++ b/src/widget/form/settings/privacyform.cpp @@ -55,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(); } @@ -166,6 +166,7 @@ void PrivacyForm::onEncryptLogsUpdated() Settings::getInstance().setEncryptLogs(false); bodyUI->cbEncryptHistory->setChecked(false); bodyUI->changeLogsPwButton->setEnabled(false); + HistoryKeeper::resetInstance(); } bool PrivacyForm::setToxPassword()