diff --git a/client/main.cpp b/client/main.cpp index 748a5e5b..196d5cec 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -148,21 +148,7 @@ int main(int argc, char *argv[]) Settings settingsTemp; } - QSettings oldSettings(ORGANIZATION_NAME, APPLICATION_NAME); - QSettings newSettings(QSettings::Format::CustomFormat1, QSettings::UserScope, - ORGANIZATION_NAME, APPLICATION_NAME); - -// QString newSettingsFileName = newSettings.fileName(); -// QFile::remove(newSettingsFileName); - - if (!oldSettings.allKeys().isEmpty() && newSettings.allKeys().isEmpty()) { - QString oldSettingsFileName = oldSettings.fileName(); - QString newSettingsFileName = newSettings.fileName(); - qDebug() << "oldSettingsFileName:" << oldSettingsFileName << QFile::exists(oldSettingsFileName) << oldSettings.isWritable(); - qDebug() << "newSettingsFileName:" << newSettingsFileName << QFile::exists(newSettingsFileName) << newSettings.isWritable(); - SecureFormat::chiperSettings(oldSettings, newSettings); - } // MobileUtils::writeToKeychain("testKey", "12345"); // qDebug() << "MobileUtils::readFromKeychain(\"testKey\"):" << MobileUtils::readFromKeychain("testKey"); diff --git a/client/secure_qsettings.cpp b/client/secure_qsettings.cpp index a9beb112..b2eeecbc 100644 --- a/client/secure_qsettings.cpp +++ b/client/secure_qsettings.cpp @@ -2,34 +2,52 @@ #include "secureformat.h" #include +#include SecureQSettings::SecureQSettings(const QString &organization, const QString &application, QObject *parent) : QObject{parent}, - m_setting(organization, application, parent) + m_setting(organization, application, parent), + encryptedKeys({"Servers/serversList"}) { - encrypted = m_setting.value("encrypted").toBool(); + encrypted = m_setting.value("Conf/encrypted").toBool(); // convert settings to encrypted if (! encrypted) { - // TODO: convert - // m_setting.sync(); + for (const QString &key : m_setting.allKeys()) { + if (encryptedKeys.contains(key)) { + const QVariant &val = value(key); + setValue(key, val); + } + } + m_setting.setValue("Conf/encrypted", true); + m_setting.sync(); + encrypted = true; } } QVariant SecureQSettings::value(const QString &key, const QVariant &defaultValue) const { - if (encrypted) { - QByteArray encryptedValue = m_setting.value(key, defaultValue).toByteArray(); + if (m_cache.contains(key)) { + return m_cache.value(key); + } + + QVariant retVal; + if (encrypted && encryptedKeys.contains(key)) { + if (!m_setting.contains(key)) return defaultValue; + + QByteArray encryptedValue = m_setting.value(key).toByteArray(); QByteArray decryptedValue = decryptText(encryptedValue); QDataStream ds(&decryptedValue, QIODevice::ReadOnly); - QVariant v; - ds >> v; - return v; + ds >> retVal; } else { - return m_setting.value(key, defaultValue); + retVal = m_setting.value(key, defaultValue); } + + m_cache.insert(key, retVal); + + return retVal; } void SecureQSettings::setValue(const QString &key, const QVariant &value) @@ -42,6 +60,55 @@ void SecureQSettings::setValue(const QString &key, const QVariant &value) QByteArray encryptedValue = encryptText(decryptedValue); m_setting.setValue(key, encryptedValue); + m_cache.insert(key, value); + + sync(); +} + +void SecureQSettings::remove(const QString &key) +{ + m_setting.remove(key); + m_cache.remove(key); + + sync(); +} + +void SecureQSettings::sync() +{ + m_setting.sync(); +} + +QByteArray SecureQSettings::backupAppConfig() const +{ + QMap cfg; + for (const QString &key : m_setting.allKeys()) { + cfg.insert(key, value(key)); + } + + QByteArray ba; + { + QDataStream ds(&ba, QIODevice::WriteOnly); + ds << cfg; + } + + return ba.toBase64(); +} + +void SecureQSettings::restoreAppConfig(const QByteArray &base64Cfg) +{ + QByteArray ba = QByteArray::fromBase64(base64Cfg); + QMap cfg; + + { + QDataStream ds(&ba, QIODevice::ReadOnly); + ds >> cfg; + } + + for (const QString &key : cfg.keys()) { + setValue(key, cfg.value(key)); + } + + sync(); } diff --git a/client/secure_qsettings.h b/client/secure_qsettings.h index a7f32f64..113757a6 100644 --- a/client/secure_qsettings.h +++ b/client/secure_qsettings.h @@ -11,12 +11,19 @@ public: QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const; void setValue(const QString &key, const QVariant &value); - void sync() { m_setting.sync(); } - void remove(const QString &key) { m_setting.remove(key); } + void remove(const QString &key); + void sync(); + + QByteArray backupAppConfig() const; + void restoreAppConfig(const QByteArray &base64Cfg); private: QSettings m_setting; bool encrypted {false}; + + mutable QMap m_cache; + + QStringList encryptedKeys; // encode only key listed here }; #endif // SECUREQSETTINGS_H diff --git a/client/secureformat.cpp b/client/secureformat.cpp index 6dfe185c..1acdeea7 100644 --- a/client/secureformat.cpp +++ b/client/secureformat.cpp @@ -148,71 +148,7 @@ QByteArray decryptText(const QByteArray& qEncryptArray) { return QByteArray::fromRawData((const char *)decryptPlainText, qEncryptArray.size()); } -SecureFormat::SecureFormat() -{ - m_format = QSettings::registerFormat("sconf", - readSecureFile, - writeSecureFile); - qDebug() << "SecureFormat" << m_format; -} -bool SecureFormat::readSecureFile(QIODevice& device, QSettings::SettingsMap& map) { - if (!device.isOpen()) { - return false; - } - QTextStream inStream(&device); - while (!inStream.atEnd()) { - QString line = inStream.readLine(); - QStringList keyValue = line.split("<=>"); - QString key = keyValue.first(); - QString value = decryptText(keyValue.last().toUtf8()); - map.insert(key, value); - qDebug() << "SecureFormat::readSecureFile: " << key << "<=>" << value; - } - - return true; -} - -bool SecureFormat::writeSecureFile(QIODevice& device, const QSettings::SettingsMap& map) { -// if (!device.isOpen()) { -// return false; -// } - -// QTextStream outStream(&device); -// auto keys = map.keys(); -// for (auto key : keys) { -// QString value = map.value(key).toString(); -// QByteArray qEncryptArray = encryptText(value); -// outStream << key << "<=>" << qEncryptArray << "\n"; - -// qDebug() << "SecureFormat::writeSecureFile: " << key << "<=>" << qEncryptArray; -// } - - return true; -} - -void SecureFormat::chiperSettings(const QSettings &oldSetting, QSettings &newSetting) { -// QVariantMap keysValuesPairs; -// QStringList keys = oldSetting.allKeys(); -// QStringListIterator it(keys); -// while ( it.hasNext() ) { -// QString currentKey = it.next(); -// keysValuesPairs.insert(currentKey, oldSetting.value(currentKey)); -// } - -// for (const QString& key : keys) { -// QString value = keysValuesPairs.value(key).toString(); -// QByteArray qEncryptArray = encryptText(value); - -// newSetting.setValue(key, qEncryptArray); -// } - -// newSetting.sync(); -} - -const QSettings::Format& SecureFormat::format() const{ - return m_format; -} diff --git a/client/secureformat.h b/client/secureformat.h index c49ed214..04a25b64 100644 --- a/client/secureformat.h +++ b/client/secureformat.h @@ -12,15 +12,9 @@ class SecureFormat public: SecureFormat(); - static bool readSecureFile(QIODevice &device, QSettings::SettingsMap &map); - static bool writeSecureFile(QIODevice &device, const QSettings::SettingsMap &map); - static void chiperSettings(const QSettings &oldSetting, QSettings &newSetting); - const QSettings::Format& format() const; -private: - QSettings::Format m_format; }; #endif // SECUREFORMAT_H diff --git a/client/settings.h b/client/settings.h index 082db891..4241f151 100644 --- a/client/settings.h +++ b/client/settings.h @@ -112,8 +112,10 @@ public: // static constexpr char openNicNs5[] = "94.103.153.176"; // static constexpr char openNicNs13[] = "144.76.103.143"; + QByteArray backupAppConfig() const { return m_settings.backupAppConfig(); } + void restoreAppConfig(const QByteArray &cfg) { m_settings.restoreAppConfig(cfg); } + private: - //static SecureFormat m_secureFormat; SecureQSettings m_settings; }; diff --git a/client/ui/pages_logic/AppSettingsLogic.cpp b/client/ui/pages_logic/AppSettingsLogic.cpp index 6429fc96..ccdb5071 100644 --- a/client/ui/pages_logic/AppSettingsLogic.cpp +++ b/client/ui/pages_logic/AppSettingsLogic.cpp @@ -74,3 +74,26 @@ void AppSettingsLogic::onPushButtonClearLogsClicked() Debug::clearLogs(); Debug::clearServiceLogs(); } + +void AppSettingsLogic::onPushButtonBackupAppConfigClicked() +{ + uiLogic()->saveTextFile("Backup application config", "AmneziaVPN.backup", ".backup", m_settings.backupAppConfig()); +} + +void AppSettingsLogic::onPushButtonRestoreAppConfigClicked() +{ + QString fileName = QFileDialog::getOpenFileName(nullptr, tr("Open backup"), + QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), "*.backup"); + + if (fileName.isEmpty()) return; + + QFile file(fileName); + file.open(QIODevice::ReadOnly); + QByteArray data = file.readAll(); + + m_settings.restoreAppConfig(data); + + emit uiLogic()->goToPage(Page::Vpn); + emit uiLogic()->setStartPage(Page::Vpn); +} + diff --git a/client/ui/pages_logic/AppSettingsLogic.h b/client/ui/pages_logic/AppSettingsLogic.h index b597b129..fc9f0da7 100644 --- a/client/ui/pages_logic/AppSettingsLogic.h +++ b/client/ui/pages_logic/AppSettingsLogic.h @@ -25,6 +25,10 @@ public: Q_INVOKABLE void onPushButtonExportLogsClicked(); Q_INVOKABLE void onPushButtonClearLogsClicked(); + Q_INVOKABLE void onPushButtonBackupAppConfigClicked(); + Q_INVOKABLE void onPushButtonRestoreAppConfigClicked(); + + public: explicit AppSettingsLogic(UiLogic *uiLogic, QObject *parent = nullptr); ~AppSettingsLogic() = default; diff --git a/client/ui/pages_logic/StartPageLogic.cpp b/client/ui/pages_logic/StartPageLogic.cpp index e8b3f269..c21aa6f6 100644 --- a/client/ui/pages_logic/StartPageLogic.cpp +++ b/client/ui/pages_logic/StartPageLogic.cpp @@ -24,7 +24,6 @@ StartPageLogic::StartPageLogic(UiLogic *logic, QObject *parent): m_labelWaitInfoVisible{true}, m_labelWaitInfoText{}, m_pushButtonBackFromStartVisible{true}, - m_pushButtonConnectVisible{true}, m_ipAddressPortRegex{Utils::ipAddressPortRegExp()} { @@ -41,7 +40,6 @@ void StartPageLogic::onUpdatePage() set_labelWaitInfoVisible(false); set_labelWaitInfoText(""); - set_pushButtonConnectVisible(true); set_pushButtonConnectKeyChecked(false); diff --git a/client/ui/pages_logic/StartPageLogic.h b/client/ui/pages_logic/StartPageLogic.h index f5f132f8..24bdc53f 100644 --- a/client/ui/pages_logic/StartPageLogic.h +++ b/client/ui/pages_logic/StartPageLogic.h @@ -20,7 +20,6 @@ class StartPageLogic : public PageLogicBase AUTO_PROPERTY(bool, labelWaitInfoVisible) AUTO_PROPERTY(QString, labelWaitInfoText) AUTO_PROPERTY(bool, pushButtonBackFromStartVisible) - AUTO_PROPERTY(bool, pushButtonConnectVisible) READONLY_PROPERTY(QRegExp, ipAddressPortRegex) public: diff --git a/client/ui/qml/Pages/PageAppSetting.qml b/client/ui/qml/Pages/PageAppSetting.qml index b795ceb5..7e8d415a 100644 --- a/client/ui/qml/Pages/PageAppSetting.qml +++ b/client/ui/qml/Pages/PageAppSetting.qml @@ -107,7 +107,7 @@ PageBase { BlueButtonType { Layout.fillWidth: true - Layout.topMargin: 15 + Layout.topMargin: 10 Layout.preferredHeight: 41 text: qsTr("Export logs") onClicked: { @@ -117,7 +117,7 @@ PageBase { BlueButtonType { Layout.fillWidth: true - Layout.topMargin: 15 + Layout.topMargin: 10 Layout.preferredHeight: 41 property string start_text: qsTr("Clear logs") @@ -135,6 +135,31 @@ PageBase { AppSettingsLogic.onPushButtonClearLogsClicked() } } + + LabelType { + Layout.fillWidth: true + Layout.topMargin: 30 + text: qsTr("Backup and restore configuration") + } + + BlueButtonType { + Layout.fillWidth: true + Layout.topMargin: 10 + Layout.preferredHeight: 41 + text: qsTr("Backup app config") + onClicked: { + AppSettingsLogic.onPushButtonBackupAppConfigClicked() + } + } + BlueButtonType { + Layout.fillWidth: true + Layout.topMargin: 10 + Layout.preferredHeight: 41 + text: qsTr("Restore app config") + onClicked: { + AppSettingsLogic.onPushButtonRestoreAppConfigClicked() + } + } } } diff --git a/client/ui/qml/Pages/PageStart.qml b/client/ui/qml/Pages/PageStart.qml index 3ff11a86..ab53792d 100644 --- a/client/ui/qml/Pages/PageStart.qml +++ b/client/ui/qml/Pages/PageStart.qml @@ -119,7 +119,6 @@ PageBase { anchors.topMargin: 40 text: qsTr("Open file") - visible: StartPageLogic.pushButtonConnectVisible onClicked: { StartPageLogic.onPushButtonImportOpenFile() } @@ -133,7 +132,6 @@ PageBase { anchors.topMargin: 10 text: qsTr("Scan QR code") - visible: StartPageLogic.pushButtonConnectVisible onClicked: { if (Qt.platform.os == "ios") { UiLogic.goToPage(PageEnum.QrDecoderIos) @@ -144,7 +142,19 @@ PageBase { enabled: StartPageLogic.pushButtonConnectEnabled } + BlueButtonType { + id: btn_restore_cfg + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: qr_code_import.bottom + anchors.topMargin: 30 + visible: UiLogic.pagesStackDepth == 1 + enabled: StartPageLogic.pushButtonConnectEnabled + text: qsTr("Restore app config") + onClicked: { + AppSettingsLogic.onPushButtonRestoreAppConfigClicked() + } + } } @@ -270,7 +280,6 @@ PageBase { anchors.topMargin: 10 text: StartPageLogic.pushButtonConnectText - visible: StartPageLogic.pushButtonConnectVisible onClicked: { StartPageLogic.onPushButtonConnect() }