Compare commits

..

7 Commits

Author SHA1 Message Date
lunardunno d85e4a413a changing stdErr to stdOut 2024-10-27 18:18:42 +04:00
lunardunno fbf11c1689 added extended error descriptions 2024-10-27 17:48:13 +04:00
lunardunno b382257460 adding error codes 2024-10-27 17:19:15 +04:00
lunardunno 8ec4232a96 Renaming one of the errors 2024-10-27 17:17:06 +04:00
lunardunno 43fd9d5d90 Adding error handling
Adding error handling in the server controller for:
Sudo package is not pre-installed for sudo users.
Server user or associated group is not listed in the sudoers file.
Server user password required
2024-10-27 16:33:01 +04:00
lunardunno ae681ad6d2 simplification 2024-10-27 15:33:08 +04:00
lunardunno 6c22a7372d Checking requirements in script
Checking requirements for sudo users in script
2024-10-27 13:09:41 +04:00
34 changed files with 171 additions and 243 deletions
+2 -6
View File
@@ -217,11 +217,7 @@ jobs:
export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/ios/bin" export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/ios/bin"
export QT_MACOS_ROOT_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos" export QT_MACOS_ROOT_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos"
export PATH=$PATH:~/go/bin export PATH=$PATH:~/go/bin
sh deploy/build_ios.sh | \ sh deploy/build_ios.sh
sed -e '/-Xcc -DPROD_AGW_PUBLIC_KEY/,/-Xcc/ { /-Xcc/!d; }' -e '/-Xcc -DPROD_AGW_PUBLIC_KEY/d' | \
sed -e '/-Xcc -DDEV_AGW_PUBLIC_KEY/,/-Xcc/ { /-Xcc/!d; }' -e '/-Xcc -DDEV_AGW_PUBLIC_KEY/d' | \
sed -e '/-DPROD_AGW_PUBLIC_KEY/,/-D/ { /-D/!d; }' -e '/-DPROD_AGW_PUBLIC_KEY/d' | \
sed -e '/-DDEV_AGW_PUBLIC_KEY/,/-D/ { /-D/!d; }' -e '/-DDEV_AGW_PUBLIC_KEY/d'
env: env:
IOS_TRUST_CERT_BASE64: ${{ secrets.IOS_TRUST_CERT_BASE64 }} IOS_TRUST_CERT_BASE64: ${{ secrets.IOS_TRUST_CERT_BASE64 }}
IOS_SIGNING_CERT_BASE64: ${{ secrets.IOS_SIGNING_CERT_BASE64 }} IOS_SIGNING_CERT_BASE64: ${{ secrets.IOS_SIGNING_CERT_BASE64 }}
@@ -260,7 +256,7 @@ jobs:
- name: 'Setup xcode' - name: 'Setup xcode'
uses: maxim-lobanov/setup-xcode@v1 uses: maxim-lobanov/setup-xcode@v1
with: with:
xcode-version: '15.4.0' xcode-version: '14.3.1'
- name: 'Install Qt' - name: 'Install Qt'
uses: jurplel/install-qt-action@v3 uses: jurplel/install-qt-action@v3
+2 -2
View File
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
set(PROJECT AmneziaVPN) set(PROJECT AmneziaVPN)
project(${PROJECT} VERSION 4.8.2.4 project(${PROJECT} VERSION 4.8.2.3
DESCRIPTION "AmneziaVPN" DESCRIPTION "AmneziaVPN"
HOMEPAGE_URL "https://amnezia.org/" HOMEPAGE_URL "https://amnezia.org/"
) )
@@ -11,7 +11,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d")
set(RELEASE_DATE "${CURRENT_DATE}") set(RELEASE_DATE "${CURRENT_DATE}")
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH}) set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
set(APP_ANDROID_VERSION_CODE 2071) set(APP_ANDROID_VERSION_CODE 2069)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(MZ_PLATFORM_NAME "linux") set(MZ_PLATFORM_NAME "linux")
+11 -12
View File
@@ -4,21 +4,21 @@
[![Build Status](https://github.com/amnezia-vpn/amnezia-client/actions/workflows/deploy.yml/badge.svg?branch=dev)](https://github.com/amnezia-vpn/amnezia-client/actions/workflows/deploy.yml?query=branch:dev) [![Build Status](https://github.com/amnezia-vpn/amnezia-client/actions/workflows/deploy.yml/badge.svg?branch=dev)](https://github.com/amnezia-vpn/amnezia-client/actions/workflows/deploy.yml?query=branch:dev)
[![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/amnezia-vpn/amnezia-client) [![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/amnezia-vpn/amnezia-client)
[Amnezia](https://amnezia.org) is an open-source VPN client, with a key feature that enables you to deploy your own VPN server on your server. Amnezia is an open-source VPN client, with a key feature that enables you to deploy your own VPN server on your server.
[![Image](https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/uipic4.png)](https://amnezia.org) ![Image](https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/uipic4.png)
### [Website](https://amnezia.org) | [Alt website link](https://storage.googleapis.com/kldscp/amnezia.org) | [Documentation](https://docs.amnezia.org) | [Troubleshooting](https://docs.amnezia.org/troubleshooting) <br>
> [!TIP] <a href="https://amnezia.org/downloads"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download.png" width="150" style="max-width: 100%;"></a>
> If the [Amnezia website](https://amnezia.org) is blocked in your region, you can use an [Alternative website link](https://storage.googleapis.com/kldscp/amnezia.org). <a href="https://play.google.com/store/search?q=amnezia+vpn&c=apps"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/play.png" width="150" style="max-width: 100%;"></a>
<a href="https://apps.apple.com/us/app/amneziavpn/id1600529900"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/apl.png" width="150" style="max-width: 100%;"></a>
<a href="https://amnezia.org/downloads"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-website.svg" width="150" style="max-width: 100%; margin-right: 10px"></a> [Alternative download link (mirror)](https://storage.googleapis.com/kldscp/amnezia.org/downloads)
<a href="https://storage.googleapis.com/kldscp/amnezia.org/downloads"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/download-alt.svg" width="150" style="max-width: 100%;"></a>
[All releases](https://github.com/amnezia-vpn/amnezia-client/releases) [All releases](https://github.com/amnezia-vpn/amnezia-client/releases)
<br/> <br>
<a href="https://www.testiny.io"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/testiny.png" height="28px"></a> <a href="https://www.testiny.io"><img src="https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/testiny.png" height="28px"></a>
@@ -33,8 +33,7 @@
## Links ## Links
- [https://amnezia.org](https://amnezia.org) - Project website | [Alternative link (mirror)](https://storage.googleapis.com/kldscp/amnezia.org) - [https://amnezia.org](https://amnezia.org) - project website | [Alternative link (mirror)](https://storage.googleapis.com/kldscp/amnezia.org)
- [https://docs.amnezia.org](https://docs.amnezia.org) - Documentation
- [https://www.reddit.com/r/AmneziaVPN](https://www.reddit.com/r/AmneziaVPN) - Reddit - [https://www.reddit.com/r/AmneziaVPN](https://www.reddit.com/r/AmneziaVPN) - Reddit
- [https://t.me/amnezia_vpn_en](https://t.me/amnezia_vpn_en) - Telegram support channel (English) - [https://t.me/amnezia_vpn_en](https://t.me/amnezia_vpn_en) - Telegram support channel (English)
- [https://t.me/amnezia_vpn_ir](https://t.me/amnezia_vpn_ir) - Telegram support channel (Farsi) - [https://t.me/amnezia_vpn_ir](https://t.me/amnezia_vpn_ir) - Telegram support channel (Farsi)
@@ -183,8 +182,8 @@ Patreon: [https://www.patreon.com/amneziavpn](https://www.patreon.com/amneziavpn
Bitcoin: bc1q26eevjcg9j0wuyywd2e3uc9cs2w58lpkpjxq6p <br> Bitcoin: bc1q26eevjcg9j0wuyywd2e3uc9cs2w58lpkpjxq6p <br>
USDT BEP20: 0x6abD576765a826f87D1D95183438f9408C901bE4 <br> USDT BEP20: 0x6abD576765a826f87D1D95183438f9408C901bE4 <br>
USDT TRC20: TELAitazF1MZGmiNjTcnxDjEiH5oe7LC9d <br> USDT TRC20: TELAitazF1MZGmiNjTcnxDjEiH5oe7LC9d <br>
XMR: 48spms39jt1L2L5vyw2RQW6CXD6odUd4jFu19GZcDyKKQV9U88wsJVjSbL4CfRys37jVMdoaWVPSvezCQPhHXUW5UKLqUp3 <br> XMR: 48spms39jt1L2L5vyw2RQW6CXD6odUd4jFu19GZcDyKKQV9U88wsJVjSbL4CfRys37jVMdoaWVPSvezCQPhHXUW5UKLqUp3
TON: UQDpU1CyKRmg7L8mNScKk9FRc2SlESuI7N-Hby4nX-CcVmns
## Acknowledgments ## Acknowledgments
This project is tested with BrowserStack. This project is tested with BrowserStack.
+1 -1
View File
@@ -20,7 +20,7 @@
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<!-- To request network state --> <!-- To request network state -->
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="28" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
@@ -1,12 +1,11 @@
package org.amnezia.vpn.protocol.wireguard package org.amnezia.vpn.protocol.wireguard
import android.net.VpnService.Builder import android.net.VpnService.Builder
import kotlinx.coroutines.CoroutineScope import java.io.IOException
import java.util.Locale
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.withContext
import org.amnezia.awg.GoBackend import org.amnezia.awg.GoBackend
import org.amnezia.vpn.protocol.Protocol import org.amnezia.vpn.protocol.Protocol
import org.amnezia.vpn.protocol.ProtocolState.CONNECTED import org.amnezia.vpn.protocol.ProtocolState.CONNECTED
@@ -28,8 +27,6 @@ open class Wireguard : Protocol() {
private var tunnelHandle: Int = -1 private var tunnelHandle: Int = -1
protected open val ifName: String = "amn0" protected open val ifName: String = "amn0"
private lateinit var scope: CoroutineScope
private var statusJob: Job? = null
override val statistics: Statistics override val statistics: Statistics
get() { get() {
@@ -52,17 +49,46 @@ open class Wireguard : Protocol() {
override fun internalInit() { override fun internalInit() {
if (!isInitialized) loadSharedLibrary(context, "wg-go") if (!isInitialized) loadSharedLibrary(context, "wg-go")
if (this::scope.isInitialized) {
scope.cancel()
}
scope = CoroutineScope(Dispatchers.IO)
} }
override suspend fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean) { override suspend fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean) {
val wireguardConfig = parseConfig(config) val wireguardConfig = parseConfig(config)
val startTime = System.currentTimeMillis()
start(wireguardConfig, vpnBuilder, protect) start(wireguardConfig, vpnBuilder, protect)
waitForConnection(startTime)
state.value = CONNECTED
} }
private suspend fun waitForConnection(startTime: Long) {
Log.d(TAG, "Waiting for connection")
withContext(Dispatchers.IO) {
val time = String.format(Locale.ROOT,"%.3f", startTime / 1000.0)
try {
delay(1000)
var log = getLogcat(time)
Log.v(TAG, "First waiting log: $log")
// check that there is a connection log,
// to avoid infinite connection
if (!log.contains("Attaching to interface")) {
Log.w(TAG, "Logs do not contain a connection log")
return@withContext
}
while (!log.contains("Received handshake response")) {
delay(1000)
log = getLogcat(time)
}
} catch (e: IOException) {
Log.e(TAG, "Failed to get logcat: $e")
}
}
}
private fun getLogcat(time: String): String =
ProcessBuilder("logcat", "--buffer=main", "--format=raw", "*:S AmneziaWG/awg0", "-t", time)
.redirectErrorStream(true)
.start()
.inputStream.reader().readText()
protected open fun parseConfig(config: JSONObject): WireguardConfig { protected open fun parseConfig(config: JSONObject): WireguardConfig {
val configData = config.getJSONObject("wireguard_config_data") val configData = config.getJSONObject("wireguard_config_data")
return WireguardConfig.build { return WireguardConfig.build {
@@ -152,43 +178,6 @@ open class Wireguard : Protocol() {
tunnelHandle = -1 tunnelHandle = -1
throw VpnStartException("Protect VPN interface: permission not granted or revoked") throw VpnStartException("Protect VPN interface: permission not granted or revoked")
} }
launchStatusJob()
}
private fun launchStatusJob() {
Log.d(TAG, "Launch status job")
statusJob = scope.launch {
while (true) {
val lastHandshake = getLastHandshake()
Log.v(TAG, "lastHandshake=$lastHandshake")
if (lastHandshake == 0L) {
delay(1000)
continue
}
if (lastHandshake == -2L || lastHandshake > 0L) state.value = CONNECTED
else if (lastHandshake == -1L) state.value = DISCONNECTED
statusJob = null
break
}
}
}
private fun getLastHandshake(): Long {
if (tunnelHandle == -1) {
Log.e(TAG, "Trying to get config of a non-existent tunnel")
return -1
}
val config = GoBackend.awgGetConfig(tunnelHandle)
if (config == null) {
Log.e(TAG, "Failed to get tunnel config")
return -2
}
val lastHandshake = config.lines().find { it.startsWith("last_handshake_time_sec=") }?.substring(24)?.toLong()
if (lastHandshake == null) {
Log.e(TAG, "Failed to get last_handshake_time_sec")
return -2
}
return lastHandshake
} }
override fun stopVpn() { override fun stopVpn() {
@@ -196,8 +185,6 @@ open class Wireguard : Protocol() {
Log.w(TAG, "Tunnel already down") Log.w(TAG, "Tunnel already down")
return return
} }
statusJob?.cancel()
statusJob = null
val handleToClose = tunnelHandle val handleToClose = tunnelHandle
tunnelHandle = -1 tunnelHandle = -1
GoBackend.awgTurnOff(handleToClose) GoBackend.awgTurnOff(handleToClose)
+4 -6
View File
@@ -50,8 +50,6 @@ namespace
constexpr char authData[] = "auth_data"; constexpr char authData[] = "auth_data";
} }
const int requestTimeoutMsecs = 12 * 1000; // 12 secs
ErrorCode checkErrors(const QList<QSslError> &sslErrors, QNetworkReply *reply) ErrorCode checkErrors(const QList<QSslError> &sslErrors, QNetworkReply *reply)
{ {
if (!sslErrors.empty()) { if (!sslErrors.empty()) {
@@ -179,7 +177,7 @@ void ApiController::fillServerConfig(const QString &protocol, const ApiControlle
QStringList ApiController::getProxyUrls() QStringList ApiController::getProxyUrls()
{ {
QNetworkRequest request; QNetworkRequest request;
request.setTransferTimeout(requestTimeoutMsecs); request.setTransferTimeout(7000);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QEventLoop wait; QEventLoop wait;
@@ -282,7 +280,7 @@ void ApiController::updateServerConfigFromApi(const QString &installationUuid, c
if (serverConfig.value(config_key::configVersion).toInt()) { if (serverConfig.value(config_key::configVersion).toInt()) {
QNetworkRequest request; QNetworkRequest request;
request.setTransferTimeout(requestTimeoutMsecs); request.setTransferTimeout(7000);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setRawHeader("Authorization", "Api-Key " + serverConfig.value(configKey::accessToken).toString().toUtf8()); request.setRawHeader("Authorization", "Api-Key " + serverConfig.value(configKey::accessToken).toString().toUtf8());
QString endpoint = serverConfig.value(configKey::apiEdnpoint).toString(); QString endpoint = serverConfig.value(configKey::apiEdnpoint).toString();
@@ -338,7 +336,7 @@ ErrorCode ApiController::getServicesList(QByteArray &responseBody)
#endif #endif
QNetworkRequest request; QNetworkRequest request;
request.setTransferTimeout(requestTimeoutMsecs); request.setTransferTimeout(7000);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setUrl(QString("%1v1/services").arg(m_gatewayEndpoint)); request.setUrl(QString("%1v1/services").arg(m_gatewayEndpoint));
@@ -392,7 +390,7 @@ ErrorCode ApiController::getConfigForService(const QString &installationUuid, co
#endif #endif
QNetworkRequest request; QNetworkRequest request;
request.setTransferTimeout(requestTimeoutMsecs); request.setTransferTimeout(7000);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setUrl(QString("%1v1/config").arg(m_gatewayEndpoint)); request.setUrl(QString("%1v1/config").arg(m_gatewayEndpoint));
@@ -770,6 +770,12 @@ ErrorCode ServerController::isUserInSudo(const ServerCredentials &credentials, D
if (!stdOut.contains("sudo")) if (!stdOut.contains("sudo"))
return ErrorCode::ServerUserNotInSudo; return ErrorCode::ServerUserNotInSudo;
if (stdOut.contains("command not found"))
return ErrorCode::SudoPackageIsNotPreinstalled;
if (stdOut.contains("sudoers"))
return ErrorCode::ServerUserNotListedInSudoers;
if (stdOut.contains("password is required"))
return ErrorCode::ServerUserPasswordRequired;
return error; return error;
} }
+3
View File
@@ -56,6 +56,9 @@ namespace amnezia
ServerCancelInstallation = 204, ServerCancelInstallation = 204,
ServerUserNotInSudo = 205, ServerUserNotInSudo = 205,
ServerPacketManagerError = 206, ServerPacketManagerError = 206,
SudoPackageIsNotPreinstalled = 207,
ServerUserNotListedInSudoers = 208,
ServerUserPasswordRequired = 209,
// Ssh connection errors // Ssh connection errors
SshRequestDeniedError = 300, SshRequestDeniedError = 300,
+3
View File
@@ -21,6 +21,9 @@ QString errorString(ErrorCode code) {
case(ErrorCode::ServerCancelInstallation): errorMessage = QObject::tr("Installation canceled by user"); break; case(ErrorCode::ServerCancelInstallation): errorMessage = QObject::tr("Installation canceled by user"); break;
case(ErrorCode::ServerUserNotInSudo): errorMessage = QObject::tr("The user does not have permission to use sudo"); break; case(ErrorCode::ServerUserNotInSudo): errorMessage = QObject::tr("The user does not have permission to use sudo"); break;
case(ErrorCode::ServerPacketManagerError): errorMessage = QObject::tr("Server error: Packet manager error"); break; case(ErrorCode::ServerPacketManagerError): errorMessage = QObject::tr("Server error: Packet manager error"); break;
case(ErrorCode::SudoPackageIsNotPreinstalled): errorMessage = QObject::tr("The sudo package is not pre-installed"); break;
case(ErrorCode::ServerUserNotListedInSudoers): errorMessage = QObject::tr("The user is not listed in sudoers"); break;
case(ErrorCode::ServerUserPasswordRequired): errorMessage = QObject::tr("The user's password is required"); break;
// Libssh errors // Libssh errors
case(ErrorCode::SshRequestDeniedError): errorMessage = QObject::tr("SSH request was denied"); break; case(ErrorCode::SshRequestDeniedError): errorMessage = QObject::tr("SSH request was denied"); break;
+2 -1
View File
@@ -1,2 +1,3 @@
echo $LC_MESSAGES | grep -qE "en_US.UTF-8|C.UTF-8" || export LC_MESSAGES=C.UTF-8;\
CUR_USER=$(whoami);\ CUR_USER=$(whoami);\
groups $CUR_USER groups $CUR_USER | grep sudo && sudo -nu $CUR_USER sudo -n uname > /dev/null
@@ -848,6 +848,7 @@ bool InstallController::updateServiceFromApi(const int serverIndex, const QStrin
newServerConfig.insert(configKey::apiConfig, newApiConfig); newServerConfig.insert(configKey::apiConfig, newApiConfig);
newServerConfig.insert(configKey::authData, authData); newServerConfig.insert(configKey::authData, authData);
newServerConfig.insert(config_key::crc, serverConfig.value(config_key::crc));
m_serversModel->editServer(newServerConfig, serverIndex); m_serversModel->editServer(newServerConfig, serverIndex);
if (reloadServiceConfig) { if (reloadServiceConfig) {
@@ -84,7 +84,7 @@ DrawerType2 {
Layout.topMargin: 16 Layout.topMargin: 16
text: qsTr("Share") text: qsTr("Share")
leftImageSource: "qrc:/images/controls/share-2.svg" imageSource: "qrc:/images/controls/share-2.svg"
KeyNavigation.tab: copyConfigTextButton KeyNavigation.tab: copyConfigTextButton
@@ -120,7 +120,7 @@ DrawerType2 {
borderWidth: 1 borderWidth: 1
text: qsTr("Copy") text: qsTr("Copy")
leftImageSource: "qrc:/images/controls/copy.svg" imageSource: "qrc:/images/controls/copy.svg"
Keys.onReturnPressed: { copyConfigTextButton.clicked() } Keys.onReturnPressed: { copyConfigTextButton.clicked() }
Keys.onEnterPressed: { copyConfigTextButton.clicked() } Keys.onEnterPressed: { copyConfigTextButton.clicked() }
@@ -143,7 +143,7 @@ DrawerType2 {
borderWidth: 1 borderWidth: 1
text: qsTr("Copy config string") text: qsTr("Copy config string")
leftImageSource: "qrc:/images/controls/copy.svg" imageSource: "qrc:/images/controls/copy.svg"
KeyNavigation.tab: showSettingsButton KeyNavigation.tab: showSettingsButton
} }
+8 -14
View File
@@ -22,10 +22,9 @@ Button {
property int borderWidth: 0 property int borderWidth: 0
property int borderFocusedWidth: 1 property int borderFocusedWidth: 1
property string leftImageSource property string imageSource
property string rightImageSource property string rightImageSource
property string leftImageColor property string leftImageColor: textColor
property bool changeLeftImageSize: true
property bool squareLeftSide: false property bool squareLeftSide: false
@@ -128,23 +127,18 @@ Button {
anchors.centerIn: parent anchors.centerIn: parent
Image { Image {
id: leftImage Layout.preferredHeight: 20
source: root.leftImageSource Layout.preferredWidth: 20
visible: root.leftImageSource === "" ? false : true
source: root.imageSource
visible: root.imageSource === "" ? false : true
layer { layer {
enabled: leftImageColor !== "" ? true : false enabled: true
effect: ColorOverlay { effect: ColorOverlay {
color: leftImageColor color: leftImageColor
} }
} }
Component.onCompleted: {
if (root.changeLeftImageSize) {
leftImage.Layout.preferredHeight = 20
leftImage.Layout.preferredWidth = 20
}
}
} }
ButtonTextType { ButtonTextType {
@@ -14,7 +14,7 @@ Popup {
visible: false visible: false
Overlay.modal: Rectangle { Overlay.modal: Rectangle {
color: AmneziaStyle.color.translucentMidnightBlack color: Qt.rgba(14/255, 14/255, 17/255, 0.8)
} }
background: Rectangle { background: Rectangle {
+1 -1
View File
@@ -19,7 +19,7 @@ RadioButton {
property string textColor: AmneziaStyle.color.midnightBlack property string textColor: AmneziaStyle.color.midnightBlack
property string pressedBorderColor: AmneziaStyle.color.softGoldenApricot property string pressedBorderColor: Qt.rgba(251/255, 178/255, 106/255, 0.3)
property string selectedBorderColor: AmneziaStyle.color.goldenApricot property string selectedBorderColor: AmneziaStyle.color.goldenApricot
property string defaultBodredColor: AmneziaStyle.color.transparent property string defaultBodredColor: AmneziaStyle.color.transparent
property int borderWidth: 0 property int borderWidth: 0
@@ -145,7 +145,6 @@ Button {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
hoverEnabled: true hoverEnabled: true
enabled: root.enabled
onEntered: { onEntered: {
backgroundRect.color = root.hoveredColor backgroundRect.color = root.hoveredColor
+1 -1
View File
@@ -92,7 +92,7 @@ Item {
id: background id: background
anchors.fill: parent anchors.fill: parent
color: root.isCollapsed ? AmneziaStyle.color.transparent : AmneziaStyle.color.translucentMidnightBlack color: root.isCollapsed ? AmneziaStyle.color.transparent : Qt.rgba(14/255, 14/255, 17/255, 0.8)
Behavior on color { Behavior on color {
PropertyAnimation { duration: 200 } PropertyAnimation { duration: 200 }
+1 -1
View File
@@ -24,7 +24,7 @@ Popup {
Overlay.modal: Rectangle { Overlay.modal: Rectangle {
visible: root.closeButtonVisible visible: root.closeButtonVisible
color: AmneziaStyle.color.translucentMidnightBlack color: Qt.rgba(14/255, 14/255, 17/255, 0.8)
} }
onOpened: { onOpened: {
@@ -183,7 +183,7 @@ Item {
focusPolicy: Qt.NoFocus focusPolicy: Qt.NoFocus
text: root.buttonText text: root.buttonText
leftImageSource: root.buttonImageSource imageSource: root.buttonImageSource
anchors.top: content.top anchors.top: content.top
anchors.bottom: content.bottom anchors.bottom: content.bottom
@@ -14,7 +14,7 @@ Popup {
visible: false visible: false
Overlay.modal: Rectangle { Overlay.modal: Rectangle {
color: AmneziaStyle.color.translucentMidnightBlack color: Qt.rgba(14/255, 14/255, 17/255, 0.8)
} }
background: Rectangle { background: Rectangle {
@@ -22,9 +22,5 @@ QtObject {
readonly property color sheerWhite: Qt.rgba(1, 1, 1, 0.12) readonly property color sheerWhite: Qt.rgba(1, 1, 1, 0.12)
readonly property color translucentWhite: Qt.rgba(1, 1, 1, 0.08) readonly property color translucentWhite: Qt.rgba(1, 1, 1, 0.08)
readonly property color barelyTranslucentWhite: Qt.rgba(1, 1, 1, 0.05) readonly property color barelyTranslucentWhite: Qt.rgba(1, 1, 1, 0.05)
readonly property color translucentMidnightBlack: Qt.rgba(14/255, 14/255, 17/255, 0.8)
readonly property color softGoldenApricot: Qt.rgba(251/255, 178/255, 106/255, 0.3)
readonly property color mistyGray: Qt.rgba(215/255, 216/255, 219/255, 0.8)
readonly property color cloudyGray: Qt.rgba(215/255, 216/255, 219/255, 0.65)
} }
} }
+12 -33
View File
@@ -98,6 +98,7 @@ PageType {
pressedColor: AmneziaStyle.color.sheerWhite pressedColor: AmneziaStyle.color.sheerWhite
disabledColor: AmneziaStyle.color.mutedGray disabledColor: AmneziaStyle.color.mutedGray
textColor: AmneziaStyle.color.mutedGray textColor: AmneziaStyle.color.mutedGray
leftImageColor: AmneziaStyle.color.transparent
borderWidth: 0 borderWidth: 0
buttonTextLabel.lineHeight: 20 buttonTextLabel.lineHeight: 20
@@ -109,7 +110,7 @@ PageType {
text: isSplitTunnelingEnabled ? qsTr("Split tunneling enabled") : qsTr("Split tunneling disabled") text: isSplitTunnelingEnabled ? qsTr("Split tunneling enabled") : qsTr("Split tunneling disabled")
leftImageSource: isSplitTunnelingEnabled ? "qrc:/images/controls/split-tunneling.svg" : "" imageSource: isSplitTunnelingEnabled ? "qrc:/images/controls/split-tunneling.svg" : ""
rightImageSource: "qrc:/images/controls/chevron-down.svg" rightImageSource: "qrc:/images/controls/chevron-down.svg"
Keys.onEnterPressed: splitTunnelingButton.clicked() Keys.onEnterPressed: splitTunnelingButton.clicked()
@@ -165,7 +166,6 @@ PageType {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
spacing: 0
Component.onCompleted: { Component.onCompleted: {
drawer.collapsedHeight = collapsed.implicitHeight drawer.collapsedHeight = collapsed.implicitHeight
@@ -267,39 +267,18 @@ PageType {
RowLayout { RowLayout {
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
Layout.topMargin: 8 Layout.bottomMargin: drawer.isCollapsed ? 44 : ServersModel.isDefaultServerFromApi ? 89 : 44
Layout.bottomMargin: drawer.isCollapsed ? 44 : ServersModel.isDefaultServerFromApi ? 61 : 16
spacing: 0 spacing: 0
BasicButtonType { Image {
enabled: (ServersModel.defaultServerImagePathCollapsed !== "") && drawer.isCollapsed Layout.rightMargin: 8
hoverEnabled: enabled visible: source !== ""
source: ServersModel.defaultServerImagePathCollapsed
implicitHeight: 36 }
leftPadding: 16
rightPadding: 16
defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.translucentWhite
pressedColor: AmneziaStyle.color.sheerWhite
disabledColor: AmneziaStyle.color.transparent
textColor: AmneziaStyle.color.mutedGray
buttonTextLabel.lineHeight: 16
buttonTextLabel.font.pixelSize: 13
buttonTextLabel.font.weight: 400
LabelTextType {
id: collapsedServerMenuDescription
text: drawer.isCollapsed ? ServersModel.defaultServerDescriptionCollapsed : ServersModel.defaultServerDescriptionExpanded text: drawer.isCollapsed ? ServersModel.defaultServerDescriptionCollapsed : ServersModel.defaultServerDescriptionExpanded
leftImageSource: ServersModel.defaultServerImagePathCollapsed
changeLeftImageSize: false
rightImageSource: hoverEnabled ? "qrc:/images/controls/chevron-down.svg" : ""
onClicked: {
ServersModel.processedIndex = ServersModel.defaultIndex
PageController.goToPage(PageEnum.PageSettingsServerInfo)
}
} }
} }
} }
@@ -337,8 +316,8 @@ PageType {
rootButtonImageColor: AmneziaStyle.color.midnightBlack rootButtonImageColor: AmneziaStyle.color.midnightBlack
rootButtonBackgroundColor: AmneziaStyle.color.paleGray rootButtonBackgroundColor: AmneziaStyle.color.paleGray
rootButtonBackgroundHoveredColor: AmneziaStyle.color.mistyGray rootButtonBackgroundHoveredColor: Qt.rgba(215, 216, 219, 0.8)
rootButtonBackgroundPressedColor: AmneziaStyle.color.cloudyGray rootButtonBackgroundPressedColor: Qt.rgba(215, 216, 219, 0.65)
rootButtonHoveredBorderColor: AmneziaStyle.color.transparent rootButtonHoveredBorderColor: AmneziaStyle.color.transparent
rootButtonDefaultBorderColor: AmneziaStyle.color.transparent rootButtonDefaultBorderColor: AmneziaStyle.color.transparent
rootButtonTextTopMargin: 8 rootButtonTextTopMargin: 8
@@ -132,8 +132,8 @@ PageType {
implicitHeight: 32 implicitHeight: 32
defaultColor: "transparent" defaultColor: "transparent"
hoveredColor: AmneziaStyle.color.translucentWhite hoveredColor: Qt.rgba(1, 1, 1, 0.08)
pressedColor: AmneziaStyle.color.sheerWhite pressedColor: Qt.rgba(1, 1, 1, 0.12)
textColor: AmneziaStyle.color.vibrantRed textColor: AmneziaStyle.color.vibrantRed
text: qsTr("Reload API config") text: qsTr("Reload API config")
@@ -172,8 +172,8 @@ PageType {
implicitHeight: 32 implicitHeight: 32
defaultColor: "transparent" defaultColor: "transparent"
hoveredColor: AmneziaStyle.color.translucentWhite hoveredColor: Qt.rgba(1, 1, 1, 0.08)
pressedColor: AmneziaStyle.color.sheerWhite pressedColor: Qt.rgba(1, 1, 1, 0.12)
textColor: AmneziaStyle.color.vibrantRed textColor: AmneziaStyle.color.vibrantRed
text: qsTr("Remove from application") text: qsTr("Remove from application")
@@ -16,82 +16,83 @@ PageType {
defaultActiveFocusItem: focusItem defaultActiveFocusItem: focusItem
ColumnLayout { FlickableType {
id: header id: fl
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
spacing: 0
Item {
id: focusItem
KeyNavigation.tab: backButton
}
BackButtonType {
id: backButton
Layout.topMargin: 20
// KeyNavigation.tab: fileButton.rightButton
}
HeaderType {
Layout.fillWidth: true
Layout.topMargin: 8
Layout.rightMargin: 16
Layout.leftMargin: 16
Layout.bottomMargin: 16
headerText: qsTr("VPN by Amnezia")
descriptionText: qsTr("Choose a VPN service that suits your needs.")
}
}
ListView {
id: servicesListView
anchors.top: header.bottom
anchors.right: parent.right
anchors.left: parent.left
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.topMargin: 16 contentHeight: content.height
spacing: 0
currentIndex: 1 ColumnLayout {
clip: true id: content
model: ApiServicesModel
ScrollBar.vertical: ScrollBar {} anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
delegate: Item { spacing: 0
implicitWidth: servicesListView.width
implicitHeight: delegateContent.implicitHeight
ColumnLayout { Item {
id: delegateContent id: focusItem
KeyNavigation.tab: backButton
}
anchors.fill: parent BackButtonType {
id: backButton
Layout.topMargin: 20
// KeyNavigation.tab: fileButton.rightButton
}
CardWithIconsType { HeaderType {
id: card Layout.fillWidth: true
Layout.topMargin: 8
Layout.rightMargin: 16
Layout.leftMargin: 16
Layout.bottomMargin: 32
Layout.fillWidth: true headerText: qsTr("VPN by Amnezia")
Layout.rightMargin: 16 descriptionText: qsTr("Choose a VPN service that suits your needs.")
Layout.leftMargin: 16 }
Layout.bottomMargin: 16
headerText: name ListView {
bodyText: cardDescription id: containers
footerText: price width: parent.width
height: containers.contentItem.height
spacing: 16
rightImageSource: "qrc:/images/controls/chevron-right.svg" currentIndex: 1
interactive: false
model: ApiServicesModel
enabled: isServiceAvailable delegate: Item {
implicitWidth: containers.width
implicitHeight: delegateContent.implicitHeight
onClicked: { ColumnLayout {
if (isServiceAvailable) { id: delegateContent
ApiServicesModel.setServiceIndex(index)
PageController.goToPage(PageEnum.PageSetupWizardApiServiceInfo) anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
CardWithIconsType {
id: card
Layout.fillWidth: true
Layout.rightMargin: 16
Layout.leftMargin: 16
headerText: name
bodyText: cardDescription
footerText: price
rightImageSource: "qrc:/images/controls/chevron-right.svg"
onClicked: {
if (isServiceAvailable) {
ApiServicesModel.setServiceIndex(index)
PageController.goToPage(PageEnum.PageSetupWizardApiServiceInfo)
}
}
} }
} }
} }
@@ -47,6 +47,7 @@ PageType {
KeyNavigation.tab: textKey.textField KeyNavigation.tab: textKey.textField
} }
HeaderType { HeaderType {
property bool isVisible: SettingsController.getInstallationUuid() !== "" || PageController.isStartPageVisible() property bool isVisible: SettingsController.getInstallationUuid() !== "" || PageController.isStartPageVisible()
+1 -1
View File
@@ -573,7 +573,7 @@ PageType {
visible: accessTypeSelector.currentIndex === 0 visible: accessTypeSelector.currentIndex === 0
text: qsTr("Share") text: qsTr("Share")
leftImageSource: "qrc:/images/controls/share-2.svg" imageSource: "qrc:/images/controls/share-2.svg"
Keys.onTabPressed: lastItemTabClicked(focusItem) Keys.onTabPressed: lastItemTabClicked(focusItem)
+1 -1
View File
@@ -135,7 +135,7 @@ PageType {
Layout.topMargin: 40 Layout.topMargin: 40
text: qsTr("Share") text: qsTr("Share")
leftImageSource: "qrc:/images/controls/share-2.svg" imageSource: "qrc:/images/controls/share-2.svg"
Keys.onTabPressed: lastItemTabClicked(focusItem) Keys.onTabPressed: lastItemTabClicked(focusItem)
-10
View File
@@ -19,11 +19,6 @@ date > $LOG_FILE
echo "Script started" >> $LOG_FILE echo "Script started" >> $LOG_FILE
sudo killall -9 $APP_NAME 2>> $LOG_FILE sudo killall -9 $APP_NAME 2>> $LOG_FILE
if command -v steamos-readonly &> /dev/null; then
sudo steamos-readonly disable >> $LOG_FILE
echo "steamos-readonly disabled" >> $LOG_FILE
fi
if sudo systemctl is-active --quiet $APP_NAME; then if sudo systemctl is-active --quiet $APP_NAME; then
sudo systemctl stop $APP_NAME >> $LOG_FILE sudo systemctl stop $APP_NAME >> $LOG_FILE
sudo systemctl disable $APP_NAME >> $LOG_FILE sudo systemctl disable $APP_NAME >> $LOG_FILE
@@ -47,11 +42,6 @@ sudo chmod 555 /usr/share/applications/$APP_NAME.desktop >> $LOG_FILE
echo "user desktop creation loop ended" >> $LOG_FILE echo "user desktop creation loop ended" >> $LOG_FILE
if command -v steamos-readonly &> /dev/null; then
sudo steamos-readonly enable >> $LOG_FILE
echo "steamos-readonly enabled" >> $LOG_FILE
fi
date >> $LOG_FILE date >> $LOG_FILE
echo "Service status:" >> $LOG_FILE echo "Service status:" >> $LOG_FILE
sudo systemctl status $APP_NAME >> $LOG_FILE sudo systemctl status $APP_NAME >> $LOG_FILE
-10
View File
@@ -13,11 +13,6 @@ date >> $LOG_FILE
echo "Uninstall Script started" >> $LOG_FILE echo "Uninstall Script started" >> $LOG_FILE
sudo killall -9 $APP_NAME 2>> $LOG_FILE sudo killall -9 $APP_NAME 2>> $LOG_FILE
if command -v steamos-readonly &> /dev/null; then
sudo steamos-readonly disable >> $LOG_FILE
echo "steamos-readonly disabled" >> $LOG_FILE
fi
ls /opt/AmneziaVPN/client/lib/* | while IFS=: read -r dir; do ls /opt/AmneziaVPN/client/lib/* | while IFS=: read -r dir; do
sudo unlink $dir >> $LOG_FILE sudo unlink $dir >> $LOG_FILE
done done
@@ -64,11 +59,6 @@ if test -f /usr/share/pixmaps/$APP_NAME.png; then
fi fi
if command -v steamos-readonly &> /dev/null; then
sudo steamos-readonly enable >> $LOG_FILE
echo "steamos-readonly enabled" >> $LOG_FILE
fi
date >> $LOG_FILE date >> $LOG_FILE
echo "Service after uninstall status:" >> $LOG_FILE echo "Service after uninstall status:" >> $LOG_FILE
sudo systemctl status $APP_NAME >> $LOG_FILE sudo systemctl status $APP_NAME >> $LOG_FILE
Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 13 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB