mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-05-08 14:33:23 +00:00
Compare commits
27 Commits
4.8.15.0
...
feat/file-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6f3c372c9d | ||
|
|
d5616615d3 | ||
|
|
3ddd38f58e | ||
|
|
50801dd559 | ||
|
|
465b3d4d95 | ||
|
|
23de0c7ed1 | ||
|
|
18b8f50d21 | ||
|
|
23dc5e7999 | ||
|
|
ae1bbb2f88 | ||
|
|
bfcb7bf979 | ||
|
|
dbce2f796c | ||
|
|
91307cf49a | ||
|
|
e044507b94 | ||
|
|
10de29217b | ||
|
|
f8c80c21c9 | ||
|
|
805d594608 | ||
|
|
452150bfff | ||
|
|
4c082654f9 | ||
|
|
d8bf7d4d1a | ||
|
|
4097af8d81 | ||
|
|
d1cd2a9d8d | ||
|
|
6ebb942466 | ||
|
|
ad5d60f915 | ||
|
|
9ed920b715 | ||
|
|
a868702be0 | ||
|
|
a9ab281221 | ||
|
|
fb9844cab8 |
3
client/images/controls/lock-locked.svg
Normal file
3
client/images/controls/lock-locked.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="10" height="11" viewBox="0 0 10 11" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2.5 5V3C2.5 2.33696 2.76339 1.70107 3.23223 1.23223C3.70107 0.763392 4.33696 0.5 5 0.5C5.66304 0.5 6.29893 0.763392 6.76777 1.23223C7.23661 1.70107 7.5 2.33696 7.5 3V5M1.5 5H8.5C9.05229 5 9.5 5.44772 9.5 6V9.5C9.5 10.0523 9.05229 10.5 8.5 10.5H1.5C0.947715 10.5 0.5 10.0523 0.5 9.5V6C0.5 5.44772 0.947715 5 1.5 5Z" stroke="#EB5757" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 494 B |
3
client/images/controls/lock-unlocked.svg
Normal file
3
client/images/controls/lock-unlocked.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="10" height="11" viewBox="0 0 10 11" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2.5 5.00252V3.00252C2.49938 2.38254 2.72914 1.78445 3.14469 1.32435C3.56023 0.864252 4.13191 0.574969 4.74875 0.512663C5.36559 0.450356 5.98357 0.61947 6.48274 0.987175C6.9819 1.35488 7.32663 1.89494 7.45 2.50252M1.5 5.00252H8.5C9.05229 5.00252 9.5 5.45023 9.5 6.00252V9.50252C9.5 10.0548 9.05229 10.5025 8.5 10.5025H1.5C0.947715 10.5025 0.5 10.0548 0.5 9.50252V6.00252C0.5 5.45023 0.947715 5.00252 1.5 5.00252Z" stroke="#5CAEE7" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 592 B |
@@ -32,6 +32,8 @@
|
||||
<file>images/controls/history.svg</file>
|
||||
<file>images/controls/home.svg</file>
|
||||
<file>images/controls/info.svg</file>
|
||||
<file>images/controls/lock-locked.svg</file>
|
||||
<file>images/controls/lock-unlocked.svg</file>
|
||||
<file>images/controls/mail.svg</file>
|
||||
<file>images/controls/map-pin.svg</file>
|
||||
<file>images/controls/more-vertical.svg</file>
|
||||
@@ -133,10 +135,13 @@
|
||||
<file>ui/qml/Components/HomeContainersListView.qml</file>
|
||||
<file>ui/qml/Components/HomeSplitTunnelingDrawer.qml</file>
|
||||
<file>ui/qml/Components/InstalledAppsDrawer.qml</file>
|
||||
<file>ui/qml/Components/PasswordDrawer.qml</file>
|
||||
<file>ui/qml/Components/QuestionDrawer.qml</file>
|
||||
<file>ui/qml/Components/SelectLanguageDrawer.qml</file>
|
||||
<file>ui/qml/Components/ServersListView.qml</file>
|
||||
<file>ui/qml/Components/SettingsContainersListView.qml</file>
|
||||
<file>ui/qml/Components/EncryptionIndicator.qml</file>
|
||||
|
||||
<file>ui/qml/Components/TransportProtoSelector.qml</file>
|
||||
<file>ui/qml/Components/AddSitePanel.qml</file>
|
||||
<file>ui/qml/Config/GlobalConfig.qml</file>
|
||||
@@ -208,6 +213,9 @@
|
||||
<file>ui/qml/Pages2/PageSettingsApiServerInfo.qml</file>
|
||||
<file>ui/qml/Pages2/PageSettingsApplication.qml</file>
|
||||
<file>ui/qml/Pages2/PageSettingsAppSplitTunneling.qml</file>
|
||||
<file>ui/qml/Pages2/PageSettingsAppEncryption.qml</file>
|
||||
<file>ui/qml/Pages2/PageSettingsAppPassword.qml</file>
|
||||
<file>ui/qml/Pages2/PageSettingsAppPasswordConfirm.qml</file>
|
||||
<file>ui/qml/Pages2/PageSettingsBackup.qml</file>
|
||||
<file>ui/qml/Pages2/PageSettingsConnection.qml</file>
|
||||
<file>ui/qml/Pages2/PageSettingsDns.qml</file>
|
||||
|
||||
@@ -44,7 +44,7 @@ private:
|
||||
QStringList encryptedKeys; // encode only key listed here
|
||||
// only this fields need for backup
|
||||
QStringList m_fieldsToBackup = {
|
||||
"Conf/", "Servers/",
|
||||
"Conf/", "Servers/", "Sec/",
|
||||
};
|
||||
|
||||
mutable QByteArray m_key;
|
||||
|
||||
@@ -94,6 +94,33 @@ public:
|
||||
m_settings.setValue("Conf/startMinimized", enabled);
|
||||
}
|
||||
|
||||
bool isFileEncryption() const
|
||||
{
|
||||
return m_settings.value("Sec/fileEncryption", false).toBool();
|
||||
}
|
||||
void setFileEncryption(bool enabled)
|
||||
{
|
||||
m_settings.setValue("Sec/fileEncryption", enabled);
|
||||
}
|
||||
|
||||
QString getPassword() const
|
||||
{
|
||||
return m_settings.value("Sec/password", "").toString();
|
||||
}
|
||||
void setPassword(const QString &pwd)
|
||||
{
|
||||
m_settings.setValue("Sec/password", pwd);
|
||||
}
|
||||
|
||||
QString getHint() const
|
||||
{
|
||||
return m_settings.value("Sec/hint", "").toString();
|
||||
}
|
||||
void setHint(const QString &hint)
|
||||
{
|
||||
m_settings.setValue("Sec/hint", hint);
|
||||
}
|
||||
|
||||
bool isNewsNotifications() const
|
||||
{
|
||||
return m_settings.value("Conf/newsNotifications", true).toBool();
|
||||
|
||||
@@ -325,6 +325,14 @@
|
||||
<translation>Выбрать всё</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>EncryptionIndicator</name>
|
||||
<message>
|
||||
<location filename="../ui/qml/Components/EncryptionIndicator.qml" line="55"/>
|
||||
<source> <a href="learnMore" style="text-decoration:none; color:%1">Learn more</a></source>
|
||||
<translation><a href="Подробнее" style="text-decoration:none; color:%1">Learn more</a></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>HomeContainersListView</name>
|
||||
<message>
|
||||
@@ -1871,67 +1879,72 @@ Thank you for staying with us!</source>
|
||||
<translation>Для настройки роутера или приложения AmneziaWG</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="73"/>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="70"/>
|
||||
<source>Encryption enabled.</source>
|
||||
<translation>Шифрование включено.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="83"/>
|
||||
<source>The configuration needs to be reissued</source>
|
||||
<translation>Необходимо заново скачать конфигурацию и добавить ее в приложение</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="135"/>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="145"/>
|
||||
<source> configuration file</source>
|
||||
<translation> файл конфигурации</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="149"/>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="159"/>
|
||||
<source>Generate a new configuration file</source>
|
||||
<translation>Создать новый файл конфигурации</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="150"/>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="160"/>
|
||||
<source>The previously created one will stop working</source>
|
||||
<translation>Ранее созданный файл перестанет работать</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="168"/>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="178"/>
|
||||
<source>Revoke the current configuration file</source>
|
||||
<translation>Отозвать текущий файл конфигурации</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="201"/>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="211"/>
|
||||
<source>Config file saved</source>
|
||||
<translation>Файл конфигурации сохранен</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="215"/>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="225"/>
|
||||
<source>The config has been revoked</source>
|
||||
<translation>Конфигурация была отозвана</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="222"/>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="232"/>
|
||||
<source>Generate a new %1 configuration file?</source>
|
||||
<translation>Создать новый %1 файл конфигурации?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="224"/>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="234"/>
|
||||
<source>Revoke the current %1 configuration file?</source>
|
||||
<translation>Отозвать текущий %1 файл конфигурации?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="227"/>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="237"/>
|
||||
<source>Your previous configuration file will no longer work, and it will not be possible to connect using it</source>
|
||||
<translation>Ваш предыдущий файл конфигурации не будет работать, и вы больше не сможете использовать его для подключения</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="228"/>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="238"/>
|
||||
<source>Download</source>
|
||||
<translation>Скачать</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="228"/>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="238"/>
|
||||
<source>Continue</source>
|
||||
<translation>Продолжить</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="229"/>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiNativeConfigs.qml" line="239"/>
|
||||
<source>Cancel</source>
|
||||
<translation>Отменить</translation>
|
||||
</message>
|
||||
@@ -2149,6 +2162,180 @@ Thank you for staying with us!</source>
|
||||
<translation>Скопировано</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PageSettingsAppEncryption</name>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAppEncryption.qml" line="27"/>
|
||||
<source>Encryption enabled</source>
|
||||
<translation>Шифрование включено</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAppEncryption.qml" line="27"/>
|
||||
<source>Encryption disabled</source>
|
||||
<translation>Шифрование выключено</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAppEncryption.qml" line="69"/>
|
||||
<source>Password & Encryption</source>
|
||||
<translation>Пароль и шифрование</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAppEncryption.qml" line="70"/>
|
||||
<source>Password protection for backups and configuration files.
|
||||
Required to restore or import encrypted files.</source>
|
||||
<translation>Пароль используется для защиты резервных копий и файлов конфигурации.
|
||||
Он потребуется при восстановлении или импорте зашифрованных файлов.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAppEncryption.qml" line="84"/>
|
||||
<source>Learn more</source>
|
||||
<translation>Подробнее</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAppEncryption.qml" line="94"/>
|
||||
<source>Password set. Encryption enabled</source>
|
||||
<translation>Пароль задан. Шифрование включено</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAppEncryption.qml" line="107"/>
|
||||
<source>Disable encryption</source>
|
||||
<translation>Выключить шифрование</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAppEncryption.qml" line="174"/>
|
||||
<source>If the password is forgotten, it can be recovered. To reset the password, <a href="appSettings" style="text-decoration:none; color:%1;">settings must be reset</a>.
|
||||
Encrypted files can only be opened with password used to encrypt them</source>
|
||||
<translation>Если забудете пароль, восстановить его нельзя. Чтобы сбросить пароль, придется <a href="appSettings" style="text-decoration:none; color:%1;">сбросить все настройки</a>.
|
||||
Зашифрованные файлы открываются только тем паролем, которым были зашифрованы</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAppEncryption.qml" line="134"/>
|
||||
<source>Change password</source>
|
||||
<translation>Измененить пароль</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PageSettingsAppPassword</name>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAppPassword.qml" line="65"/>
|
||||
<source>Password & Encryption</source>
|
||||
<translation>Пароль и шифрование</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAppPassword.qml" line="65"/>
|
||||
<source>Change password</source>
|
||||
<translation>Изменение пароля</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAppPassword.qml" line="66"/>
|
||||
<source>Existing encrypted files will still require the old password.
|
||||
The new password will be used for new encrypted files.</source>
|
||||
<translation>Ранее зашифрованные файлы по-прежнему будут требовать старый пароль.
|
||||
Новый пароль будет использоваться для новых зашифрованных файлов</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAppPassword.qml" line="67"/>
|
||||
<source>Password protection for backups and configuration files.
|
||||
Required to restore or import encrypted files.</source>
|
||||
<translation>Пароль используется для защиты резервных копий и файлов конфигурации.
|
||||
Он потребуется при восстановлении или импорте зашифрованных файлов.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAppPassword.qml" line="81"/>
|
||||
<source>Learn more</source>
|
||||
<translation>Подробнее</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAppPassword.qml" line="93"/>
|
||||
<source>Password not set. Encryption disabled</source>
|
||||
<translation>Пароль не задан. Шифрование выключено</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAppPassword.qml" line="144"/>
|
||||
<source>Continue</source>
|
||||
<translation>Продолжить</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAppPassword.qml" line="171"/>
|
||||
<source>Password cannot be empty</source>
|
||||
<translation>Пароль не может быть пустым</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAppPassword.qml" line="174"/>
|
||||
<source>Password too short</source>
|
||||
<translation>Пароль слишком короткий</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAppPassword.qml" line="189"/>
|
||||
<source>New password</source>
|
||||
<translation>Новый пароль</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAppPassword.qml" line="189"/>
|
||||
<source>Set encryption password</source>
|
||||
<translation>Придумайте пароль шифрования</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAppPassword.qml" line="202"/>
|
||||
<source>New password hint (optional)</source>
|
||||
<translation>Новая подсказка (необязательно)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAppPassword.qml" line="202"/>
|
||||
<source>Password hint</source>
|
||||
<translation>Подсказка</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PageSettingsAppPasswordConfirm</name>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAppPasswordConfirm.qml" line="57"/>
|
||||
<source>Confirm new password</source>
|
||||
<translation>Введите новый пароль ещё раз</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAppPasswordConfirm.qml" line="57"/>
|
||||
<source>Confirm password</source>
|
||||
<translation>Введите пароль ещё раз</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAppPasswordConfirm.qml" line="58"/>
|
||||
<source></source>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAppPasswordConfirm.qml" line="58"/>
|
||||
<source>If the password is forgotten, it cant be recovered. To reset the password, the app settings must be reset.
|
||||
Encrypted files can only be opened with the password used to encrypt them</source>
|
||||
<translation>Если забудете пароль, восстановить его нельзя. Чтобы сбросить пароль, придется сбросить все настройки.
|
||||
Зашифрованные файлы открываются только тем паролем, которым были зашифрованы</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAppPasswordConfirm.qml" line="80"/>
|
||||
<source>Re-enter new password</source>
|
||||
<translation>Введите новый пароль ещё раз</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAppPasswordConfirm.qml" line="80"/>
|
||||
<source>Re-enter password</source>
|
||||
<translation>Введите пароль ещё раз</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAppPasswordConfirm.qml" line="122"/>
|
||||
<source>Save new password</source>
|
||||
<translation>Сохранить новый пароль</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAppPasswordConfirm.qml" line="122"/>
|
||||
<source>Turn on encryption</source>
|
||||
<translation>Включить шифрование</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAppPasswordConfirm.qml" line="149"/>
|
||||
<source>Passwords not match</source>
|
||||
<translation>Пароли не совпадают</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PageSettingsAppSplitTunneling</name>
|
||||
<message>
|
||||
@@ -2343,63 +2530,68 @@ Thank you for staying with us!</source>
|
||||
<translation>Вы можете сохранить настройки в файл резервной копии, чтобы восстановить их при следующей установке приложения.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsBackup.qml" line="90"/>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsBackup.qml" line="81"/>
|
||||
<source>Encryption enabled.</source>
|
||||
<translation>Шифрование включено.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsBackup.qml" line="100"/>
|
||||
<source>The backup will contain your passwords and private keys for all servers added to AmneziaVPN. Keep this information in a secure place.</source>
|
||||
<translation>Резервная копия будет содержать ваши пароли и закрытые ключи для всех серверов, добавленных в AmneziaVPN. Храните эту информацию в надежном месте.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsBackup.qml" line="104"/>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsBackup.qml" line="114"/>
|
||||
<source>Make a backup</source>
|
||||
<translation>Создать резервную копию</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsBackup.qml" line="111"/>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsBackup.qml" line="121"/>
|
||||
<source>Save backup file</source>
|
||||
<translation>Сохранить резервную копию</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsBackup.qml" line="112"/>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsBackup.qml" line="145"/>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsBackup.qml" line="122"/>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsBackup.qml" line="155"/>
|
||||
<source>Backup files (*.backup)</source>
|
||||
<translation>Файлы резервных копий (*.backup)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsBackup.qml" line="121"/>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsBackup.qml" line="131"/>
|
||||
<source>Backup file saved</source>
|
||||
<translation>Резервная копия сохранена</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsBackup.qml" line="141"/>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsBackup.qml" line="151"/>
|
||||
<source>Restore from backup</source>
|
||||
<translation>Восстановить из резервной копии</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsBackup.qml" line="144"/>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsBackup.qml" line="154"/>
|
||||
<source>Open backup file</source>
|
||||
<translation>Открыть резервную копию</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsBackup.qml" line="155"/>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsBackup.qml" line="179"/>
|
||||
<source>Import settings from a backup file?</source>
|
||||
<translation>Импортировать настройки из резервной копии?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsBackup.qml" line="156"/>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsBackup.qml" line="180"/>
|
||||
<source>All current settings will be reset</source>
|
||||
<translation>Все текущие настройки будут сброшены</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsBackup.qml" line="157"/>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsBackup.qml" line="181"/>
|
||||
<source>Continue</source>
|
||||
<translation>Продолжить</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsBackup.qml" line="158"/>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsBackup.qml" line="182"/>
|
||||
<source>Cancel</source>
|
||||
<translation>Отменить</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsBackup.qml" line="162"/>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsBackup.qml" line="186"/>
|
||||
<source>Cannot restore backup settings during active connection</source>
|
||||
<translation>Невозможно восстановить настройки из резервной копии во время активного соединения</translation>
|
||||
</message>
|
||||
@@ -2681,11 +2873,6 @@ Thank you for staying with us!</source>
|
||||
<source>Logging</source>
|
||||
<translation>Логирование</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsLogging.qml" line="51"/>
|
||||
<source>Enabling this function will save application's logs automatically. By default, logging functionality is disabled. Enable log saving in case of application malfunction.</source>
|
||||
<translation>Включение этой функции позволяет сохранять логи на вашем устройстве. По умолчанию она отключена. Включите сохранение логов в случае сбоев в работе приложения.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsLogging.qml" line="192"/>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsLogging.qml" line="218"/>
|
||||
@@ -2704,10 +2891,20 @@ Thank you for staying with us!</source>
|
||||
<source>Logs file saved</source>
|
||||
<translation>Файл с логами сохранен</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsLogging.qml" line="51"/>
|
||||
<source>Logs help diagnose app errors and connection issuesLogging is disabled by default. Enable it when troubleshooting or if requested by support</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsLogging.qml" line="63"/>
|
||||
<source>Enable logs</source>
|
||||
<translation>Включить запись логов</translation>
|
||||
<source>Enable logging</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsLogging.qml" line="80"/>
|
||||
<source>Delete all logs</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsLogging.qml" line="85"/>
|
||||
@@ -2729,10 +2926,15 @@ Thank you for staying with us!</source>
|
||||
<source>Logs have been cleaned up</source>
|
||||
<translation>Логи очищены</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsLogging.qml" line="157"/>
|
||||
<source>Save logs to file</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsLogging.qml" line="181"/>
|
||||
<source>Client logs</source>
|
||||
<translation>Логи приложения</translation>
|
||||
<source>App logs</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsLogging.qml" line="182"/>
|
||||
@@ -2744,11 +2946,6 @@ Thank you for staying with us!</source>
|
||||
<source>Open logs folder</source>
|
||||
<translation>Открыть папку с логами</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsLogging.qml" line="157"/>
|
||||
<source>Export logs</source>
|
||||
<translation>Сохранить логи</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsLogging.qml" line="210"/>
|
||||
<source>Service logs</source>
|
||||
@@ -2759,10 +2956,13 @@ Thank you for staying with us!</source>
|
||||
<source>AmneziaVPN-service logs</source>
|
||||
<translation>AmneziaVPN-service logs</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PageSettingsNewsNotifications</name>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsLogging.qml" line="80"/>
|
||||
<source>Clear logs</source>
|
||||
<translation>Очистить логи</translation>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsNewsNotifications.qml" line="33"/>
|
||||
<source>News & Notifications</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@@ -3179,77 +3379,77 @@ Thank you for staying with us!</source>
|
||||
<translation>Файл с настройками подключения</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="48"/>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="49"/>
|
||||
<source>Connection</source>
|
||||
<translation>Соединение</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="75"/>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="76"/>
|
||||
<source>Settings</source>
|
||||
<translation>Настройки</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="85"/>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="86"/>
|
||||
<source>Enable logs</source>
|
||||
<translation>Включить запись логов</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="99"/>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="100"/>
|
||||
<source>Export client logs</source>
|
||||
<translation>Экспорт логов клиента</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="109"/>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="110"/>
|
||||
<source>Save</source>
|
||||
<translation>Сохранить</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="110"/>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="111"/>
|
||||
<source>Logs files (*.log)</source>
|
||||
<translation>Файлы логов (*.log)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="119"/>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="120"/>
|
||||
<source>Logs file saved</source>
|
||||
<translation>Файл с логами сохранен</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="129"/>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="130"/>
|
||||
<source>Support tag</source>
|
||||
<translation>Support tag</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="140"/>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="141"/>
|
||||
<source>Copied</source>
|
||||
<translation>Скопировано</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="159"/>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="160"/>
|
||||
<source>Insert the key, add a configuration file or scan the QR-code</source>
|
||||
<translation>Вставьте ключ, добавьте файл конфигурации или отсканируйте QR-код</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="169"/>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="170"/>
|
||||
<source>Insert key</source>
|
||||
<translation>Вставьте ключ</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="170"/>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="171"/>
|
||||
<source>Insert</source>
|
||||
<translation>Вставить</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="188"/>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="189"/>
|
||||
<source>Continue</source>
|
||||
<translation>Продолжить</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="205"/>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="206"/>
|
||||
<source>Other connection options</source>
|
||||
<translation>Другие варианты подключения</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="253"/>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="254"/>
|
||||
<source>Site Amnezia</source>
|
||||
<translation>Сайт Amnezia</translation>
|
||||
</message>
|
||||
@@ -3279,11 +3479,16 @@ Thank you for staying with us!</source>
|
||||
<translation>Настроить VPN на собственном сервере</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="315"/>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="306"/>
|
||||
<source>Restore from backup</source>
|
||||
<translation>Восстановить из резервной копии</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="316"/>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="339"/>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="363"/>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="378"/>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="307"/>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="325"/>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="344"/>
|
||||
@@ -3293,21 +3498,25 @@ Thank you for staying with us!</source>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="320"/>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="311"/>
|
||||
<source>Open backup file</source>
|
||||
<translation>Открыть резервную копию</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="321"/>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="312"/>
|
||||
<source>Backup files (*.backup)</source>
|
||||
<translation>Файлы резервных копий (*.backup)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="345"/>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="331"/>
|
||||
<source>Open config file</source>
|
||||
<translation>Открыть файл с конфигурацией</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="362"/>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardConfigSource.qml" line="343"/>
|
||||
<source>QR code</source>
|
||||
<translation>QR-код</translation>
|
||||
@@ -3600,8 +3809,13 @@ Thank you for staying with us!</source>
|
||||
<translation>Соединение</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="323"/>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="324"/>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="303"/>
|
||||
<source>Encryption enabled.</source>
|
||||
<translation>Шифрование включено.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="333"/>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="334"/>
|
||||
<source>Server</source>
|
||||
<translation>Сервер</translation>
|
||||
</message>
|
||||
@@ -3702,7 +3916,7 @@ Thank you for staying with us!</source>
|
||||
<translation>Пользователи</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="304"/>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="314"/>
|
||||
<source>User name</source>
|
||||
<translation>Имя пользователя</translation>
|
||||
</message>
|
||||
@@ -3859,28 +4073,33 @@ Thank you for staying with us!</source>
|
||||
<translation>Если вы поделитесь полным доступом с другими людьми, то они смогут удалять и добавлять протоколы и сервисы на сервер, что приведет к некорректной работе VPN для всех пользователей. </translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="87"/>
|
||||
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="88"/>
|
||||
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="78"/>
|
||||
<source>Encryption enabled.</source>
|
||||
<translation>Шифрование включено.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="97"/>
|
||||
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="98"/>
|
||||
<source>Server</source>
|
||||
<translation>Сервер</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="115"/>
|
||||
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="125"/>
|
||||
<source>Accessing </source>
|
||||
<translation>Доступ </translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="116"/>
|
||||
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="126"/>
|
||||
<source>File with accessing settings to </source>
|
||||
<translation>Файл с настройками доступа к </translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="147"/>
|
||||
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="157"/>
|
||||
<source>Share</source>
|
||||
<translation>Поделиться</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="155"/>
|
||||
<location filename="../ui/qml/Pages2/PageShareFullAccess.qml" line="165"/>
|
||||
<source>Access error!</source>
|
||||
<translation>Ошибка доступа!</translation>
|
||||
</message>
|
||||
@@ -3903,6 +4122,30 @@ Thank you for staying with us!</source>
|
||||
<translation>Логирование включено. Обратите внимание, что через 14 дней оно будет автоматически отключено, а все файлы логов будут удалены.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PasswordDrawer</name>
|
||||
<message>
|
||||
<location filename="../ui/qml/Components/PasswordDrawer.qml" line="56"/>
|
||||
<source>Password required</source>
|
||||
<translation>Для продолжения введите пароль</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Components/PasswordDrawer.qml" line="74"/>
|
||||
<source>Password</source>
|
||||
<translation>Пароль</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Components/PasswordDrawer.qml" line="107"/>
|
||||
<source>Done</source>
|
||||
<translation>Готово</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Components/PasswordDrawer.qml" line="112"/>
|
||||
<location filename="../ui/qml/Components/PasswordDrawer.qml" line="117"/>
|
||||
<source>Invalid password</source>
|
||||
<translation>Неверный пароль</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PopupType</name>
|
||||
<message>
|
||||
|
||||
@@ -305,6 +305,8 @@ bool ApiConfigsController::exportNativeConfig(const QString &serverCountryCode,
|
||||
nativeConfig.replace("$WIREGUARD_CLIENT_PRIVATE_KEY", protocolData.wireGuardClientPrivKey);
|
||||
|
||||
SystemController::saveFile(fileName, nativeConfig);
|
||||
if (m_settings->isFileEncryption())
|
||||
SystemController::encryptFile(fileName, m_settings->getPassword(), m_settings->getHint());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -346,6 +346,8 @@ QList<QString> ExportController::getQrCodes()
|
||||
void ExportController::exportConfig(const QString &fileName)
|
||||
{
|
||||
SystemController::saveFile(fileName, m_config);
|
||||
if (m_settings->isFileEncryption())
|
||||
SystemController::encryptFile(fileName, m_settings->getPassword(), m_settings->getHint());
|
||||
}
|
||||
|
||||
void ExportController::updateClientManagementModel(const DockerContainer container, ServerCredentials credentials)
|
||||
|
||||
@@ -34,6 +34,9 @@ namespace PageLoader
|
||||
PageSettingsSplitTunneling,
|
||||
PageSettingsAppSplitTunneling,
|
||||
PageSettingsKillSwitch,
|
||||
PageSettingsAppEncryption,
|
||||
PageSettingsAppPassword,
|
||||
PageSettingsAppPasswordConfirm,
|
||||
PageSettingsApiServerInfo,
|
||||
PageSettingsApiAvailableCountries,
|
||||
PageSettingsApiSupport,
|
||||
|
||||
@@ -174,6 +174,8 @@ void SettingsController::backupAppConfig(const QString &fileName)
|
||||
config["Conf/useAmneziaDns"] = isAmneziaDnsEnabled();
|
||||
|
||||
SystemController::saveFile(fileName, QJsonDocument(config).toJson());
|
||||
if (m_settings->isFileEncryption())
|
||||
SystemController::encryptFile(fileName, m_settings->getPassword(), m_settings->getHint());
|
||||
}
|
||||
|
||||
void SettingsController::restoreAppConfig(const QString &fileName)
|
||||
@@ -307,6 +309,57 @@ void SettingsController::toggleStartMinimized(bool enable)
|
||||
emit startMinimizedChanged();
|
||||
}
|
||||
|
||||
bool SettingsController::isFileEncryptionEnabled()
|
||||
{
|
||||
return m_settings->isFileEncryption();
|
||||
}
|
||||
|
||||
void SettingsController::toggleFileEncryption(bool enable)
|
||||
{
|
||||
m_settings->setFileEncryption(enable);
|
||||
emit fileEncryptionStateChanged();
|
||||
}
|
||||
|
||||
void SettingsController::setPassword(QString pwd)
|
||||
{
|
||||
m_settings->setPassword(pwd);
|
||||
}
|
||||
|
||||
QString SettingsController::getPassword()
|
||||
{
|
||||
return m_settings->getPassword();
|
||||
}
|
||||
|
||||
void SettingsController::setHint(QString hint)
|
||||
{
|
||||
m_settings->setHint(hint);
|
||||
}
|
||||
|
||||
QString SettingsController::getHint()
|
||||
{
|
||||
return m_settings->getHint();
|
||||
}
|
||||
|
||||
void SettingsController::setTempPassword(QString pwd)
|
||||
{
|
||||
tempPassword = pwd;
|
||||
}
|
||||
|
||||
QString SettingsController::getTempPassword()
|
||||
{
|
||||
return tempPassword;
|
||||
}
|
||||
|
||||
void SettingsController::setTempHint(QString hint)
|
||||
{
|
||||
tempHint = hint;
|
||||
}
|
||||
|
||||
QString SettingsController::getTempHint()
|
||||
{
|
||||
return tempHint;
|
||||
}
|
||||
|
||||
bool SettingsController::isNewsNotificationsEnabled()
|
||||
{
|
||||
return m_settings->isNewsNotifications();
|
||||
|
||||
@@ -73,6 +73,19 @@ public slots:
|
||||
bool isStartMinimizedEnabled();
|
||||
void toggleStartMinimized(bool enable);
|
||||
|
||||
bool isFileEncryptionEnabled();
|
||||
void toggleFileEncryption(bool enable);
|
||||
|
||||
void setPassword(QString pwd);
|
||||
QString getPassword();
|
||||
void setHint(QString hint);
|
||||
QString getHint();
|
||||
|
||||
void setTempPassword(QString pwd);
|
||||
QString getTempPassword();
|
||||
void setTempHint(QString hint);
|
||||
QString getTempHint();
|
||||
|
||||
bool isNewsNotificationsEnabled();
|
||||
void toggleNewsNotificationsEnabled(bool enable);
|
||||
|
||||
@@ -144,6 +157,9 @@ signals:
|
||||
void isHomeAdLabelVisibleChanged(bool visible);
|
||||
void startMinimizedChanged();
|
||||
|
||||
void fileEncryptionStateChanged();
|
||||
void changingPassword();
|
||||
|
||||
private:
|
||||
QSharedPointer<ServersModel> m_serversModel;
|
||||
QSharedPointer<ContainersModel> m_containersModel;
|
||||
@@ -162,6 +178,9 @@ private:
|
||||
|
||||
QString getPlatform();
|
||||
|
||||
QString tempPassword;
|
||||
QString tempHint;
|
||||
|
||||
QDateTime m_loggingDisableDate;
|
||||
|
||||
bool m_isDevModeEnabled = false;
|
||||
|
||||
@@ -10,6 +10,21 @@
|
||||
#include <QUrl>
|
||||
#include <QtConcurrent>
|
||||
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/rand.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr int SALT_LEN = 16;
|
||||
constexpr int IV_LEN = 12;
|
||||
constexpr int KEY_LEN = 32;
|
||||
constexpr int TAG_LEN = 16;
|
||||
constexpr int PBKDF2_ITER = 100000;
|
||||
|
||||
const QByteArray magicString { "EncData" };
|
||||
}
|
||||
|
||||
#ifdef Q_OS_ANDROID
|
||||
#include "platforms/android/android_controller.h"
|
||||
#endif
|
||||
@@ -89,6 +104,313 @@ bool SystemController::readFile(const QString &fileName, QString &data)
|
||||
return true;
|
||||
}
|
||||
|
||||
static QString opensslErrString()
|
||||
{
|
||||
unsigned long e = ERR_get_error();
|
||||
if (!e)
|
||||
return QStringLiteral("Unknown OpenSSL error");
|
||||
char buf[256];
|
||||
ERR_error_string_n(e, buf, sizeof(buf));
|
||||
return QString::fromUtf8(buf);
|
||||
}
|
||||
|
||||
static bool deriveKey(const QByteArray &password, const QByteArray &salt, QByteArray &outKey)
|
||||
{
|
||||
outKey.resize(KEY_LEN);
|
||||
const unsigned char *pw = reinterpret_cast<const unsigned char *>(password.constData());
|
||||
const unsigned char *s = reinterpret_cast<const unsigned char *>(salt.constData());
|
||||
int ok = PKCS5_PBKDF2_HMAC(reinterpret_cast<const char *>(pw), password.size(), s, salt.size(), PBKDF2_ITER,
|
||||
EVP_sha256(), KEY_LEN, reinterpret_cast<unsigned char *>(outKey.data()));
|
||||
if (!ok) {
|
||||
qDebug() << opensslErrString();
|
||||
}
|
||||
return ok == 1;
|
||||
}
|
||||
|
||||
static bool aesCrypt(const QByteArray &in, const QByteArray &key, const QByteArray &iv, QByteArray &out,
|
||||
QByteArray &tag, bool encrypt)
|
||||
{
|
||||
std::unique_ptr<EVP_CIPHER_CTX, void (*)(EVP_CIPHER_CTX *)> ctx { EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free };
|
||||
|
||||
if (!ctx) {
|
||||
qDebug() << "EVP_CIPHER_CTX_new failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
const EVP_CIPHER *cipher = EVP_aes_256_gcm();
|
||||
|
||||
if (1 != EVP_CipherInit_ex(ctx.get(), cipher, nullptr, nullptr, nullptr, encrypt ? 1 : 0)) {
|
||||
qDebug() << opensslErrString();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (1 != EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_IVLEN, iv.size(), nullptr)) {
|
||||
qDebug() << opensslErrString();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (1 != EVP_CipherInit_ex(ctx.get(), nullptr, nullptr, reinterpret_cast<const unsigned char *>(key.constData()),
|
||||
reinterpret_cast<const unsigned char *>(iv.constData()), -1)) {
|
||||
qDebug() << opensslErrString();
|
||||
return false;
|
||||
}
|
||||
|
||||
out.clear();
|
||||
out.resize(in.size());
|
||||
|
||||
int outlen = 0;
|
||||
|
||||
if (1 != EVP_CipherUpdate(ctx.get(), reinterpret_cast<unsigned char *>(out.data()), &outlen,
|
||||
reinterpret_cast<const unsigned char *>(in.constData()), in.size())) {
|
||||
qDebug() << opensslErrString();
|
||||
return false;
|
||||
}
|
||||
|
||||
int tmplen = 0;
|
||||
|
||||
if (encrypt) {
|
||||
if (1 != EVP_CipherFinal_ex(ctx.get(), reinterpret_cast<unsigned char *>(out.data()) + outlen, &tmplen)) {
|
||||
qDebug() << opensslErrString();
|
||||
return false;
|
||||
}
|
||||
|
||||
out.resize(outlen + tmplen);
|
||||
|
||||
tag.resize(TAG_LEN);
|
||||
if (1 != EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_GET_TAG, TAG_LEN, tag.data())) {
|
||||
qDebug() << opensslErrString();
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (1 != EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_TAG, tag.size(), const_cast<char *>(tag.constData()))) {
|
||||
qDebug() << opensslErrString();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (1 != EVP_CipherFinal_ex(ctx.get(), reinterpret_cast<unsigned char *>(out.data()) + outlen, &tmplen)) {
|
||||
qDebug() << "Authentication failed: " << opensslErrString();
|
||||
return false;
|
||||
}
|
||||
|
||||
out.resize(outlen + tmplen);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SystemController::encryptFile(const QString &filePath, const QString &password, const QString &hint)
|
||||
{
|
||||
QFile f(filePath);
|
||||
if (!f.open(QIODevice::ReadOnly)) {
|
||||
qDebug() << "Cannot open file for read: " << f.errorString();
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray content = f.readAll();
|
||||
f.close();
|
||||
|
||||
if (content.startsWith(magicString)) {
|
||||
qDebug() << "File already encrypted";
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray salt(SALT_LEN, 0);
|
||||
QByteArray iv(IV_LEN, 0);
|
||||
QByteArray key;
|
||||
QByteArray cipher;
|
||||
QByteArray tag;
|
||||
|
||||
if (1 != RAND_bytes(reinterpret_cast<unsigned char *>(salt.data()), SALT_LEN)
|
||||
|| 1 != RAND_bytes(reinterpret_cast<unsigned char *>(iv.data()), IV_LEN)) {
|
||||
qDebug() << opensslErrString();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!deriveKey(password.toUtf8(), salt, key))
|
||||
return false;
|
||||
|
||||
if (!aesCrypt(content, key, iv, cipher, tag, true))
|
||||
return false;
|
||||
|
||||
QByteArray out;
|
||||
QByteArray hintBytes = hint.toUtf8();
|
||||
quint32 hintLen = static_cast<quint32>(hintBytes.size());
|
||||
|
||||
out += magicString;
|
||||
out.append(reinterpret_cast<const char *>(&hintLen), sizeof(hintLen));
|
||||
out += hintBytes;
|
||||
out += salt;
|
||||
out += iv;
|
||||
out += tag;
|
||||
out += cipher;
|
||||
|
||||
if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
|
||||
qDebug() << "Cannot open file for write: " << f.errorString();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (f.write(out) != out.size()) {
|
||||
qDebug() << "Write failed";
|
||||
f.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
f.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
QByteArray SystemController::getDecryptedData(const QString &filePath, const QString &password)
|
||||
{
|
||||
QFile f(filePath);
|
||||
if (!f.open(QIODevice::ReadOnly)) {
|
||||
qDebug() << "Cannot open file: " << f.errorString();
|
||||
return {};
|
||||
}
|
||||
|
||||
QByteArray content = f.readAll();
|
||||
f.close();
|
||||
|
||||
if (!content.startsWith(magicString)) {
|
||||
qDebug() << "Invalid file format (magic missing)";
|
||||
return {};
|
||||
}
|
||||
|
||||
int pos = magicString.size();
|
||||
|
||||
if (content.size() < pos + static_cast<int>(sizeof(quint32))) {
|
||||
qDebug() << "Corrupted file (no hint length)";
|
||||
return {};
|
||||
}
|
||||
|
||||
quint32 hintLen = 0;
|
||||
memcpy(&hintLen, content.constData() + pos, sizeof(quint32));
|
||||
pos += sizeof(quint32);
|
||||
|
||||
if (content.size() < pos + static_cast<int>(hintLen) + SALT_LEN + IV_LEN + TAG_LEN) {
|
||||
qDebug() << "Corrupted file (invalid sizes)";
|
||||
return {};
|
||||
}
|
||||
|
||||
pos += hintLen;
|
||||
|
||||
QByteArray salt = content.mid(pos, SALT_LEN);
|
||||
pos += SALT_LEN;
|
||||
|
||||
QByteArray iv = content.mid(pos, IV_LEN);
|
||||
pos += IV_LEN;
|
||||
|
||||
QByteArray tag = content.mid(pos, TAG_LEN);
|
||||
pos += TAG_LEN;
|
||||
|
||||
QByteArray cipher = content.mid(pos);
|
||||
|
||||
QByteArray key;
|
||||
if (!deriveKey(password.toUtf8(), salt, key)) {
|
||||
qDebug() << "Key derivation failed";
|
||||
return {};
|
||||
}
|
||||
|
||||
QByteArray plain;
|
||||
if (!aesCrypt(cipher, key, iv, plain, tag, false)) {
|
||||
qDebug() << "Decryption failed (wrong password or corrupted data)";
|
||||
return {};
|
||||
}
|
||||
|
||||
return plain;
|
||||
}
|
||||
|
||||
bool SystemController::isFileEncrypted(const QString &filePath)
|
||||
{
|
||||
QFile f(filePath);
|
||||
if (!f.open(QIODevice::ReadOnly)) {
|
||||
qDebug() << "Cannot open file for read: %1", f.errorString();
|
||||
return false;
|
||||
}
|
||||
QByteArray data = f.readAll();
|
||||
f.close();
|
||||
|
||||
if (!data.startsWith(magicString)) {
|
||||
qDebug() << "File is not recognized as encrypted (magic missing)";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SystemController::isPasswordValid(const QString &filePath, const QString &password)
|
||||
{
|
||||
QFile f(filePath);
|
||||
if (!f.open(QIODevice::ReadOnly)) {
|
||||
qDebug() << f.errorString();
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray content = f.readAll();
|
||||
f.close();
|
||||
|
||||
if (!content.startsWith(magicString))
|
||||
return false;
|
||||
|
||||
int pos = magicString.size();
|
||||
|
||||
quint32 hintLen = 0;
|
||||
memcpy(&hintLen, content.constData() + pos, sizeof(quint32));
|
||||
pos += sizeof(quint32);
|
||||
pos += hintLen;
|
||||
|
||||
QByteArray salt = content.mid(pos, SALT_LEN);
|
||||
pos += SALT_LEN;
|
||||
QByteArray iv = content.mid(pos, IV_LEN);
|
||||
pos += IV_LEN;
|
||||
QByteArray tag = content.mid(pos, TAG_LEN);
|
||||
pos += TAG_LEN;
|
||||
QByteArray cipher = content.mid(pos);
|
||||
|
||||
QByteArray key;
|
||||
if (!deriveKey(password.toUtf8(), salt, key))
|
||||
return false;
|
||||
|
||||
QByteArray plain;
|
||||
bool ok = aesCrypt(cipher, key, iv, plain, tag, false);
|
||||
|
||||
if (!ok)
|
||||
qDebug() << "Wrong password";
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
QString SystemController::readHint(const QString &filePath)
|
||||
{
|
||||
if (filePath.isEmpty())
|
||||
return "";
|
||||
|
||||
QByteArray data;
|
||||
readFile(filePath, data);
|
||||
|
||||
if (!data.startsWith(magicString)) {
|
||||
qDebug() << "Not an encrypted file";
|
||||
return {};
|
||||
}
|
||||
|
||||
int pos = magicString.size();
|
||||
|
||||
if (data.size() < pos + static_cast<int>(sizeof(quint32))) {
|
||||
qDebug() << "Corrupted file (no hint length)";
|
||||
return {};
|
||||
}
|
||||
|
||||
quint32 hintLen = 0;
|
||||
memcpy(&hintLen, data.constData() + pos, sizeof(quint32));
|
||||
pos += sizeof(quint32);
|
||||
|
||||
if (data.size() < pos + static_cast<int>(hintLen)) {
|
||||
qDebug() << "Corrupted file (hint truncated)";
|
||||
return {};
|
||||
}
|
||||
|
||||
return QString::fromUtf8(data.constData() + pos, hintLen);
|
||||
}
|
||||
|
||||
QString SystemController::getFileName(const QString &acceptLabel, const QString &nameFilter,
|
||||
const QString &selectedFile, const bool isSaveMode, const QString &defaultSuffix)
|
||||
{
|
||||
|
||||
@@ -15,10 +15,18 @@ public:
|
||||
static bool readFile(const QString &fileName, QByteArray &data);
|
||||
static bool readFile(const QString &fileName, QString &data);
|
||||
|
||||
static bool encryptFile(const QString &filePath, const QString &password, const QString &hint);
|
||||
|
||||
public slots:
|
||||
QString getFileName(const QString &acceptLabel, const QString &nameFilter, const QString &selectedFile = "",
|
||||
const bool isSaveMode = false, const QString &defaultSuffix = "");
|
||||
|
||||
QByteArray getDecryptedData(const QString &filePath, const QString &password);
|
||||
|
||||
bool isFileEncrypted(const QString &filePath);
|
||||
bool isPasswordValid(const QString &filePath, const QString &password);
|
||||
QString readHint(const QString &filePath);
|
||||
|
||||
void setQmlRoot(QObject *qmlRoot);
|
||||
|
||||
bool isAuthenticated();
|
||||
|
||||
86
client/ui/qml/Components/EncryptionIndicator.qml
Normal file
86
client/ui/qml/Components/EncryptionIndicator.qml
Normal file
@@ -0,0 +1,86 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
|
||||
import Style 1.0
|
||||
|
||||
import "../Controls2"
|
||||
import "../Controls2/TextTypes"
|
||||
import "../Config"
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property bool linkEnabled: false
|
||||
|
||||
property string textColor: AmneziaStyle.color.paleGray
|
||||
|
||||
property string textString
|
||||
property int textFormat: Text.PlainText
|
||||
|
||||
property string iconPath
|
||||
property real iconWidth: 16
|
||||
property real iconHeight: 16
|
||||
|
||||
color: AmneziaStyle.color.onyxBlack
|
||||
radius: 32
|
||||
implicitHeight: iconHeight + 8
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
RowLayout {
|
||||
id: content
|
||||
anchors.centerIn: parent
|
||||
|
||||
spacing: 0
|
||||
|
||||
Image {
|
||||
width: root.iconWidth
|
||||
height: root.iconHeight
|
||||
|
||||
source: root.iconPath
|
||||
}
|
||||
|
||||
CaptionTextType {
|
||||
id: supportingText
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 8
|
||||
|
||||
text: root.textString
|
||||
textFormat: Text.RichText
|
||||
color: root.textColor
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
visible: root.linkEnabled
|
||||
|
||||
hoverEnabled: false
|
||||
|
||||
implicitHeight: root.iconHeight
|
||||
implicitWidth: linkText.width/2 + 8
|
||||
|
||||
defaultColor: AmneziaStyle.color.transparent
|
||||
|
||||
CaptionTextType {
|
||||
id: linkText
|
||||
|
||||
leftPadding: 4
|
||||
|
||||
width: linkText.text.length * linkText.font.pixelSize
|
||||
|
||||
text: qsTr("Learn more")
|
||||
textFormat: Text.RichText
|
||||
color: AmneziaStyle.color.goldenApricot
|
||||
}
|
||||
|
||||
clickedFunc: function() {
|
||||
Qt.openUrlExternally("https://storage.googleapis.com/amnezia/docs?m-path=/documentation/instructions/encryption")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
130
client/ui/qml/Components/PasswordDrawer.qml
Normal file
130
client/ui/qml/Components/PasswordDrawer.qml
Normal file
@@ -0,0 +1,130 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import PageEnum 1.0
|
||||
import Style 1.0
|
||||
|
||||
import "../Controls2"
|
||||
import "../Controls2/TextTypes"
|
||||
|
||||
import "../Config"
|
||||
|
||||
DrawerType2 {
|
||||
id: root
|
||||
objectName: "passwordDrawer"
|
||||
|
||||
property bool fromOutside: true
|
||||
property string fileName
|
||||
property var securedFunc
|
||||
|
||||
signal restoreSecuredBackup
|
||||
signal importSecuredFile
|
||||
|
||||
expandedStateContent: ColumnLayout {
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 16
|
||||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 16
|
||||
|
||||
Connections {
|
||||
target: root
|
||||
|
||||
function onRestoreSecuredBackup() {
|
||||
root.securedFunc = function() {
|
||||
SettingsController.restoreAppConfigFromData(SystemController.getDecryptedData(fileName, passwordField.textField.text))
|
||||
}
|
||||
passwordDrawer.openTriggered()
|
||||
}
|
||||
|
||||
function onImportSecuredFile() {
|
||||
root.securedFunc = function() {
|
||||
if (ImportController.extractConfigFromData(SystemController.getDecryptedData(fileName, passwordField.textField.text))) {
|
||||
PageController.goToPage(PageEnum.PageSetupWizardViewConfig)
|
||||
}
|
||||
}
|
||||
passwordDrawer.openTriggered()
|
||||
}
|
||||
}
|
||||
|
||||
Header2TextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.bottomMargin: 8
|
||||
|
||||
text: qsTr("Password required")
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: passwordField
|
||||
|
||||
Connections {
|
||||
target: root
|
||||
|
||||
function onCloseTriggered() {
|
||||
passwordField.textField.text = ""
|
||||
}
|
||||
}
|
||||
|
||||
property bool hideContent: true
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
headerText: qsTr("Password")
|
||||
textField.echoMode: hideContent ? TextInput.Password : TextInput.Normal
|
||||
textField.text: textField.text
|
||||
|
||||
rightButtonClickedOnEnter: true
|
||||
|
||||
clickedFunc: function () {
|
||||
hideContent = !hideContent
|
||||
buttonImageSource = textField.text !== "" ? (hideContent ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg") : ""
|
||||
}
|
||||
|
||||
textField.onFocusChanged: {
|
||||
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
|
||||
}
|
||||
|
||||
textField.onTextChanged: {
|
||||
buttonImageSource = textField.text !== "" ? (hideContent ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg") : ""
|
||||
}
|
||||
}
|
||||
|
||||
LabelTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
text: qsTr("Hint: ") + (fromOutside ? SystemController.readHint(fileName) : SettingsController.getHint())
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: doneButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("Done")
|
||||
|
||||
clickedFunc: function() {
|
||||
if (fromOutside) {
|
||||
if (!SystemController.isPasswordValid(fileName, passwordField.textField.text)) {
|
||||
passwordField.errorText = qsTr("Invalid password")
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if (passwordField.textField.text !== SettingsController.getPassword()) {
|
||||
passwordField.errorText = qsTr("Invalid password")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (root.securedFunc && typeof root.securedFunc === "function") {
|
||||
root.securedFunc()
|
||||
}
|
||||
|
||||
root.closeTriggered()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -188,11 +188,10 @@ Item {
|
||||
leftImageSource: root.buttonImageSource
|
||||
|
||||
anchors.top: content.top
|
||||
anchors.bottom: content.bottom
|
||||
anchors.right: content.right
|
||||
|
||||
height: content.implicitHeight
|
||||
width: content.implicitHeight
|
||||
height: root.errorText !== "" ? content.implicitHeight - errorField.height - 5: content.implicitHeight
|
||||
width: root.errorText !== "" ? content.implicitHeight - errorField.height - 5: content.implicitHeight
|
||||
squareLeftSide: true
|
||||
|
||||
clickedFunc: function() {
|
||||
|
||||
@@ -60,6 +60,16 @@ PageType {
|
||||
headerText: qsTr("Configuration Files")
|
||||
descriptionText: qsTr("For router setup or the AmneziaWG app")
|
||||
}
|
||||
|
||||
EncryptionIndicator {
|
||||
id: indicator
|
||||
|
||||
visible: SettingsController.isFileEncryptionEnabled()
|
||||
linkEnabled: true
|
||||
|
||||
textString: qsTr("Encryption enabled.")
|
||||
iconPath: "qrc:/images/controls/lock-locked.svg"
|
||||
}
|
||||
}
|
||||
|
||||
delegate: ColumnLayout {
|
||||
|
||||
187
client/ui/qml/Pages2/PageSettingsAppEncryption.qml
Normal file
187
client/ui/qml/Pages2/PageSettingsAppEncryption.qml
Normal file
@@ -0,0 +1,187 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import PageEnum 1.0
|
||||
import Style 1.0
|
||||
|
||||
import "./"
|
||||
import "../Controls2"
|
||||
import "../Config"
|
||||
import "../Controls2/TextTypes"
|
||||
import "../Components"
|
||||
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
property bool isChangingPassword: false
|
||||
|
||||
Connections {
|
||||
target: SettingsController
|
||||
|
||||
function onFileEncryptionStateChanged() {
|
||||
PageController.showBusyIndicator(true)
|
||||
PageController.closePage()
|
||||
SettingsController.isFileEncryptionEnabled() ? PageController.goToPage(PageEnum.PageSettingsAppEncryption) : PageController.goToPage(PageEnum.PageSettingsAppPassword)
|
||||
PageController.showBusyIndicator(false)
|
||||
PageController.showNotificationMessage(SettingsController.isFileEncryptionEnabled() ? qsTr("Encryption enabled") : qsTr("Encryption disabled"))
|
||||
}
|
||||
}
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 20
|
||||
|
||||
onFocusChanged: {
|
||||
if (this.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
|
||||
backButtonFunction: function() {
|
||||
PageController.closePage()
|
||||
if (root.isChangingPassword) {
|
||||
root.isChangingPassword = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("Password & Encryption")
|
||||
descriptionText: qsTr("Password protection for backups and configuration files.\nRequired to restore or import encrypted files.")
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
Layout.leftMargin: 8
|
||||
Layout.bottomMargin: 32
|
||||
implicitHeight: 16
|
||||
|
||||
defaultColor: AmneziaStyle.color.transparent
|
||||
hoveredColor: AmneziaStyle.color.translucentWhite
|
||||
pressedColor: AmneziaStyle.color.sheerWhite
|
||||
disabledColor: AmneziaStyle.color.mutedGray
|
||||
textColor: AmneziaStyle.color.goldenApricot
|
||||
|
||||
text: qsTr("Learn more")
|
||||
|
||||
clickedFunc: function() {
|
||||
Qt.openUrlExternally("https://storage.googleapis.com/amnezia/docs?m-path=/documentation/instructions/encryption")
|
||||
}
|
||||
}
|
||||
|
||||
EncryptionIndicator {
|
||||
id: indicator
|
||||
|
||||
textString: qsTr("Password set. Encryption enabled")
|
||||
iconPath: "qrc:/images/controls/lock-locked.svg"
|
||||
}
|
||||
|
||||
|
||||
BasicButtonType {
|
||||
id: disableEncryptionButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("Disable encryption")
|
||||
|
||||
clickedFunc: function() {
|
||||
passwordDrawer.securedFunc = function() {
|
||||
PageController.showBusyIndicator(true)
|
||||
SettingsController.toggleFileEncryption(false)
|
||||
SettingsController.setPassword("")
|
||||
SettingsController.setHint("")
|
||||
PageController.showBusyIndicator(false)
|
||||
}
|
||||
passwordDrawer.openTriggered()
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: changePasswordButton
|
||||
|
||||
hoveredColor: AmneziaStyle.color.slateGray
|
||||
defaultColor: AmneziaStyle.color.midnightBlack
|
||||
textColor: AmneziaStyle.color.paleGray
|
||||
borderWidth: 1
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("Change password")
|
||||
|
||||
clickedFunc: function() {
|
||||
passwordDrawer.securedFunc = function() {
|
||||
root.isChangingPassword = true
|
||||
|
||||
PageController.showBusyIndicator(true)
|
||||
PageController.closePage()
|
||||
PageController.goToPage(PageEnum.PageSettingsAppPassword)
|
||||
PageController.showBusyIndicator(false)
|
||||
|
||||
SettingsController.changingPassword()
|
||||
}
|
||||
passwordDrawer.openTriggered()
|
||||
}
|
||||
}
|
||||
|
||||
PasswordDrawer {
|
||||
id: passwordDrawer
|
||||
|
||||
fromOutside: false
|
||||
|
||||
parent: root
|
||||
|
||||
anchors.fill: parent
|
||||
expandedHeight: root.height * 0.45
|
||||
}
|
||||
}
|
||||
|
||||
spacing: 16
|
||||
|
||||
footer: ColumnLayout {
|
||||
width: listView.width
|
||||
CaptionTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
textFormat: Text.RichText
|
||||
|
||||
text: qsTr("If the password is forgotten, it can be recovered. To reset the password, "
|
||||
+ "<a href=\"appSettings\" style=\"text-decoration:none; color:%1;\">settings must be reset</a>."
|
||||
+ "\nEncrypted files can only be opened with password used to encrypt them").arg(AmneziaStyle.color.goldenApricot)
|
||||
color: AmneziaStyle.color.mutedGray
|
||||
|
||||
onLinkActivated: function(link) {
|
||||
if (link === "appSettings") {
|
||||
PageController.closePage()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
215
client/ui/qml/Pages2/PageSettingsAppPassword.qml
Normal file
215
client/ui/qml/Pages2/PageSettingsAppPassword.qml
Normal file
@@ -0,0 +1,215 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import PageEnum 1.0
|
||||
import Style 1.0
|
||||
|
||||
import "./"
|
||||
import "../Controls2"
|
||||
import "../Config"
|
||||
import "../Controls2/TextTypes"
|
||||
import "../Components"
|
||||
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
property bool isChangingPassword: false
|
||||
|
||||
Connections {
|
||||
target: SettingsController
|
||||
|
||||
function onChangingPassword() {
|
||||
root.isChangingPassword = true
|
||||
}
|
||||
}
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 20
|
||||
|
||||
onFocusChanged: {
|
||||
if (this.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
|
||||
backButtonFunction: function() {
|
||||
PageController.closePage()
|
||||
if (root.isChangingPassword) {
|
||||
root.isChangingPassword = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: root.isChangingPassword ? qsTr("Change password") : qsTr("Password & Encryption")
|
||||
descriptionText: root.isChangingPassword ? qsTr("Existing encrypted files will still require the old password.\nThe new password will be used for new encrypted files.")
|
||||
: qsTr("Password protection for backups and configuration files.\nRequired to restore or import encrypted files.")
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
Layout.leftMargin: 8
|
||||
Layout.bottomMargin: 32
|
||||
implicitHeight: 16
|
||||
|
||||
defaultColor: AmneziaStyle.color.transparent
|
||||
hoveredColor: AmneziaStyle.color.translucentWhite
|
||||
pressedColor: AmneziaStyle.color.sheerWhite
|
||||
disabledColor: AmneziaStyle.color.mutedGray
|
||||
textColor: AmneziaStyle.color.goldenApricot
|
||||
|
||||
text: qsTr("Learn more")
|
||||
|
||||
clickedFunc: function() {
|
||||
Qt.openUrlExternally("https://storage.googleapis.com/amnezia/docs?m-path=/documentation/instructions/encryption")
|
||||
}
|
||||
}
|
||||
|
||||
EncryptionIndicator {
|
||||
id: indicator
|
||||
|
||||
visible: !root.isChangingPassword
|
||||
|
||||
textString: qsTr("Password not set. Encryption disabled")
|
||||
iconPath: "qrc:/images/controls/lock-unlocked.svg"
|
||||
}
|
||||
}
|
||||
|
||||
model: inputFields
|
||||
spacing: 16
|
||||
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: delegate
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: title
|
||||
textField.echoMode: hideContent ? TextInput.Password : TextInput.Normal
|
||||
textField.placeholderText: placeholderContent
|
||||
textField.text: textField.text
|
||||
|
||||
rightButtonClickedOnEnter: true
|
||||
|
||||
clickedFunc: function () {
|
||||
clickedHandler()
|
||||
buttonImageSource = textField.text !== "" ? imageSource : ""
|
||||
}
|
||||
|
||||
textField.onFocusChanged: {
|
||||
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
|
||||
}
|
||||
|
||||
textField.onTextChanged: {
|
||||
buttonImageSource = textField.text !== "" ? imageSource : ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
footer: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
BasicButtonType {
|
||||
id: continueButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("Continue")
|
||||
|
||||
clickedFunc: function() {
|
||||
if (!root.isPasswordProperlyFilled()) {
|
||||
return
|
||||
}
|
||||
|
||||
var _password = listView.itemAtIndex(vars.passwordIndex).children[0].textField.text
|
||||
var _hint = listView.itemAtIndex(vars.hintIndex).children[0].textField.text
|
||||
|
||||
SettingsController.setTempPassword(_password)
|
||||
SettingsController.setTempHint(_hint)
|
||||
|
||||
PageController.goToPage(PageEnum.PageSettingsAppPasswordConfirm)
|
||||
if (root.isChangingPassword) {
|
||||
SettingsController.changingPassword()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isPasswordProperlyFilled() {
|
||||
var tooShort = false
|
||||
|
||||
var secretDataItem = listView.itemAtIndex(vars.passwordIndex).children[0]
|
||||
if (secretDataItem.textField.text === "") {
|
||||
secretDataItem.errorText = qsTr("Password cannot be empty")
|
||||
tooShort = true
|
||||
} else if (secretDataItem.textField.text.length < 4) {
|
||||
secretDataItem.errorText = qsTr("Password too short")
|
||||
tooShort = true
|
||||
}
|
||||
|
||||
return !tooShort
|
||||
}
|
||||
|
||||
property list<QtObject> inputFields: [
|
||||
passwordObject,
|
||||
hintObject
|
||||
]
|
||||
|
||||
QtObject {
|
||||
id: passwordObject
|
||||
|
||||
property string title: root.isChangingPassword ? qsTr("New password") : qsTr("Set encryption password")
|
||||
readonly property string placeholderContent: ""
|
||||
property string imageSource: "qrc:/images/controls/eye.svg"
|
||||
property bool hideContent: true
|
||||
readonly property var clickedHandler: function() {
|
||||
hideContent = !hideContent
|
||||
imageSource = hideContent ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg"
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: hintObject
|
||||
|
||||
property string title: root.isChangingPassword ? qsTr("New password hint (optional)") : qsTr("Password hint")
|
||||
readonly property string placeholderContent: ""
|
||||
property string imageSource: ""
|
||||
property bool hideContent: false
|
||||
readonly property var clickedHandler: undefined
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: vars
|
||||
|
||||
readonly property int passwordIndex: 0
|
||||
readonly property int hintIndex: 1
|
||||
}
|
||||
}
|
||||
155
client/ui/qml/Pages2/PageSettingsAppPasswordConfirm.qml
Normal file
155
client/ui/qml/Pages2/PageSettingsAppPasswordConfirm.qml
Normal file
@@ -0,0 +1,155 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import PageEnum 1.0
|
||||
import Style 1.0
|
||||
|
||||
import "./"
|
||||
import "../Controls2"
|
||||
import "../Config"
|
||||
import "../Controls2/TextTypes"
|
||||
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
property bool isChangingPassword: false
|
||||
|
||||
Connections {
|
||||
target: SettingsController
|
||||
|
||||
function onChangingPassword() {
|
||||
root.isChangingPassword = true
|
||||
}
|
||||
}
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 20
|
||||
|
||||
onFocusChanged: {
|
||||
if (this.activeFocus) {
|
||||
listView.positionViewAtBeginning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ListViewType {
|
||||
id: listView
|
||||
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
BaseHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: root.isChangingPassword ? qsTr("Confirm new password") : qsTr("Confirm password")
|
||||
descriptionText: root.isChangingPassword ? qsTr("") : qsTr("If the password is forgotten, it cant be recovered. "
|
||||
+ "To reset the password, the app settings must be reset.\n"
|
||||
+ "Encrypted files can only be opened with the password used to encrypt them")
|
||||
}
|
||||
}
|
||||
|
||||
model: 1 // fake model
|
||||
spacing: 16
|
||||
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: delegate
|
||||
|
||||
property bool hideContent: true
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: root.isChangingPassword ? qsTr("Re-enter new password") : qsTr("Re-enter password")
|
||||
textField.echoMode: hideContent ? TextInput.Password : TextInput.Normal
|
||||
textField.placeholderText: ""
|
||||
textField.text: textField.text
|
||||
|
||||
rightButtonClickedOnEnter: true
|
||||
|
||||
clickedFunc: function () {
|
||||
hideContent = !hideContent
|
||||
buttonImageSource = textField.text !== "" ? (hideContent ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg") : ""
|
||||
}
|
||||
|
||||
textField.onFocusChanged: {
|
||||
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
|
||||
}
|
||||
|
||||
textField.onTextChanged: {
|
||||
buttonImageSource = textField.text !== "" ? (hideContent ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg") : ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
footer: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
LabelTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.bottomMargin: 24
|
||||
|
||||
text: qsTr("Hint: ") + SettingsController.getTempHint()
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: continueButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: root.isChangingPassword ? qsTr("Save new password") : qsTr("Turn on encryption")
|
||||
|
||||
clickedFunc: function() {
|
||||
if (!root.isPasswordProperlyFilled()) {
|
||||
return
|
||||
}
|
||||
|
||||
SettingsController.setPassword(SettingsController.getTempPassword())
|
||||
SettingsController.setHint(SettingsController.getTempHint())
|
||||
|
||||
SettingsController.setTempPassword("")
|
||||
SettingsController.setTempHint("")
|
||||
|
||||
PageController.closePage()
|
||||
PageController.goToPage(PageEnum.PageSettings)
|
||||
PageController.goToPage(PageEnum.PageSettingsAppEncryption)
|
||||
SettingsController.toggleFileEncryption(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isPasswordProperlyFilled() {
|
||||
var notMatch = false
|
||||
|
||||
var secretDataItem = listView.itemAtIndex(0).children[0]
|
||||
if (secretDataItem.textField.text !== SettingsController.getTempPassword()) {
|
||||
secretDataItem.errorText = qsTr("Passwords not match")
|
||||
notMatch = true
|
||||
}
|
||||
|
||||
return !notMatch
|
||||
}
|
||||
}
|
||||
@@ -213,6 +213,23 @@ PageType {
|
||||
|
||||
DividerType {}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: labelWithButtonAppPassword
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: qsTr("Password & Encryption")
|
||||
descriptionText: qsTr("Password protection for backups and configuration files")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
|
||||
clickedFunction: function() {
|
||||
SettingsController.getPassword() === "" ? PageController.goToPage(PageEnum.PageSettingsAppPassword)
|
||||
: PageController.goToPage(PageEnum.PageSettingsAppEncryption)
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {}
|
||||
|
||||
LabelWithButtonType {
|
||||
id: labelWithButtonLogging
|
||||
|
||||
|
||||
@@ -71,6 +71,16 @@ PageType {
|
||||
headerText: qsTr("Back up your configuration")
|
||||
descriptionText: qsTr("You can save your settings to a backup file to restore them the next time you install the application.")
|
||||
}
|
||||
|
||||
EncryptionIndicator {
|
||||
id: indicator
|
||||
|
||||
visible: SettingsController.isFileEncryptionEnabled()
|
||||
linkEnabled: true
|
||||
|
||||
textString: qsTr("Encryption enabled.")
|
||||
iconPath: "qrc:/images/controls/lock-locked.svg"
|
||||
}
|
||||
}
|
||||
|
||||
model: 1 // fake model to force the ListView to be created without a model
|
||||
@@ -144,10 +154,20 @@ PageType {
|
||||
var filePath = SystemController.getFileName(qsTr("Open backup file"),
|
||||
qsTr("Backup files (*.backup)"))
|
||||
if (filePath !== "") {
|
||||
restoreBackup(filePath)
|
||||
passwordDrawer.fileName = filePath
|
||||
SystemController.isFileEncrypted(filePath) ? passwordDrawer.restoreSecuredBackup() : restoreBackup(filePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PasswordDrawer {
|
||||
id: passwordDrawer
|
||||
|
||||
parent: root
|
||||
|
||||
anchors.fill: parent
|
||||
expandedHeight: root.height * 0.45
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,8 +48,8 @@ PageType {
|
||||
Layout.rightMargin: 16
|
||||
|
||||
headerText: qsTr("Logging")
|
||||
descriptionText: qsTr("Enabling this function will save application's logs automatically. " +
|
||||
"By default, logging functionality is disabled. Enable log saving in case of application malfunction.")
|
||||
descriptionText: qsTr("Logs help diagnose app errors and connection issues" +
|
||||
"Logging is disabled by default. Enable it when troubleshooting or if requested by support")
|
||||
}
|
||||
|
||||
SwitcherType {
|
||||
@@ -60,7 +60,7 @@ PageType {
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: qsTr("Enable logs")
|
||||
text: qsTr("Enable logging")
|
||||
|
||||
checked: SettingsController.isLoggingEnabled
|
||||
|
||||
@@ -77,7 +77,7 @@ PageType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: -8
|
||||
|
||||
text: qsTr("Clear logs")
|
||||
text: qsTr("Delete all logs")
|
||||
leftImageSource: "qrc:/images/controls/trash.svg"
|
||||
isSmallLeftImage: true
|
||||
|
||||
@@ -154,7 +154,7 @@ PageType {
|
||||
Layout.topMargin: -8
|
||||
Layout.bottomMargin: -8
|
||||
|
||||
text: qsTr("Export logs")
|
||||
text: qsTr("Save logs to file")
|
||||
leftImageSource: "qrc:/images/controls/save.svg"
|
||||
isSmallLeftImage: true
|
||||
|
||||
@@ -178,7 +178,7 @@ PageType {
|
||||
QtObject {
|
||||
id: clientLogs
|
||||
|
||||
readonly property string title: qsTr("Client logs")
|
||||
readonly property string title: qsTr("App logs")
|
||||
readonly property string description: qsTr("AmneziaVPN logs")
|
||||
readonly property bool isVisible: true
|
||||
readonly property var openLogsHandler: function() {
|
||||
|
||||
@@ -12,6 +12,7 @@ import "./"
|
||||
import "../Controls2"
|
||||
import "../Controls2/TextTypes"
|
||||
import "../Config"
|
||||
import "../Components"
|
||||
|
||||
PageType {
|
||||
id: root
|
||||
@@ -261,6 +262,15 @@ PageType {
|
||||
}
|
||||
}
|
||||
|
||||
PasswordDrawer {
|
||||
id: passwordDrawer
|
||||
|
||||
parent: root
|
||||
|
||||
anchors.fill: parent
|
||||
expandedHeight: root.height * 0.45
|
||||
}
|
||||
|
||||
property list<QtObject> variants: [
|
||||
amneziaVpn,
|
||||
selfHostVpn,
|
||||
@@ -312,7 +322,12 @@ PageType {
|
||||
qsTr("Backup files (*.backup)"))
|
||||
if (filePath !== "") {
|
||||
PageController.showBusyIndicator(true)
|
||||
SettingsController.restoreAppConfig(filePath)
|
||||
if (SystemController.isFileEncrypted(filePath)) {
|
||||
passwordDrawer.fileName = filePath
|
||||
passwordDrawer.restoreSecuredBackup()
|
||||
} else {
|
||||
SettingsController.restoreAppConfig(filePath)
|
||||
}
|
||||
PageController.showBusyIndicator(false)
|
||||
}
|
||||
}
|
||||
@@ -330,8 +345,13 @@ PageType {
|
||||
"Config files (*.vpn *.ovpn *.conf *.json)"
|
||||
var fileName = SystemController.getFileName(qsTr("Open config file"), nameFilter)
|
||||
if (fileName !== "") {
|
||||
if (ImportController.extractConfigFromFile(fileName)) {
|
||||
PageController.goToPage(PageEnum.PageSetupWizardViewConfig)
|
||||
if (SystemController.isFileEncrypted(fileName)) {
|
||||
passwordDrawer.fileName = fileName
|
||||
passwordDrawer.importSecuredFile()
|
||||
} else {
|
||||
if (ImportController.extractConfigFromFile(fileName)) {
|
||||
PageController.goToPage(PageEnum.PageSetupWizardViewConfig)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,6 +294,16 @@ PageType {
|
||||
color: AmneziaStyle.color.mutedGray
|
||||
}
|
||||
|
||||
EncryptionIndicator {
|
||||
id: indicator
|
||||
|
||||
visible: SettingsController.isFileEncryptionEnabled()
|
||||
linkEnabled: true
|
||||
|
||||
textString: qsTr("Encryption enabled.")
|
||||
iconPath: "qrc:/images/controls/lock-locked.svg"
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
id: clientNameTextField
|
||||
Layout.fillWidth: true
|
||||
|
||||
@@ -69,6 +69,16 @@ PageType {
|
||||
color: AmneziaStyle.color.mutedGray
|
||||
}
|
||||
|
||||
EncryptionIndicator {
|
||||
id: indicator
|
||||
|
||||
visible: SettingsController.isFileEncryptionEnabled()
|
||||
linkEnabled: true
|
||||
|
||||
textString: qsTr("Encryption enabled.")
|
||||
iconPath: "qrc:/images/controls/lock-locked.svg"
|
||||
}
|
||||
|
||||
DropDownType {
|
||||
id: serverSelector
|
||||
objectName: "serverSelector"
|
||||
|
||||
Reference in New Issue
Block a user