mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-06-18 00:03:21 +03:00
Compare commits
19 Commits
fix/linux-
...
support_16
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
81050542cf | ||
|
|
38c0c1ec5d | ||
|
|
32b985942a | ||
|
|
1cfe6a1e50 | ||
|
|
132b6bc083 | ||
|
|
2b5c2cb021 | ||
|
|
729d6ee6d3 | ||
|
|
cd87253844 | ||
|
|
8cdaa0c2ee | ||
|
|
d563d66b28 | ||
|
|
37327e30d3 | ||
|
|
e0af63ce1c | ||
|
|
041d0fcab5 | ||
|
|
2a6960a4fb | ||
|
|
8481367fb6 | ||
|
|
70d6f9996d | ||
|
|
616d58e901 | ||
|
|
0bfbd82536 | ||
|
|
544ad4ba6e |
@@ -1,7 +1,36 @@
|
||||
cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
||||
|
||||
set(PROJECT AmneziaVPN)
|
||||
set(AMNEZIAVPN_VERSION 4.8.14.5)
|
||||
set(AMNEZIAVPN_VERSION 4.8.13.1)
|
||||
|
||||
include(cmake/conan_bootstrap.cmake)
|
||||
set(CMAKE_PROJECT_TOP_LEVEL_INCLUDES ${CMAKE_SOURCE_DIR}/cmake/conan_provider.cmake)
|
||||
|
||||
if(APPLE)
|
||||
get_property(generator_is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
||||
if (generator_is_multi_config)
|
||||
set(CONAN_INSTALL_BUILD_CONFIGURATIONS Release Debug MinSizeRel RelWithDebInfo)
|
||||
endif()
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "iOS")
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "14.0" CACHE STRING "" FORCE)
|
||||
set(CMAKE_OSX_ARCHITECTURES "arm64" CACHE STRING "" FORCE)
|
||||
elseif(MACOS_NE)
|
||||
set(CONAN_INSTALL_ARGS "--build=missing;-o=&:macos_ne=True" CACHE STRING "" FORCE)
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "13.0" CACHE STRING "" FORCE)
|
||||
set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64" CACHE STRING "" FORCE)
|
||||
else()
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "13.0" CACHE STRING "" FORCE)
|
||||
set(CMAKE_OSX_ARCHITECTURES "x86_64" CACHE STRING "" FORCE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ANDROID)
|
||||
set(CONAN_INSTALL_ARGS
|
||||
"--build=missing"
|
||||
"-o=*:page_16k=True"
|
||||
"-e=*:LDFLAGS=-Wl,-z,max-page-size=16384"
|
||||
CACHE STRING "" FORCE)
|
||||
endif()
|
||||
|
||||
project(${PROJECT} VERSION ${AMNEZIAVPN_VERSION}
|
||||
DESCRIPTION "AmneziaVPN"
|
||||
@@ -12,7 +41,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d")
|
||||
set(RELEASE_DATE "${CURRENT_DATE}")
|
||||
|
||||
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
||||
set(APP_ANDROID_VERSION_CODE 2118)
|
||||
set(APP_ANDROID_VERSION_CODE 2107)
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
set(MZ_PLATFORM_NAME "linux")
|
||||
@@ -32,16 +61,6 @@ set(QT_BUILD_TOOLS_WHEN_CROSS_COMPILING ON)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
if(APPLE)
|
||||
if(IOS)
|
||||
set(CMAKE_OSX_ARCHITECTURES "arm64")
|
||||
elseif(MACOS_NE)
|
||||
set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64")
|
||||
else()
|
||||
set(CMAKE_OSX_ARCHITECTURES "x86_64")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_subdirectory(client)
|
||||
|
||||
if(NOT IOS AND NOT ANDROID AND NOT MACOS_NE)
|
||||
@@ -61,9 +80,7 @@ if(WIN32 AND NOT IOS AND NOT ANDROID AND NOT MACOS_NE)
|
||||
set(CPACK_PACKAGE_VENDOR "AmneziaVPN")
|
||||
set(CPACK_PACKAGE_VERSION ${AMNEZIAVPN_VERSION})
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "AmneziaVPN client")
|
||||
set(AMNEZIA_LICENSE_TXT "${CMAKE_BINARY_DIR}/LICENSE.txt")
|
||||
configure_file("${CMAKE_SOURCE_DIR}/LICENSE" "${AMNEZIA_LICENSE_TXT}" COPYONLY)
|
||||
set(CPACK_RESOURCE_FILE_LICENSE "${AMNEZIA_LICENSE_TXT}")
|
||||
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE")
|
||||
set(CPACK_PACKAGE_INSTALL_DIRECTORY "AmneziaVPN")
|
||||
set(CPACK_PACKAGE_DIRECTORY "${CMAKE_BINARY_DIR}")
|
||||
set(CPACK_PACKAGE_EXECUTABLES "AmneziaVPN" "AmneziaVPN")
|
||||
|
||||
@@ -179,7 +179,7 @@ You may face compiling issues in QT Creator after you've worked in Android Studi
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the GNU General Public License v3.0 (see LICENSE) and also includes third-party components distributed under their own terms (see THIRD_PARTY_LICENSES.md).
|
||||
GPL v3.0
|
||||
|
||||
## Donate
|
||||
|
||||
|
||||
@@ -1,149 +0,0 @@
|
||||
# Third-Party Licenses
|
||||
|
||||
This project is licensed under the GNU General Public License v3.0.
|
||||
This file lists third-party software components used by this repository.
|
||||
Each component is distributed under its own license as linked below.
|
||||
|
||||
---
|
||||
|
||||
## QtKeychain
|
||||
|
||||
- Source: https://github.com/frankosterfeld/qtkeychain
|
||||
- License: BSD License
|
||||
- License Text: https://www.gnu.org/licenses/license-list.html#ModifiedBSD
|
||||
|
||||
---
|
||||
|
||||
## QSimpleCrypto
|
||||
|
||||
- Source: https://github.com/n1flh31mur/QSimpleCrypto
|
||||
- License: Apache License 2.0
|
||||
- License Text: https://github.com/n1flh31mur/QSimpleCrypto/blob/master/LICENSE
|
||||
|
||||
---
|
||||
|
||||
## SortFilterProxyModel
|
||||
|
||||
- Source: https://github.com/oKcerG/SortFilterProxyModel
|
||||
- License: MIT License
|
||||
- License Text: https://github.com/oKcerG/SortFilterProxyModel/blob/master/LICENSE
|
||||
|
||||
---
|
||||
|
||||
## QJsonStruct
|
||||
|
||||
- Source: https://github.com/Qv2ray/QJsonStruct
|
||||
- License: MIT License
|
||||
- License Text: https://github.com/Qv2ray/QJsonStruct/blob/master/LICENSE
|
||||
|
||||
---
|
||||
|
||||
## QR Code Generator (qrcodegen)
|
||||
|
||||
- Source: https://github.com/nayuki/QR-Code-generator
|
||||
- License: MIT License
|
||||
- License Text: https://www.nayuki.io/page/qr-code-generator-library
|
||||
|
||||
---
|
||||
|
||||
## Qt Gamepad
|
||||
|
||||
- Source: https://github.com/qt/qtgamepad
|
||||
- License: GNU General Public License v3.0 (GPL-3.0)
|
||||
- License Text: https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
|
||||
---
|
||||
|
||||
## AmneziaWG Apple (WireGuard)
|
||||
|
||||
- Source: https://github.com/amnezia-vpn/amneziawg-apple
|
||||
- License: MIT License
|
||||
- License Text: https://github.com/amnezia-vpn/amneziawg-apple/blob/master/COPYING
|
||||
|
||||
---
|
||||
|
||||
## AmneziaWG Android
|
||||
|
||||
- Source: https://github.com/amnezia-vpn/amneziawg-go
|
||||
- License: MIT License
|
||||
- License Text: https://github.com/amnezia-vpn/amneziawg-go/blob/master/LICENSE
|
||||
|
||||
---
|
||||
|
||||
## Xray Core
|
||||
|
||||
- Source: https://github.com/XTLS/Xray-core
|
||||
- License: Mozilla Public License 2.0 (MPL-2.0)
|
||||
- License Text: https://github.com/XTLS/Xray-core/blob/main/LICENSE
|
||||
|
||||
---
|
||||
|
||||
## Cloak
|
||||
|
||||
- Source: https://github.com/cbeuw/Cloak
|
||||
- License: GNU General Public License v3.0 (GPL-3.0)
|
||||
- License Text: https://github.com/cbeuw/Cloak/blob/master/LICENSE
|
||||
|
||||
---
|
||||
|
||||
## Shadowsocks
|
||||
|
||||
- Source: https://github.com/shadowsocks/shadowsocks-libev
|
||||
- License: GPL-3.0-or-later
|
||||
- License Text: http://www.gnu.org/licenses/
|
||||
|
||||
---
|
||||
|
||||
## OpenSSL
|
||||
|
||||
- Source: https://github.com/openssl/openssl
|
||||
- License: Apache License 2.0
|
||||
- License Text: https://www.openssl.org/source/license.html
|
||||
|
||||
---
|
||||
|
||||
## libssh
|
||||
|
||||
- Source: https://www.libssh.org/
|
||||
- License: GNU Lesser General Public License (LGPL)
|
||||
- License Text: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
|
||||
|
||||
---
|
||||
|
||||
## OpenVPNAdapter
|
||||
|
||||
- Source: https://github.com/ss-abramchuk/OpenVPNAdapter
|
||||
- License: GNU Affero General Public License v3.0 (AGPL-3.0)
|
||||
- License Text: https://github.com/ss-abramchuk/OpenVPNAdapter/blob/master/LICENSE
|
||||
|
||||
---
|
||||
|
||||
## Wintun
|
||||
|
||||
- Source: https://www.wintun.net/
|
||||
- License: Prebuilt Binaries License
|
||||
- License Text: https://github.com/WireGuard/wintun/blob/master/prebuilt-binaries-license.txt
|
||||
|
||||
---
|
||||
|
||||
## Mullvad Split Tunnel Driver
|
||||
|
||||
- Source: https://github.com/mullvad/win-split-tunnel
|
||||
- License: GNU General Public License v3.0 (GPL-3.0) and Mozilla Public License Version 2.0
|
||||
- License Text: https://github.com/mullvad/win-split-tunnel/blob/master/LICENSE-GPL.md https://github.com/mullvad/win-split-tunnel/blob/master/LICENSE-MPL.txt
|
||||
|
||||
---
|
||||
|
||||
## tun2socks
|
||||
|
||||
- Source: https://github.com/eycorsican/go-tun2socks
|
||||
- License: MIT License
|
||||
- License Text: https://github.com/eycorsican/go-tun2socks/blob/master/LICENSE
|
||||
|
||||
---
|
||||
|
||||
## TAP-Windows Driver
|
||||
|
||||
- Source: https://github.com/OpenVPN/tap-windows6
|
||||
- License: tap-windows6 license
|
||||
- License Text: https://github.com/OpenVPN/tap-windows6/blob/master/COPYING
|
||||
Submodule client/3rd-prebuilt updated: 51bb4703a4...b8c229288d
@@ -51,6 +51,13 @@ if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
|
||||
endif()
|
||||
|
||||
qt_standard_project_setup()
|
||||
|
||||
# 16KB page size support for Android 15+ / Google Play
|
||||
# Must be set before any targets are defined so it applies to all compiled-from-source libraries
|
||||
if(ANDROID)
|
||||
add_link_options("-Wl,-z,max-page-size=16384")
|
||||
endif()
|
||||
|
||||
qt_add_executable(${PROJECT} MANUAL_FINALIZATION)
|
||||
target_include_directories(${PROJECT} PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>
|
||||
@@ -78,7 +85,6 @@ set(AMNEZIAVPN_TS_FILES
|
||||
)
|
||||
|
||||
file(GLOB_RECURSE AMNEZIAVPN_TS_SOURCES *.qrc *.cpp *.h *.ui)
|
||||
list(FILTER AMNEZIAVPN_TS_SOURCES EXCLUDE REGEX "qtgamepad/examples")
|
||||
|
||||
qt_create_translation(AMNEZIAVPN_QM_FILES ${AMNEZIAVPN_TS_SOURCES} ${AMNEZIAVPN_TS_FILES})
|
||||
|
||||
@@ -218,13 +224,6 @@ if(NOT IOS AND NOT ANDROID AND NOT MACOS_NE)
|
||||
$<TARGET_FILE_DIR:${PROJECT}>
|
||||
COMMAND_EXPAND_LISTS
|
||||
)
|
||||
add_custom_command(
|
||||
TARGET ${PROJECT} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E $<IF:$<CONFIG:Debug>,copy_directory,true>
|
||||
${CMAKE_SOURCE_DIR}/client/3rd-prebuilt/deploy-prebuilt/${DEPLOY_PLATFORM_PATH}
|
||||
$<TARGET_FILE_DIR:${PROJECT}>
|
||||
COMMAND_EXPAND_LISTS
|
||||
)
|
||||
endif()
|
||||
|
||||
target_sources(${PROJECT} PRIVATE ${SOURCES} ${HEADERS} ${RESOURCES} ${QRC} ${I18NQRC})
|
||||
|
||||
@@ -109,16 +109,6 @@ void AmneziaApplication::init()
|
||||
// install filter on main window
|
||||
if (auto win = qobject_cast<QQuickWindow*>(obj)) {
|
||||
win->installEventFilter(this);
|
||||
#ifdef Q_OS_ANDROID
|
||||
QObject::connect(win, &QQuickWindow::sceneGraphError,
|
||||
[](QQuickWindow::SceneGraphError, const QString &msg) {
|
||||
qWarning() << "Scene graph error (suppressed):" << msg;
|
||||
});
|
||||
// Keep graphics context alive across hide/show cycles to avoid
|
||||
// eglSwapBuffers/makeCurrent being called on a context Android has reclaimed.
|
||||
win->setPersistentSceneGraph(true);
|
||||
win->setPersistentGraphics(true);
|
||||
#endif
|
||||
win->show();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
[versions]
|
||||
agp = "8.5.2"
|
||||
agp = "8.6.1"
|
||||
kotlin = "1.9.24"
|
||||
androidx-core = "1.13.1"
|
||||
androidx-activity = "1.9.1"
|
||||
androidx-annotation = "1.8.2"
|
||||
androidx-biometric = "1.2.0-alpha05"
|
||||
androidx-camera = "1.3.4"
|
||||
androidx-camera = "1.5.3"
|
||||
androidx-fragment = "1.8.2"
|
||||
androidx-security-crypto = "1.1.0-alpha06"
|
||||
androidx-datastore = "1.1.1"
|
||||
|
||||
@@ -75,8 +75,6 @@ private const val OPEN_FILE_ACTION_CODE = 3
|
||||
private const val CHECK_NOTIFICATION_PERMISSION_ACTION_CODE = 4
|
||||
|
||||
private const val PREFS_NOTIFICATION_PERMISSION_ASKED = "NOTIFICATION_PERMISSION_ASKED"
|
||||
private const val OPEN_FILE_AFTER_RESUME_DELAY_MS = 400L
|
||||
private const val KEY_PENDING_OPEN_FILE_URI = "pending_open_file_uri"
|
||||
|
||||
class AmneziaActivity : QtActivity() {
|
||||
|
||||
@@ -92,12 +90,10 @@ class AmneziaActivity : QtActivity() {
|
||||
|
||||
private val actionResultHandlers = mutableMapOf<Int, ActivityResultHandler>()
|
||||
private val permissionRequestHandlers = mutableMapOf<Int, PermissionRequestHandler>()
|
||||
|
||||
|
||||
private var isActivityResumed = false
|
||||
private var hasWindowFocus = false
|
||||
private val resumeHandler = Handler(Looper.getMainLooper())
|
||||
private var pendingOpenFileUri: String? = null
|
||||
private var openFileDeliveryScheduled = false
|
||||
|
||||
private val vpnServiceEventHandler: Handler by lazy(NONE) {
|
||||
object : Handler(Looper.getMainLooper()) {
|
||||
@@ -200,24 +196,14 @@ class AmneziaActivity : QtActivity() {
|
||||
doBindService()
|
||||
}
|
||||
)
|
||||
pendingOpenFileUri = savedInstanceState?.getString(KEY_PENDING_OPEN_FILE_URI)
|
||||
openFileDeliveryScheduled = false
|
||||
registerBroadcastReceivers()
|
||||
intent?.let(::processIntent)
|
||||
runBlocking { vpnProto = proto.await() }
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
pendingOpenFileUri?.let { outState.putString(KEY_PENDING_OPEN_FILE_URI, it) }
|
||||
}
|
||||
|
||||
private fun loadLibs() {
|
||||
listOf(
|
||||
"rsapss",
|
||||
"crypto_3",
|
||||
"ssl_3",
|
||||
"ssh"
|
||||
"rsapss"
|
||||
).forEach {
|
||||
loadSharedLibrary(this.applicationContext, it)
|
||||
}
|
||||
@@ -281,7 +267,6 @@ class AmneziaActivity : QtActivity() {
|
||||
hasWindowFocus = false
|
||||
// Cancel all pending operations when activity stops
|
||||
resumeHandler.removeCallbacksAndMessages(null)
|
||||
openFileDeliveryScheduled = false
|
||||
Log.d(TAG, "Stop Amnezia activity")
|
||||
doUnbindService()
|
||||
mainScope.launch {
|
||||
@@ -295,55 +280,43 @@ class AmneziaActivity : QtActivity() {
|
||||
super.onWindowFocusChanged(hasFocus)
|
||||
hasWindowFocus = hasFocus
|
||||
Log.d(TAG, "Window focus changed: hasFocus=$hasFocus")
|
||||
|
||||
|
||||
// Cancel pending operations if window loses focus
|
||||
if (!hasFocus) {
|
||||
// Cancel pending operations if window loses focus
|
||||
resumeHandler.removeCallbacksAndMessages(null)
|
||||
} else if (isActivityResumed && Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||
window.decorView.apply {
|
||||
invalidate()
|
||||
resumeHandler.postDelayed({
|
||||
if (isActivityResumed && hasWindowFocus && !isFinishing && !isDestroyed) {
|
||||
sendTouch(1f, 1f)
|
||||
}
|
||||
}, 50)
|
||||
resumeHandler.postDelayed({
|
||||
if (isActivityResumed && hasWindowFocus && !isFinishing && !isDestroyed) {
|
||||
sendTouch(2f, 2f)
|
||||
requestLayout()
|
||||
invalidate()
|
||||
}
|
||||
}, 150)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
|
||||
val deviceId = event.deviceId
|
||||
val keyCode = event.keyCode
|
||||
val pressed = event.action == KeyEvent.ACTION_DOWN
|
||||
val source = event.source
|
||||
|
||||
when (keyCode) {
|
||||
KeyEvent.KEYCODE_BUTTON_A,
|
||||
KeyEvent.KEYCODE_BUTTON_B,
|
||||
KeyEvent.KEYCODE_BUTTON_X,
|
||||
KeyEvent.KEYCODE_BUTTON_Y,
|
||||
KeyEvent.KEYCODE_BUTTON_START,
|
||||
KeyEvent.KEYCODE_BUTTON_SELECT -> {
|
||||
nativeGamepadKeyEvent(0, keyCode, pressed)
|
||||
if (deviceId < 0 && pressed) {
|
||||
when (keyCode) {
|
||||
KeyEvent.KEYCODE_BUTTON_A,
|
||||
KeyEvent.KEYCODE_BUTTON_B,
|
||||
KeyEvent.KEYCODE_BUTTON_X,
|
||||
KeyEvent.KEYCODE_BUTTON_Y,
|
||||
KeyEvent.KEYCODE_BUTTON_START,
|
||||
KeyEvent.KEYCODE_BUTTON_SELECT,
|
||||
KeyEvent.KEYCODE_DPAD_CENTER -> {
|
||||
nativeGamepadKeyEvent(0, keyCode, true)
|
||||
nativeGamepadKeyEvent(0, keyCode, false)
|
||||
return true
|
||||
}
|
||||
}
|
||||
KeyEvent.KEYCODE_DPAD_CENTER,
|
||||
KeyEvent.KEYCODE_DPAD_UP,
|
||||
KeyEvent.KEYCODE_DPAD_DOWN,
|
||||
KeyEvent.KEYCODE_DPAD_LEFT,
|
||||
KeyEvent.KEYCODE_DPAD_RIGHT -> {
|
||||
val syntheticKeyCode = if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) KeyEvent.KEYCODE_ENTER else keyCode
|
||||
val synthetic = KeyEvent(
|
||||
event.downTime, event.eventTime, event.action, syntheticKeyCode,
|
||||
event.repeatCount, event.metaState, -1, event.scanCode,
|
||||
event.flags, InputDevice.SOURCE_KEYBOARD
|
||||
)
|
||||
return super.dispatchKeyEvent(synthetic)
|
||||
}
|
||||
|
||||
// Real gamepad events (deviceId >= 0)
|
||||
if (deviceId >= 0) {
|
||||
val isGamepad = (source and InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD
|
||||
val isJoystick = (source and InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK
|
||||
val isDpad = (source and InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD
|
||||
if (isGamepad || isJoystick || isDpad) {
|
||||
nativeGamepadKeyEvent(deviceId, keyCode, pressed)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -353,18 +326,10 @@ class AmneziaActivity : QtActivity() {
|
||||
private external fun nativeGamepadKeyEvent(deviceId: Int, keyCode: Int, pressed: Boolean)
|
||||
|
||||
override fun onPause() {
|
||||
// Notify Qt to stop rendering BEFORE super.onPause() destroys the EGL surface.
|
||||
// Using a coroutine here would be too late — the surface is gone by the time
|
||||
// the coroutine runs. A direct synchronous call gives Qt's render thread the
|
||||
// best chance to process visible=false before surface destruction.
|
||||
if (qtInitialized.isCompleted) {
|
||||
QtAndroidController.onActivityPaused()
|
||||
}
|
||||
super.onPause()
|
||||
isActivityResumed = false
|
||||
// Cancel all pending operations when activity pauses
|
||||
resumeHandler.removeCallbacksAndMessages(null)
|
||||
openFileDeliveryScheduled = false
|
||||
Log.d(TAG, "Pause Amnezia activity")
|
||||
}
|
||||
|
||||
@@ -372,24 +337,6 @@ class AmneziaActivity : QtActivity() {
|
||||
super.onResume()
|
||||
isActivityResumed = true
|
||||
Log.d(TAG, "Resume Amnezia activity")
|
||||
if (qtInitialized.isCompleted) {
|
||||
QtAndroidController.onActivityResumed()
|
||||
}
|
||||
|
||||
if (pendingOpenFileUri != null && !openFileDeliveryScheduled) {
|
||||
val uri = pendingOpenFileUri!!
|
||||
openFileDeliveryScheduled = true
|
||||
resumeHandler.postDelayed({
|
||||
if (!isFinishing && !isDestroyed) {
|
||||
pendingOpenFileUri = null
|
||||
openFileDeliveryScheduled = false
|
||||
mainScope.launch {
|
||||
qtInitialized.await()
|
||||
QtAndroidController.onFileOpened(uri)
|
||||
}
|
||||
}
|
||||
}, OPEN_FILE_AFTER_RESUME_DELAY_MS)
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||
window.decorView.apply {
|
||||
@@ -401,13 +348,13 @@ class AmneziaActivity : QtActivity() {
|
||||
sendTouch(1f, 1f)
|
||||
}
|
||||
}, 100)
|
||||
|
||||
|
||||
resumeHandler.postDelayed({
|
||||
if (isActivityResumed && hasWindowFocus && !isFinishing && !isDestroyed) {
|
||||
sendTouch(2f, 2f)
|
||||
}
|
||||
}, 200)
|
||||
|
||||
|
||||
resumeHandler.postDelayed({
|
||||
if (isActivityResumed && hasWindowFocus && !isFinishing && !isDestroyed) {
|
||||
requestLayout()
|
||||
@@ -453,25 +400,25 @@ class AmneziaActivity : QtActivity() {
|
||||
ViewCompat.setOnApplyWindowInsetsListener(window.decorView) { view, windowInsets ->
|
||||
val imeInsets = windowInsets.getInsets(WindowInsetsCompat.Type.ime())
|
||||
val imeVisible = windowInsets.isVisible(WindowInsetsCompat.Type.ime())
|
||||
|
||||
|
||||
val imeHeight = if (imeVisible) imeInsets.bottom else 0
|
||||
|
||||
val density = resources.displayMetrics.density
|
||||
val imeHeightDp = (imeHeight / density).toInt()
|
||||
|
||||
|
||||
// Also track system bars (navigation bar, status bar) changes
|
||||
val systemBarsInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
val navBarHeight = systemBarsInsets.bottom
|
||||
val navBarHeightDp = (navBarHeight / density).toInt()
|
||||
val statusBarHeight = systemBarsInsets.top
|
||||
val statusBarHeightDp = (statusBarHeight / density).toInt()
|
||||
|
||||
|
||||
mainScope.launch {
|
||||
qtInitialized.await()
|
||||
QtAndroidController.onImeInsetsChanged(imeHeightDp)
|
||||
QtAndroidController.onSystemBarsInsetsChanged(navBarHeightDp, statusBarHeightDp)
|
||||
}
|
||||
|
||||
|
||||
// Return windowInsets instead of CONSUMED to allow proper handling
|
||||
windowInsets
|
||||
}
|
||||
@@ -807,13 +754,9 @@ class AmneziaActivity : QtActivity() {
|
||||
grantUriPermission(packageName, this, Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
}?.toString() ?: ""
|
||||
Log.v(TAG, "Open file: $uri")
|
||||
if (uri.isNotEmpty()) {
|
||||
pendingOpenFileUri = uri
|
||||
} else {
|
||||
mainScope.launch {
|
||||
qtInitialized.await()
|
||||
QtAndroidController.onFileOpened(uri)
|
||||
}
|
||||
mainScope.launch {
|
||||
qtInitialized.await()
|
||||
QtAndroidController.onFileOpened(uri)
|
||||
}
|
||||
}
|
||||
))
|
||||
@@ -842,7 +785,7 @@ class AmneziaActivity : QtActivity() {
|
||||
@Suppress("unused")
|
||||
fun getFd(fileName: String): Int {
|
||||
Log.v(TAG, "Get fd for $fileName")
|
||||
return blockingCall(Dispatchers.IO) {
|
||||
return blockingCall {
|
||||
try {
|
||||
pfd = contentResolver.openFileDescriptor(Uri.parse(fileName), "r")
|
||||
pfd?.fd ?: -1
|
||||
|
||||
@@ -33,10 +33,7 @@ class TvFilePicker : ComponentActivity() {
|
||||
return intent
|
||||
}
|
||||
}) {
|
||||
setResult(RESULT_OK, Intent().apply {
|
||||
data = it
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
})
|
||||
setResult(RESULT_OK, Intent().apply { data = it })
|
||||
finish()
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,4 @@ object QtAndroidController {
|
||||
|
||||
external fun onImeInsetsChanged(heightDp: Int)
|
||||
external fun onSystemBarsInsetsChanged(navBarHeightDp: Int, statusBarHeightDp: Int)
|
||||
|
||||
external fun onActivityPaused()
|
||||
external fun onActivityResumed()
|
||||
}
|
||||
@@ -8,76 +8,6 @@ include(${CLIENT_ROOT_DIR}/cmake/QSimpleCrypto.cmake)
|
||||
|
||||
include(${CLIENT_ROOT_DIR}/3rd/qrcodegen/qrcodegen.cmake)
|
||||
|
||||
set(LIBSSH_ROOT_DIR "${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/libssh/")
|
||||
set(OPENSSL_ROOT_DIR "${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/openssl/")
|
||||
|
||||
set(OPENSSL_LIBRARIES_DIR "${OPENSSL_ROOT_DIR}/lib")
|
||||
|
||||
if(WIN32)
|
||||
set(OPENSSL_INCLUDE_DIR "${OPENSSL_ROOT_DIR}/windows/include")
|
||||
if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8")
|
||||
set(LIBSSH_LIB_PATH "${LIBSSH_ROOT_DIR}/windows/x86_64/ssh.lib")
|
||||
set(LIBSSH_INCLUDE_DIR "${LIBSSH_ROOT_DIR}/windows/x86_64")
|
||||
set(OPENSSL_LIB_SSL_PATH "${OPENSSL_ROOT_DIR}/windows/win64/libssl.lib")
|
||||
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/windows/win64/libcrypto.lib")
|
||||
else()
|
||||
set(LIBSSH_LIB_PATH "${LIBSSH_ROOT_DIR}/windows/x86/ssh.lib")
|
||||
set(LIBSSH_INCLUDE_DIR "${LIBSSH_ROOT_DIR}/windows/x86")
|
||||
set(OPENSSL_LIB_SSL_PATH "${OPENSSL_ROOT_DIR}/windows/win32/libssl.lib")
|
||||
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/windows/win32/libcrypto.lib")
|
||||
endif()
|
||||
elseif(APPLE AND NOT IOS)
|
||||
if(MACOS_NE)
|
||||
set(LIBSSH_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/universal2/libssh.a")
|
||||
set(ZLIB_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/universal2/libz.a")
|
||||
set(LIBSSH_INCLUDE_DIR "${LIBSSH_ROOT_DIR}/macos/universal2")
|
||||
else()
|
||||
set(LIBSSH_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/x86_64/libssh.a")
|
||||
set(ZLIB_LIB_PATH "${LIBSSH_ROOT_DIR}/macos/x86_64/libz.a")
|
||||
set(LIBSSH_INCLUDE_DIR "${LIBSSH_ROOT_DIR}/macos/x86_64")
|
||||
endif()
|
||||
set(OPENSSL_INCLUDE_DIR "${OPENSSL_ROOT_DIR}/macos/include")
|
||||
set(OPENSSL_LIB_SSL_PATH "${OPENSSL_ROOT_DIR}/macos/lib/libssl.a")
|
||||
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/macos/lib/libcrypto.a")
|
||||
elseif(IOS)
|
||||
set(LIBSSH_INCLUDE_DIR "${LIBSSH_ROOT_DIR}/ios/arm64")
|
||||
set(LIBSSH_LIB_PATH "${LIBSSH_ROOT_DIR}/ios/arm64/libssh.a")
|
||||
set(ZLIB_LIB_PATH "${LIBSSH_ROOT_DIR}/ios/arm64/libz.a")
|
||||
set(OPENSSL_INCLUDE_DIR "${OPENSSL_ROOT_DIR}/ios/iphone/include")
|
||||
set(OPENSSL_LIB_SSL_PATH "${OPENSSL_ROOT_DIR}/ios/iphone/lib/libssl.a")
|
||||
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/ios/iphone/lib/libcrypto.a")
|
||||
elseif(ANDROID)
|
||||
set(abi ${CMAKE_ANDROID_ARCH_ABI})
|
||||
set(LIBSSH_INCLUDE_DIR "${LIBSSH_ROOT_DIR}/android/${abi}")
|
||||
set(LIBSSH_LIB_PATH "${LIBSSH_ROOT_DIR}/android/${abi}/libssh.so")
|
||||
set(OPENSSL_INCLUDE_DIR "${OPENSSL_ROOT_DIR}/android/include")
|
||||
set(OPENSSL_LIB_SSL_PATH "${OPENSSL_ROOT_DIR}/android/${abi}/libssl.a")
|
||||
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/android/${abi}/libcrypto.a")
|
||||
set(OPENSSL_LIBRARIES_DIR "${OPENSSL_ROOT_DIR}/android/${abi}")
|
||||
elseif(LINUX)
|
||||
set(LIBSSH_INCLUDE_DIR "${LIBSSH_ROOT_DIR}/linux/x86_64")
|
||||
set(ZLIB_LIB_PATH "${LIBSSH_ROOT_DIR}/linux/x86_64/libz.a")
|
||||
set(LIBSSH_LIB_PATH "${LIBSSH_ROOT_DIR}/linux/x86_64/libssh.a")
|
||||
set(OPENSSL_INCLUDE_DIR "${OPENSSL_ROOT_DIR}/linux/include")
|
||||
set(OPENSSL_LIB_SSL_PATH "${OPENSSL_ROOT_DIR}/linux/x86_64/libssl.a")
|
||||
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/linux/x86_64/libcrypto.a")
|
||||
endif()
|
||||
|
||||
file(COPY ${OPENSSL_LIB_SSL_PATH} ${OPENSSL_LIB_CRYPTO_PATH}
|
||||
DESTINATION ${OPENSSL_LIBRARIES_DIR})
|
||||
|
||||
set(OPENSSL_USE_STATIC_LIBS TRUE)
|
||||
|
||||
set(LIBS ${LIBS}
|
||||
${LIBSSH_LIB_PATH}
|
||||
${ZLIB_LIB_PATH}
|
||||
)
|
||||
|
||||
set(LIBS ${LIBS}
|
||||
${OPENSSL_LIB_SSL_PATH}
|
||||
${OPENSSL_LIB_CRYPTO_PATH}
|
||||
)
|
||||
|
||||
add_compile_definitions(_WINSOCKAPI_)
|
||||
|
||||
set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
|
||||
@@ -106,12 +36,20 @@ endif()
|
||||
set(LIBS ${LIBS} qt6keychain)
|
||||
|
||||
include_directories(
|
||||
${OPENSSL_INCLUDE_DIR}
|
||||
${LIBSSH_INCLUDE_DIR}/include
|
||||
${LIBSSH_ROOT_DIR}/include
|
||||
${CLIENT_ROOT_DIR}/3rd/libssh/include
|
||||
${CLIENT_ROOT_DIR}/3rd/QSimpleCrypto/src/include
|
||||
${CLIENT_ROOT_DIR}/3rd/qtkeychain/qtkeychain
|
||||
${CMAKE_CURRENT_BINARY_DIR}/3rd/qtkeychain
|
||||
${CMAKE_CURRENT_BINARY_DIR}/3rd/libssh/include
|
||||
)
|
||||
|
||||
if(ANDROID)
|
||||
find_package(android-openssl REQUIRED)
|
||||
# CMakeConfigDeps doesn't propagate include dirs onto IMPORTED targets;
|
||||
# use the legacy variable that IS correctly populated by the config file.
|
||||
include_directories(${android-openssl_INCLUDE_DIRS})
|
||||
else()
|
||||
find_package(openssl REQUIRED)
|
||||
endif()
|
||||
list(APPEND LIBS OpenSSL::SSL OpenSSL::Crypto)
|
||||
|
||||
find_package(libssh REQUIRED)
|
||||
list(APPEND LIBS ssh::ssh)
|
||||
|
||||
@@ -42,18 +42,14 @@ set(SOURCES ${SOURCES}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/core/installedAppsImageProvider.cpp
|
||||
)
|
||||
|
||||
foreach(abi IN ITEMS ${QT_ANDROID_ABIS})
|
||||
set_property(TARGET ${PROJECT} PROPERTY QT_ANDROID_EXTRA_LIBS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/amneziawg/android/${abi}/libwg-go.so
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/android/${abi}/libck-ovpn-plugin.so
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/android/${abi}/libovpn3.so
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/android/${abi}/libovpnutil.so
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/android/${abi}/librsapss.so
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openssl/android/${abi}/libcrypto_3.so
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openssl/android/${abi}/libssl_3.so
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/libssh/android/${abi}/libssh.so
|
||||
)
|
||||
endforeach()
|
||||
|
||||
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/xray/android/libxray.aar
|
||||
DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/android/xray/libXray)
|
||||
find_package(awg-android REQUIRED)
|
||||
set(LIBS ${LIBS} amnezia::awg-android)
|
||||
set_property(TARGET ${PROJECT} APPEND PROPERTY QT_ANDROID_EXTRA_LIBS ${AMNEZIA_ANDROID_LIBWG_PATH} ${AMNEZIA_ANDROID_LIBWG_QUICK_PATH})
|
||||
|
||||
find_package(amnezia-libxray REQUIRED)
|
||||
file(COPY ${AMNEZIA_LIBXRAY_PATH} DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/android/xray/libXray)
|
||||
|
||||
find_package(openvpn-pt-android REQUIRED)
|
||||
set(LIBS ${LIBS} amnezia::openvpn-pt-android)
|
||||
set_property(TARGET ${PROJECT} APPEND PROPERTY QT_ANDROID_EXTRA_LIBS ${OPENVPN_PT_ANDROID_LIBCK_OVPN_PLUGIN_PATH})
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
message("Client iOS build")
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 13.0)
|
||||
set(APPLE_PROJECT_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
||||
|
||||
|
||||
enable_language(OBJC)
|
||||
enable_language(OBJCXX)
|
||||
enable_language(Swift)
|
||||
@@ -137,11 +135,3 @@ set_property(TARGET ${PROJECT} APPEND PROPERTY RESOURCE
|
||||
|
||||
add_subdirectory(ios/networkextension)
|
||||
add_dependencies(${PROJECT} networkextension)
|
||||
|
||||
set_property(TARGET ${PROJECT} PROPERTY XCODE_EMBED_FRAMEWORKS
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-ios/OpenVPNAdapter.framework"
|
||||
)
|
||||
|
||||
set(CMAKE_XCODE_ATTRIBUTE_FRAMEWORK_SEARCH_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-ios/)
|
||||
target_link_libraries("networkextension" PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-ios/OpenVPNAdapter.framework")
|
||||
|
||||
|
||||
@@ -23,9 +23,6 @@ set_target_properties(${PROJECT} PROPERTIES
|
||||
MACOSX_BUNDLE_SHORT_VERSION_STRING "${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH}"
|
||||
MACOSX_BUNDLE_BUNDLE_VERSION "${CMAKE_PROJECT_VERSION_TWEAK}"
|
||||
)
|
||||
set(CMAKE_OSX_ARCHITECTURES "x86_64" CACHE INTERNAL "" FORCE)
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15)
|
||||
|
||||
|
||||
set(HEADERS ${HEADERS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ui/macos_util.h
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
message("Client ==> MacOS NE build")
|
||||
|
||||
set_target_properties(${PROJECT} PROPERTIES MACOSX_BUNDLE TRUE)
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15)
|
||||
|
||||
set(APPLE_PROJECT_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
||||
|
||||
@@ -152,19 +151,6 @@ message(${QtCore_location})
|
||||
|
||||
get_filename_component(QT_BIN_DIR_DETECTED "${QtCore_location}/../../../../../bin" ABSOLUTE)
|
||||
|
||||
set_property(TARGET ${PROJECT} PROPERTY XCODE_EMBED_FRAMEWORKS
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-macos/OpenVPNAdapter.framework"
|
||||
)
|
||||
|
||||
set(CMAKE_XCODE_ATTRIBUTE_FRAMEWORK_SEARCH_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-macos)
|
||||
target_link_libraries("AmneziaVPNNetworkExtension" PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/3rd-prebuilt/3rd-prebuilt/openvpn/apple/OpenVPNAdapter-macos/OpenVPNAdapter.framework")
|
||||
|
||||
add_custom_command(TARGET ${PROJECT} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory
|
||||
$<TARGET_BUNDLE_DIR:AmneziaVPN>/Contents/Frameworks
|
||||
COMMAND /usr/bin/find "$<TARGET_BUNDLE_DIR:AmneziaVPN>/Contents/Frameworks/OpenVPNAdapter.framework" -name "*.sha256" -delete
|
||||
COMMAND /usr/bin/codesign --force --sign "Apple Distribution: Privacy Technologies OU"
|
||||
"$<TARGET_BUNDLE_DIR:AmneziaVPN>/Contents/Frameworks/OpenVPNAdapter.framework/Versions/Current/OpenVPNAdapter"
|
||||
COMMAND ${QT_BIN_DIR_DETECTED}/macdeployqt $<TARGET_BUNDLE_DIR:AmneziaVPN> -appstore-compliant -qmldir=${CMAKE_CURRENT_SOURCE_DIR}
|
||||
COMMENT "Signing OpenVPNAdapter framework"
|
||||
)
|
||||
|
||||
@@ -10,10 +10,8 @@ namespace apiDefs
|
||||
AmneziaFreeV3,
|
||||
AmneziaPremiumV1,
|
||||
AmneziaPremiumV2,
|
||||
AmneziaTrialV2,
|
||||
SelfHosted,
|
||||
ExternalPremium,
|
||||
ExternalTrial
|
||||
ExternalPremium
|
||||
};
|
||||
|
||||
enum ConfigSource {
|
||||
@@ -34,7 +32,6 @@ namespace apiDefs
|
||||
constexpr QLatin1String stackType("stack_type");
|
||||
constexpr QLatin1String serviceType("service_type");
|
||||
constexpr QLatin1String cliVersion("cli_version");
|
||||
constexpr QLatin1String cliName("cli_name");
|
||||
constexpr QLatin1String supportedProtocols("supported_protocols");
|
||||
|
||||
constexpr QLatin1String vpnKey("vpn_key");
|
||||
|
||||
@@ -58,24 +58,18 @@ apiDefs::ConfigType apiUtils::getConfigType(const QJsonObject &serverConfigObjec
|
||||
};
|
||||
case apiDefs::ConfigSource::AmneziaGateway: {
|
||||
constexpr QLatin1String servicePremium("amnezia-premium");
|
||||
constexpr QLatin1String serviceTrial("amnezia-trial");
|
||||
constexpr QLatin1String serviceFree("amnezia-free");
|
||||
constexpr QLatin1String serviceExternalPremium("external-premium");
|
||||
constexpr QLatin1String serviceExternalTrial("external-trial");
|
||||
|
||||
auto apiConfigObject = serverConfigObject.value(apiDefs::key::apiConfig).toObject();
|
||||
auto serviceType = apiConfigObject.value(apiDefs::key::serviceType).toString();
|
||||
|
||||
if (serviceType == servicePremium) {
|
||||
return apiDefs::ConfigType::AmneziaPremiumV2;
|
||||
} else if (serviceType == serviceTrial) {
|
||||
return apiDefs::ConfigType::AmneziaTrialV2;
|
||||
} else if (serviceType == serviceFree) {
|
||||
return apiDefs::ConfigType::AmneziaFreeV3;
|
||||
} else if (serviceType == serviceExternalPremium) {
|
||||
return apiDefs::ConfigType::ExternalPremium;
|
||||
} else if (serviceType == serviceExternalTrial) {
|
||||
return apiDefs::ConfigType::ExternalTrial;
|
||||
}
|
||||
}
|
||||
default: {
|
||||
@@ -96,7 +90,6 @@ amnezia::ErrorCode apiUtils::checkNetworkReplyErrors(const QList<QSslError> &ssl
|
||||
const int httpStatusCodeConflict = 409;
|
||||
const int httpStatusCodeNotFound = 404;
|
||||
const int httpStatusCodeNotImplemented = 501;
|
||||
const int httpStatusCodeUnprocessableEntity = 422;
|
||||
|
||||
if (!sslErrors.empty()) {
|
||||
qDebug().noquote() << sslErrors;
|
||||
@@ -129,8 +122,6 @@ amnezia::ErrorCode apiUtils::checkNetworkReplyErrors(const QList<QSslError> &ssl
|
||||
return amnezia::ErrorCode::ApiNotFoundError;
|
||||
} else if (httpStatusFromBody == httpStatusCodeNotImplemented) {
|
||||
return amnezia::ErrorCode::ApiUpdateRequestError;
|
||||
} else if (httpStatusFromBody == httpStatusCodeUnprocessableEntity) {
|
||||
return amnezia::ErrorCode::ApiSubscriptionExpiredError;
|
||||
}
|
||||
return amnezia::ErrorCode::ApiConfigDownloadError;
|
||||
}
|
||||
@@ -142,8 +133,7 @@ amnezia::ErrorCode apiUtils::checkNetworkReplyErrors(const QList<QSslError> &ssl
|
||||
bool apiUtils::isPremiumServer(const QJsonObject &serverConfigObject)
|
||||
{
|
||||
static const QSet<apiDefs::ConfigType> premiumTypes = { apiDefs::ConfigType::AmneziaPremiumV1, apiDefs::ConfigType::AmneziaPremiumV2,
|
||||
apiDefs::ConfigType::AmneziaTrialV2, apiDefs::ConfigType::ExternalPremium,
|
||||
apiDefs::ConfigType::ExternalTrial };
|
||||
apiDefs::ConfigType::ExternalPremium };
|
||||
return premiumTypes.contains(getConfigType(serverConfigObject));
|
||||
}
|
||||
|
||||
@@ -187,9 +177,7 @@ QString apiUtils::getPremiumV1VpnKey(const QJsonObject &serverConfigObject)
|
||||
|
||||
QString apiUtils::getPremiumV2VpnKey(const QJsonObject &serverConfigObject)
|
||||
{
|
||||
auto configType = apiUtils::getConfigType(serverConfigObject);
|
||||
if (configType != apiDefs::ConfigType::AmneziaPremiumV2 && configType != apiDefs::ConfigType::AmneziaTrialV2
|
||||
&& configType != apiDefs::ConfigType::ExternalPremium && configType != apiDefs::ConfigType::ExternalTrial) {
|
||||
if (apiUtils::getConfigType(serverConfigObject) != apiDefs::ConfigType::AmneziaPremiumV2) {
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
@@ -135,7 +135,7 @@ void CoreController::initControllers()
|
||||
new SettingsController(m_serversModel, m_containersModel, m_languageModel, m_sitesModel, m_appSplitTunnelingModel, m_settings));
|
||||
m_engine->rootContext()->setContextProperty("SettingsController", m_settingsController.get());
|
||||
|
||||
m_sitesController.reset(new SitesController(m_settings, m_sitesModel));
|
||||
m_sitesController.reset(new SitesController(m_settings, m_vpnConnection, m_sitesModel));
|
||||
m_engine->rootContext()->setContextProperty("SitesController", m_sitesController.get());
|
||||
|
||||
m_allowedDnsController.reset(new AllowedDnsController(m_settings, m_allowedDnsModel));
|
||||
@@ -153,8 +153,6 @@ void CoreController::initControllers()
|
||||
|
||||
m_apiConfigsController.reset(new ApiConfigsController(m_serversModel, m_apiServicesModel, m_settings));
|
||||
m_engine->rootContext()->setContextProperty("ApiConfigsController", m_apiConfigsController.get());
|
||||
connect(m_apiConfigsController.get(), &ApiConfigsController::subscriptionRefreshNeeded,
|
||||
this, [this]() { m_apiSettingsController->getAccountInfo(false); });
|
||||
|
||||
m_apiNewsController.reset(new ApiNewsController(m_newsModel, m_settings, m_serversModel, this));
|
||||
m_engine->rootContext()->setContextProperty("ApiNewsController", m_apiNewsController.get());
|
||||
@@ -370,11 +368,7 @@ void CoreController::initPrepareConfigHandler()
|
||||
return;
|
||||
}
|
||||
|
||||
m_installController->validateConfig();
|
||||
});
|
||||
|
||||
connect(m_installController.get(), &InstallController::configValidated, this, [this](bool isValid) {
|
||||
if (!isValid) {
|
||||
if (!m_installController->isConfigValid()) {
|
||||
emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -46,7 +46,6 @@ namespace
|
||||
constexpr int httpStatusCodeConflict = 409;
|
||||
|
||||
constexpr int httpStatusCodeNotImplemented = 501;
|
||||
constexpr int httpStatusCodeUnprocessableEntity = 422;
|
||||
}
|
||||
|
||||
GatewayController::GatewayController(const QString &gatewayEndpoint, const bool isDevEnvironment, const int requestTimeoutMsecs,
|
||||
@@ -338,9 +337,6 @@ QStringList GatewayController::getProxyUrls(const QString &serviceType, const QS
|
||||
} else {
|
||||
baseUrls = QString(PROD_S3_ENDPOINT).split(", ");
|
||||
}
|
||||
std::random_device randomDevice;
|
||||
std::mt19937 generator(randomDevice());
|
||||
std::shuffle(baseUrls.begin(), baseUrls.end(), generator);
|
||||
|
||||
QByteArray key = m_isDevEnvironment ? DEV_AGW_PUBLIC_KEY : PROD_AGW_PUBLIC_KEY;
|
||||
|
||||
@@ -452,8 +448,6 @@ bool GatewayController::shouldBypassProxy(const QNetworkReply::NetworkError &rep
|
||||
}
|
||||
} else if (httpStatus == httpStatusCodeConflict) {
|
||||
return false;
|
||||
} else if (httpStatus == httpStatusCodeUnprocessableEntity) {
|
||||
return false;
|
||||
} else if (replyError != QNetworkReply::NetworkError::NoError) {
|
||||
qDebug() << replyError;
|
||||
return true;
|
||||
|
||||
@@ -110,21 +110,14 @@ set_property(TARGET networkextension APPEND PROPERTY RESOURCE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/PrivacyInfo.xcprivacy
|
||||
)
|
||||
|
||||
## Build wireguard-go-version.h
|
||||
execute_process(
|
||||
COMMAND go list -m golang.zx2c4.com/wireguard
|
||||
WORKING_DIRECTORY ${CLIENT_ROOT_DIR}/3rd/wireguard-apple/Sources/WireGuardKitGo
|
||||
OUTPUT_VARIABLE WG_VERSION_FULL
|
||||
)
|
||||
string(REGEX REPLACE ".*v\([0-9.]*\).*" "\\1" WG_VERSION_STRING 1.1.1)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/wireguard-go-version.h.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/wireguard-go-version.h)
|
||||
target_sources(networkextension PRIVATE
|
||||
${CMAKE_CURRENT_BINARY_DIR}/wireguard-go-version.h)
|
||||
|
||||
target_include_directories(networkextension PRIVATE ${CLIENT_ROOT_DIR})
|
||||
target_include_directories(networkextension PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
target_link_libraries(networkextension PRIVATE ${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/wireguard/ios/arm64/libwg-go.a)
|
||||
find_package(openvpnadapter REQUIRED)
|
||||
target_link_libraries(networkextension PRIVATE amnezia::openvpnadapter)
|
||||
|
||||
target_link_libraries(networkextension PRIVATE ${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/xray/HevSocks5Tunnel.xcframework)
|
||||
find_package(awg-apple REQUIRED)
|
||||
target_link_libraries(networkextension PRIVATE amnezia::awg-apple)
|
||||
|
||||
find_package(hev-socks5-tunnel REQUIRED)
|
||||
target_link_libraries(networkextension PRIVATE heiher::hev-socks5-tunnel)
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
#ifndef WIREGUARD_GO_VERSION
|
||||
#define WIREGUARD_GO_VERSION "@WG_VERSION_STRING@"
|
||||
#endif // WIREGUARD_GO_VERSION
|
||||
@@ -114,25 +114,14 @@ set_property(TARGET AmneziaVPNNetworkExtension APPEND PROPERTY RESOURCE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/PrivacyInfo.xcprivacy
|
||||
)
|
||||
|
||||
## Build wireguard-go-version.h
|
||||
execute_process(
|
||||
COMMAND go list -m golang.zx2c4.com/wireguard
|
||||
WORKING_DIRECTORY ${CLIENT_ROOT_DIR}/3rd/wireguard-apple/Sources/WireGuardKitGo
|
||||
OUTPUT_VARIABLE WG_VERSION_FULL
|
||||
)
|
||||
string(REGEX REPLACE ".*v\([0-9.]*\).*" "\\1" WG_VERSION_STRING 1.1.1)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/wireguard-go-version.h.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/wireguard-go-version.h)
|
||||
target_sources(AmneziaVPNNetworkExtension PRIVATE
|
||||
${CMAKE_CURRENT_BINARY_DIR}/wireguard-go-version.h)
|
||||
|
||||
target_include_directories(AmneziaVPNNetworkExtension PRIVATE ${CLIENT_ROOT_DIR})
|
||||
target_include_directories(AmneziaVPNNetworkExtension PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
target_link_libraries(AmneziaVPNNetworkExtension PRIVATE ${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/wireguard/macos/universal2/libwg-go.a)
|
||||
find_package(openvpnadapter REQUIRED)
|
||||
target_link_libraries(AmneziaVPNNetworkExtension PRIVATE amnezia::openvpnadapter)
|
||||
|
||||
message(${CLIENT_ROOT_DIR})
|
||||
message(${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/xray/HevSocks5Tunnel.xcframework/macos-arm64_x86_64/libhev-socks5-tunnel.a)
|
||||
target_link_libraries(AmneziaVPNNetworkExtension PRIVATE ${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/xray/HevSocks5Tunnel.xcframework/macos-arm64_x86_64/libhev-socks5-tunnel.a)
|
||||
find_package(awg-apple REQUIRED)
|
||||
target_link_libraries(AmneziaVPNNetworkExtension PRIVATE amnezia::awg-apple)
|
||||
|
||||
target_include_directories(AmneziaVPNNetworkExtension PRIVATE ${CLIENT_ROOT_DIR}/3rd-prebuilt/3rd-prebuilt/xray/HevSocks5Tunnel.xcframework/macos-arm64_x86_64/Headers)
|
||||
find_package(hev-socks5-tunnel REQUIRED)
|
||||
target_link_libraries(AmneziaVPNNetworkExtension PRIVATE heiher::hev-socks5-tunnel)
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
#ifndef WIREGUARD_GO_VERSION
|
||||
#define WIREGUARD_GO_VERSION "@WG_VERSION_STRING@"
|
||||
#endif // WIREGUARD_GO_VERSION
|
||||
@@ -75,7 +75,6 @@ int main(int argc, char *argv[])
|
||||
|
||||
qInfo().noquote() << QString("Started %1 version %2 %3").arg(APPLICATION_NAME, APP_VERSION, GIT_COMMIT_HASH);
|
||||
qInfo().noquote() << QString("%1 (%2)").arg(QSysInfo::prettyProductName(), QSysInfo::currentCpuArchitecture());
|
||||
qInfo().noquote() << QString("SSL backend: %1").arg(QSslSocket::sslLibraryVersionString());
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
|
||||
@@ -101,9 +101,7 @@ bool AndroidController::initialize()
|
||||
{"onAuthResult", "(Z)V", reinterpret_cast<void *>(onAuthResult)},
|
||||
{"decodeQrCode", "(Ljava/lang/String;)Z", reinterpret_cast<bool *>(decodeQrCode)},
|
||||
{"onImeInsetsChanged", "(I)V", reinterpret_cast<void *>(onImeInsetsChanged)},
|
||||
{"onSystemBarsInsetsChanged", "(II)V", reinterpret_cast<void *>(onSystemBarsInsetsChanged)},
|
||||
{"onActivityPaused", "()V", reinterpret_cast<void *>(onActivityPaused)},
|
||||
{"onActivityResumed", "()V", reinterpret_cast<void *>(onActivityResumed)}
|
||||
{"onSystemBarsInsetsChanged", "(II)V", reinterpret_cast<void *>(onSystemBarsInsetsChanged)}
|
||||
};
|
||||
|
||||
QJniEnvironment env;
|
||||
@@ -560,22 +558,3 @@ void AndroidController::onSystemBarsInsetsChanged(JNIEnv *env, jobject thiz, jin
|
||||
emit AndroidController::instance()->systemBarsInsetsChanged(navBarHeightDp, statusBarHeightDp);
|
||||
}
|
||||
|
||||
// static
|
||||
void AndroidController::onActivityPaused(JNIEnv *env, jobject thiz)
|
||||
{
|
||||
Q_UNUSED(env);
|
||||
Q_UNUSED(thiz);
|
||||
|
||||
emit AndroidController::instance()->activityPaused();
|
||||
}
|
||||
|
||||
// static
|
||||
void AndroidController::onActivityResumed(JNIEnv *env, jobject thiz)
|
||||
{
|
||||
Q_UNUSED(env);
|
||||
Q_UNUSED(thiz);
|
||||
|
||||
emit AndroidController::instance()->activityResumed();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -75,8 +75,6 @@ signals:
|
||||
void authenticationResult(bool result);
|
||||
void imeInsetsChanged(int heightDp);
|
||||
void systemBarsInsetsChanged(int navBarHeightDp, int statusBarHeightDp);
|
||||
void activityPaused();
|
||||
void activityResumed();
|
||||
|
||||
private:
|
||||
bool isWaitingStatus = true;
|
||||
@@ -107,8 +105,6 @@ private:
|
||||
static bool decodeQrCode(JNIEnv *env, jobject thiz, jstring data);
|
||||
static void onImeInsetsChanged(JNIEnv *env, jobject thiz, jint heightDp);
|
||||
static void onSystemBarsInsetsChanged(JNIEnv *env, jobject thiz, jint navBarHeightDp, jint statusBarHeightDp);
|
||||
static void onActivityPaused(JNIEnv *env, jobject thiz);
|
||||
static void onActivityResumed(JNIEnv *env, jobject thiz);
|
||||
|
||||
template <typename Ret, typename ...Args>
|
||||
static auto callActivityMethod(const char *methodName, const char *signature, Args &&...args);
|
||||
|
||||
@@ -126,7 +126,8 @@ extension PacketTunnelProvider {
|
||||
}
|
||||
|
||||
vpnReachability.startTracking { [weak self] status in
|
||||
self?.handleOpenVPNReachabilityChange(status)
|
||||
guard status == .reachableViaWiFi else { return }
|
||||
self?.ovpnAdapter?.reconnect(afterTimeInterval: 5)
|
||||
}
|
||||
|
||||
startHandler = completionHandler
|
||||
|
||||
@@ -21,44 +21,6 @@ extension Constants {
|
||||
}
|
||||
|
||||
extension PacketTunnelProvider {
|
||||
private func applyXraySplitTunnel(_ xrayConfig: XrayConfig,
|
||||
settings: NEPacketTunnelNetworkSettings) {
|
||||
guard let splitTunnelType = xrayConfig.splitTunnelType else {
|
||||
return
|
||||
}
|
||||
|
||||
guard let splitTunnelSites = xrayConfig.splitTunnelSites else {
|
||||
xrayLog(.error, message: "Split tunnel sites are not set")
|
||||
return
|
||||
}
|
||||
|
||||
if splitTunnelType == 1 {
|
||||
var ipv4IncludedRoutes = [NEIPv4Route]()
|
||||
|
||||
for allowedIPString in splitTunnelSites {
|
||||
if let allowedIP = IPAddressRange(from: allowedIPString) {
|
||||
ipv4IncludedRoutes.append(NEIPv4Route(
|
||||
destinationAddress: "\(allowedIP.address)",
|
||||
subnetMask: "\(allowedIP.subnetMask())"))
|
||||
}
|
||||
}
|
||||
|
||||
settings.ipv4Settings?.includedRoutes = ipv4IncludedRoutes
|
||||
} else if splitTunnelType == 2 {
|
||||
var ipv4ExcludedRoutes = [NEIPv4Route]()
|
||||
|
||||
for excludedIPString in splitTunnelSites {
|
||||
if let excludedIP = IPAddressRange(from: excludedIPString) {
|
||||
ipv4ExcludedRoutes.append(NEIPv4Route(
|
||||
destinationAddress: "\(excludedIP.address)",
|
||||
subnetMask: "\(excludedIP.subnetMask())"))
|
||||
}
|
||||
}
|
||||
|
||||
settings.ipv4Settings?.excludedRoutes = ipv4ExcludedRoutes
|
||||
}
|
||||
}
|
||||
|
||||
func startXray(completionHandler: @escaping (Error?) -> Void) {
|
||||
|
||||
// Xray configuration
|
||||
@@ -110,7 +72,6 @@ extension PacketTunnelProvider {
|
||||
settings.dnsSettings = !dnsArray.isEmpty
|
||||
? NEDNSSettings(servers: dnsArray)
|
||||
: NEDNSSettings(servers: ["1.1.1.1"])
|
||||
applyXraySplitTunnel(xrayConfig, settings: settings)
|
||||
|
||||
let xrayConfigData = xrayConfig.config.data(using: .utf8)
|
||||
|
||||
|
||||
@@ -41,15 +41,10 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||
var ovpnAdapter: OpenVPNAdapter?
|
||||
private lazy var openVPNPacketFlowAdapter = PacketTunnelFlowAdapter(flow: packetFlow)
|
||||
private let pathMonitorQueue = DispatchQueue(label: Constants.processQueueName + ".path-monitor")
|
||||
private let networkChangeQueue = DispatchQueue(label: Constants.processQueueName + ".network-change")
|
||||
private let pathMonitor = NWPathMonitor()
|
||||
private var didReceiveInitialPathUpdate = false
|
||||
private var currentPath: Network.NWPath?
|
||||
private var currentPathSignature: String?
|
||||
private var pendingOpenVPNReconnectWorkItem: DispatchWorkItem?
|
||||
private var pendingNetworkChangeWorkItem: DispatchWorkItem?
|
||||
private var isApplyingNetworkChange = false
|
||||
private var lastOpenVPNReachabilityStatus: OpenVPNReachabilityStatus?
|
||||
|
||||
var splitTunnelType: Int?
|
||||
var splitTunnelSites: [String]?
|
||||
@@ -83,22 +78,14 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||
|
||||
guard hasMeaningfulChange, let proto = self.protoType else { return }
|
||||
|
||||
// WireGuard/AWG manages network changes internally in its own adapter.
|
||||
// WireGuard/AWG manages network changes internally; avoid restarting the tunnel here.
|
||||
if proto == .wireguard {
|
||||
return
|
||||
}
|
||||
|
||||
if proto == .openvpn {
|
||||
self.scheduleOpenVPNReconnect(reason: "NWPath changed")
|
||||
return
|
||||
DispatchQueue.main.async {
|
||||
self.handle(networkChange: path) { _ in }
|
||||
}
|
||||
|
||||
if self.isApplyingNetworkChange || self.reasserting {
|
||||
xrayLog(.debug, message: "Ignoring path change while xray restart is in progress")
|
||||
return
|
||||
}
|
||||
|
||||
self.scheduleNetworkChangeHandling(for: proto, path: path)
|
||||
}
|
||||
pathMonitor.start(queue: pathMonitorQueue)
|
||||
|
||||
@@ -210,8 +197,6 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||
return
|
||||
}
|
||||
|
||||
cancelPendingOpenVPNReconnect()
|
||||
cancelPendingNetworkChangeHandling()
|
||||
didReceiveInitialPathUpdate = false
|
||||
updateActiveInterfaceIndexForCurrentPath()
|
||||
|
||||
@@ -230,9 +215,6 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||
|
||||
|
||||
override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
|
||||
cancelPendingOpenVPNReconnect()
|
||||
cancelPendingNetworkChangeHandling()
|
||||
|
||||
guard let protoType else {
|
||||
completionHandler()
|
||||
return
|
||||
@@ -277,111 +259,9 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||
}
|
||||
|
||||
private func handle(networkChange changePath: Network.NWPath, completion: @escaping (Error?) -> Void) {
|
||||
guard protoType == .xray else {
|
||||
updateActiveInterfaceIndex(for: changePath)
|
||||
completion(nil)
|
||||
return
|
||||
}
|
||||
|
||||
updateActiveInterfaceIndex(for: changePath)
|
||||
reasserting = true
|
||||
xrayLog(.info, message: "Applying network change to xray tunnel")
|
||||
stopXray { }
|
||||
startXray { [weak self] error in
|
||||
self?.reasserting = false
|
||||
completion(error)
|
||||
}
|
||||
}
|
||||
|
||||
private func scheduleNetworkChangeHandling(for proto: TunnelProtoType, path: Network.NWPath) {
|
||||
guard proto == .xray else { return }
|
||||
|
||||
pendingNetworkChangeWorkItem?.cancel()
|
||||
|
||||
let workItem = DispatchWorkItem { [weak self] in
|
||||
guard let self else { return }
|
||||
self.pendingNetworkChangeWorkItem = nil
|
||||
|
||||
if self.isApplyingNetworkChange || self.reasserting {
|
||||
xrayLog(.debug, message: "Skipping network change while restart is already in progress")
|
||||
return
|
||||
}
|
||||
|
||||
self.isApplyingNetworkChange = true
|
||||
DispatchQueue.main.async {
|
||||
self.handle(networkChange: path) { [weak self] _ in
|
||||
self?.networkChangeQueue.async {
|
||||
self?.isApplyingNetworkChange = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pendingNetworkChangeWorkItem = workItem
|
||||
networkChangeQueue.asyncAfter(deadline: .now() + 1.0, execute: workItem)
|
||||
}
|
||||
|
||||
private func scheduleOpenVPNReconnect(reason: String) {
|
||||
guard protoType == .openvpn else { return }
|
||||
|
||||
pendingOpenVPNReconnectWorkItem?.cancel()
|
||||
|
||||
let workItem = DispatchWorkItem { [weak self] in
|
||||
guard let self else { return }
|
||||
self.pendingOpenVPNReconnectWorkItem = nil
|
||||
|
||||
guard self.protoType == .openvpn else { return }
|
||||
|
||||
if self.reasserting {
|
||||
ovpnLog(.debug, message: "Skipping OpenVPN reconnect while session is already reasserting")
|
||||
return
|
||||
}
|
||||
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
guard let self else { return }
|
||||
guard !self.reasserting else {
|
||||
ovpnLog(.debug, message: "Skipping OpenVPN reconnect while session is already reasserting")
|
||||
return
|
||||
}
|
||||
|
||||
ovpnLog(.info, message: "\(reason), reconnecting OpenVPN session")
|
||||
self.ovpnAdapter?.reconnect(afterTimeInterval: 1)
|
||||
}
|
||||
}
|
||||
|
||||
pendingOpenVPNReconnectWorkItem = workItem
|
||||
networkChangeQueue.asyncAfter(deadline: .now() + 1.0, execute: workItem)
|
||||
}
|
||||
|
||||
func handleOpenVPNReachabilityChange(_ status: OpenVPNReachabilityStatus) {
|
||||
defer { lastOpenVPNReachabilityStatus = status }
|
||||
|
||||
guard let previousStatus = lastOpenVPNReachabilityStatus else {
|
||||
return
|
||||
}
|
||||
|
||||
guard previousStatus != status else {
|
||||
return
|
||||
}
|
||||
|
||||
switch status {
|
||||
case .reachableViaWiFi, .reachableViaWWAN:
|
||||
scheduleOpenVPNReconnect(reason: "Reachability changed")
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
private func cancelPendingOpenVPNReconnect() {
|
||||
pendingOpenVPNReconnectWorkItem?.cancel()
|
||||
pendingOpenVPNReconnectWorkItem = nil
|
||||
lastOpenVPNReachabilityStatus = nil
|
||||
}
|
||||
|
||||
private func cancelPendingNetworkChangeHandling() {
|
||||
pendingNetworkChangeWorkItem?.cancel()
|
||||
pendingNetworkChangeWorkItem = nil
|
||||
isApplyingNetworkChange = false
|
||||
wg_log(.info, message: "Tunnel restarted.")
|
||||
startTunnel(options: nil, completionHandler: completion)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -391,14 +271,8 @@ private extension PacketTunnelProvider {
|
||||
signatureComponents.append(path.isExpensive ? "exp" : "noexp")
|
||||
signatureComponents.append(path.isConstrained ? "con" : "nocon")
|
||||
|
||||
// Ignore loopback and tunnel-style `.other` interfaces so Xray does not
|
||||
// react to its own utun lifecycle as if the physical uplink changed.
|
||||
let preferredTypes: [NWInterface.InterfaceType] = [.wiredEthernet, .wifi, .cellular]
|
||||
let externalInterfaces = path.availableInterfaces.filter { interface in
|
||||
interface.type == .wiredEthernet || interface.type == .wifi || interface.type == .cellular
|
||||
}
|
||||
|
||||
let sortedInterfaces = externalInterfaces.sorted { lhs, rhs in
|
||||
let preferredTypes: [NWInterface.InterfaceType] = [.wiredEthernet, .wifi, .cellular, .loopback, .other]
|
||||
let sortedInterfaces = path.availableInterfaces.sorted { lhs, rhs in
|
||||
if lhs.type == rhs.type {
|
||||
return lhs.index < rhs.index
|
||||
}
|
||||
@@ -419,8 +293,8 @@ private extension PacketTunnelProvider {
|
||||
case .wiredEthernet: typeName = "ethernet"
|
||||
case .wifi: typeName = "wifi"
|
||||
case .cellular: typeName = "cellular"
|
||||
case .loopback, .other:
|
||||
continue
|
||||
case .loopback: typeName = "loopback"
|
||||
case .other: typeName = "other"
|
||||
@unknown default: typeName = "unknown"
|
||||
}
|
||||
signatureComponents.append("\(typeName):\(interface.index)")
|
||||
|
||||
@@ -3,7 +3,5 @@ import Foundation
|
||||
struct XrayConfig: Decodable {
|
||||
let dns1: String?
|
||||
let dns2: String?
|
||||
let splitTunnelType: Int?
|
||||
let splitTunnelSites: [String]?
|
||||
let config: String
|
||||
}
|
||||
|
||||
@@ -684,15 +684,6 @@ bool IosController::setupXray()
|
||||
QJsonObject finalConfig;
|
||||
finalConfig.insert(config_key::dns1, m_rawConfig[config_key::dns1].toString());
|
||||
finalConfig.insert(config_key::dns2, m_rawConfig[config_key::dns2].toString());
|
||||
finalConfig.insert(config_key::splitTunnelType, m_rawConfig[config_key::splitTunnelType]);
|
||||
|
||||
QJsonArray splitTunnelSites = m_rawConfig[config_key::splitTunnelSites].toArray();
|
||||
|
||||
for(int index = 0; index < splitTunnelSites.count(); index++) {
|
||||
splitTunnelSites[index] = splitTunnelSites[index].toString().remove(" ");
|
||||
}
|
||||
|
||||
finalConfig.insert(config_key::splitTunnelSites, splitTunnelSites);
|
||||
finalConfig.insert(config_key::config, xrayConfigStr);
|
||||
|
||||
QJsonDocument finalConfigDoc(finalConfig);
|
||||
|
||||
@@ -108,7 +108,7 @@ int LinuxFirewall::linkChain(LinuxFirewall::IPVersion ip, const QString& chain,
|
||||
// (we can't safely delete all rules at once since rule numbers change)
|
||||
// TODO: occasionally this script results in warnings in logs "Bad rule (does a matching rule exist in the chain?)" - this happens when
|
||||
// the e.g OUTPUT chain is empty but this script attempts to delete things from it anyway. It doesn't cause any problems, but we should still fix at some point..
|
||||
return execute(QStringLiteral("if ! %1 -L %2 -n --line-numbers -t %4 2> /dev/null | awk 'int($1) == 1 && $2 == \"%3\" { found=1 } END { if(found==1) { exit 0 } else { exit 1 } }' ; then %1 -I %2 -j %3 -t %4 && %1 -L %2 -n --line-numbers -t %4 2> /dev/null | awk 'int($1) > 1 && $2 == \"%3\" { print $1; exit }' | xargs -r %1 -t %4 -D %2 ; fi").arg(cmd, parent, chain, tableName));
|
||||
return execute(QStringLiteral("if ! %1 -L %2 -n --line-numbers -t %4 2> /dev/null | awk 'int($1) == 1 && $2 == \"%3\" { found=1 } END { if(found==1) { exit 0 } else { exit 1 } }' ; then %1 -I %2 -j %3 -t %4 && %1 -L %2 -n --line-numbers -t %4 2> /dev/null | awk 'int($1) > 1 && $2 == \"%3\" { print $1; exit }' | xargs %1 -t %4 -D %2 ; fi").arg(cmd, parent, chain, tableName));
|
||||
}
|
||||
else
|
||||
return execute(QStringLiteral("if ! %1 -C %2 -j %3 -t %4 2> /dev/null ; then %1 -A %2 -j %3 -t %4; fi").arg(cmd, parent, chain, tableName));
|
||||
@@ -289,7 +289,6 @@ void LinuxFirewall::install()
|
||||
installAnchor(Both, QStringLiteral("100.blockAll"), {
|
||||
QStringLiteral("-j REJECT"),
|
||||
});
|
||||
installAnchor(Both, QStringLiteral("400.allowPIA"), {});
|
||||
// NAT rules
|
||||
installAnchor(Both, QStringLiteral("100.transIp"), {
|
||||
|
||||
@@ -496,23 +495,13 @@ int LinuxFirewall::execute(const QString &command, bool ignoreErrors)
|
||||
logger.debug() << "(" << exitCode << ") $ " << command;
|
||||
if (!out.isEmpty())
|
||||
logger.info() << out;
|
||||
if (!err.isEmpty() && !ignoreErrors)
|
||||
if (!err.isEmpty())
|
||||
logger.warning() << err;
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
void LinuxFirewall::setupTrafficSplitting()
|
||||
{
|
||||
// Register the routing table name so the ip tool can resolve it by name
|
||||
execute(QStringLiteral("grep -q '%1' /etc/iproute2/rt_tables || printf '200\\t%1\\n' >> /etc/iproute2/rt_tables").arg(kRtableName));
|
||||
|
||||
// On cgroup v2 (unified hierarchy) systems /sys/fs/cgroup/net_cls/ does not exist.
|
||||
// Try to mount the legacy net_cls cgroup v1 controller so traffic splitting works.
|
||||
execute(QStringLiteral(
|
||||
"if [ ! -d /sys/fs/cgroup/net_cls ] ; then "
|
||||
"mkdir -p /sys/fs/cgroup/net_cls && "
|
||||
"mount -t cgroup -o net_cls cgroup /sys/fs/cgroup/net_cls ; fi"));
|
||||
|
||||
auto cGroupDir = "/sys/fs/cgroup/net_cls/" BRAND_CODE "vpnexclusions/";
|
||||
logger.info() << "Should be setting up cgroup in" << cGroupDir << "for traffic splitting";
|
||||
execute(QStringLiteral("if [ ! -d %1 ] ; then mkdir %1 ; sleep 0.1 ; echo %2 > %1/net_cls.classid ; fi").arg(cGroupDir).arg(kCGroupId));
|
||||
@@ -524,9 +513,6 @@ void LinuxFirewall::teardownTrafficSplitting()
|
||||
{
|
||||
logger.info() << "Tearing down cgroup and routing rules";
|
||||
execute(QStringLiteral("if ip rule list | grep -q %1; then ip rule del from all fwmark %1 lookup %2 2> /dev/null ; fi").arg(kPacketTag, kRtableName));
|
||||
// Ignore errors here: on first teardown the table name may not be registered yet
|
||||
execute(QStringLiteral("ip route flush table %1").arg(kRtableName), true);
|
||||
execute(QStringLiteral("ip route flush table %1").arg(kRtableName));
|
||||
execute(QStringLiteral("ip route flush cache"));
|
||||
// Remove the rt_tables entry we added
|
||||
execute(QStringLiteral("sed -i '/%1/d' /etc/iproute2/rt_tables").arg(kRtableName));
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ bool WireguardUtilsLinux::addInterface(const InterfaceConfig& config) {
|
||||
|
||||
QDir appPath(QCoreApplication::applicationDirPath());
|
||||
QStringList wgArgs = {"-f", "amn0"};
|
||||
m_tunnel.start(appPath.filePath("../../client/bin/wireguard-go"), wgArgs);
|
||||
m_tunnel.start(appPath.filePath("../../client/bin/amneziawg-go"), wgArgs);
|
||||
if (!m_tunnel.waitForStarted(WG_TUN_PROC_TIMEOUT)) {
|
||||
logger.error() << "Unable to start tunnel process due to timeout";
|
||||
m_tunnel.kill();
|
||||
|
||||
@@ -79,7 +79,7 @@ bool WireguardUtilsMacos::addInterface(const InterfaceConfig& config) {
|
||||
|
||||
QDir appPath(QCoreApplication::applicationDirPath());
|
||||
QStringList wgArgs = {"-f", "utun"};
|
||||
m_tunnel.start(appPath.filePath("wireguard-go"), wgArgs);
|
||||
m_tunnel.start(appPath.filePath("amneziawg-go"), wgArgs);
|
||||
if (!m_tunnel.waitForStarted(WG_TUN_PROC_TIMEOUT)) {
|
||||
logger.error() << "Unable to start tunnel process due to timeout";
|
||||
m_tunnel.kill();
|
||||
|
||||
@@ -124,14 +124,14 @@ ErrorCode XrayProtocol::startTun2Socks()
|
||||
m_tun2socksProcess->setProgram(PermittedProcess::Tun2Socks);
|
||||
m_tun2socksProcess->setArguments({"-device", QString("tun://%1").arg(tunName), "-proxy", "socks5://127.0.0.1:10808" });
|
||||
|
||||
connect(m_tun2socksProcess.data(), &IpcProcessInterfaceReplica::readyReadStandardOutput, this, [this]() {
|
||||
auto readAllStandardOutput = m_tun2socksProcess->readAllStandardOutput();
|
||||
if (!readAllStandardOutput.waitForFinished()) {
|
||||
connect(m_tun2socksProcess.data(), &IpcProcessInterfaceReplica::readyReadStandardError, this, [this]() {
|
||||
auto readAllStandardError = m_tun2socksProcess->readAllStandardError();
|
||||
if (!readAllStandardError.waitForFinished()) {
|
||||
qWarning() << "Failed to read output from tun2socks";
|
||||
return;
|
||||
}
|
||||
|
||||
const QString line = readAllStandardOutput.returnValue();
|
||||
const QString line = readAllStandardError.returnValue();
|
||||
|
||||
if (!line.contains("[TCP]") && !line.contains("[UDP]"))
|
||||
qDebug() << "[tun2socks]:" << line;
|
||||
|
||||
@@ -135,7 +135,6 @@
|
||||
<file>ui/qml/Components/InstalledAppsDrawer.qml</file>
|
||||
<file>ui/qml/Components/QuestionDrawer.qml</file>
|
||||
<file>ui/qml/Components/SelectLanguageDrawer.qml</file>
|
||||
<file>ui/qml/Components/SubscriptionExpiredDrawer.qml</file>
|
||||
<file>ui/qml/Components/ServersListView.qml</file>
|
||||
<file>ui/qml/Components/SettingsContainersListView.qml</file>
|
||||
<file>ui/qml/Components/TransportProtoSelector.qml</file>
|
||||
|
||||
@@ -35,12 +35,13 @@ SecureQSettings::SecureQSettings(const QString &organization, const QString &app
|
||||
}
|
||||
}
|
||||
m_settings.setValue("Conf/encrypted", true);
|
||||
m_settings.sync();
|
||||
}
|
||||
}
|
||||
|
||||
QVariant SecureQSettings::value(const QString &key, const QVariant &defaultValue) const
|
||||
{
|
||||
QMutexLocker locker(&m_mutex);
|
||||
QMutexLocker locker(&mutex);
|
||||
|
||||
if (m_cache.contains(key)) {
|
||||
return m_cache.value(key);
|
||||
@@ -84,7 +85,7 @@ QVariant SecureQSettings::value(const QString &key, const QVariant &defaultValue
|
||||
|
||||
void SecureQSettings::setValue(const QString &key, const QVariant &value)
|
||||
{
|
||||
QMutexLocker locker(&m_mutex);
|
||||
QMutexLocker locker(&mutex);
|
||||
|
||||
if (encryptionRequired() && encryptedKeys.contains(key)) {
|
||||
if (!getEncKey().isEmpty() && !getEncIv().isEmpty()) {
|
||||
@@ -106,20 +107,26 @@ void SecureQSettings::setValue(const QString &key, const QVariant &value)
|
||||
}
|
||||
|
||||
m_cache.insert(key, value);
|
||||
sync();
|
||||
}
|
||||
|
||||
void SecureQSettings::remove(const QString &key)
|
||||
{
|
||||
QMutexLocker locker(&m_mutex);
|
||||
QMutexLocker locker(&mutex);
|
||||
|
||||
m_settings.remove(key);
|
||||
m_cache.remove(key);
|
||||
|
||||
sync();
|
||||
}
|
||||
|
||||
void SecureQSettings::sync()
|
||||
{
|
||||
m_settings.sync();
|
||||
}
|
||||
|
||||
QByteArray SecureQSettings::backupAppConfig() const
|
||||
{
|
||||
QMutexLocker locker(&m_mutex);
|
||||
|
||||
QJsonObject cfg;
|
||||
|
||||
const auto needToBackup = [this](const auto &key) {
|
||||
@@ -154,8 +161,6 @@ QByteArray SecureQSettings::backupAppConfig() const
|
||||
|
||||
bool SecureQSettings::restoreAppConfig(const QByteArray &json)
|
||||
{
|
||||
QMutexLocker locker(&m_mutex);
|
||||
|
||||
QJsonObject cfg = QJsonDocument::fromJson(json).object();
|
||||
if (cfg.isEmpty())
|
||||
return false;
|
||||
@@ -168,16 +173,10 @@ bool SecureQSettings::restoreAppConfig(const QByteArray &json)
|
||||
setValue(key, cfg.value(key).toVariant());
|
||||
}
|
||||
|
||||
sync();
|
||||
return true;
|
||||
}
|
||||
|
||||
void SecureQSettings::clearSettings()
|
||||
{
|
||||
QMutexLocker locker(&m_mutex);
|
||||
m_settings.clear();
|
||||
m_cache.clear();
|
||||
}
|
||||
|
||||
QByteArray SecureQSettings::encryptText(const QByteArray &value) const
|
||||
{
|
||||
QSimpleCrypto::QBlockCipher cipher;
|
||||
@@ -295,3 +294,11 @@ void SecureQSettings::setSecTag(const QString &tag, const QByteArray &data)
|
||||
qCritical() << "SecureQSettings::setSecTag Error:" << job->errorString();
|
||||
}
|
||||
}
|
||||
|
||||
void SecureQSettings::clearSettings()
|
||||
{
|
||||
QMutexLocker locker(&mutex);
|
||||
m_settings.clear();
|
||||
m_cache.clear();
|
||||
sync();
|
||||
}
|
||||
|
||||
@@ -16,16 +16,14 @@ public:
|
||||
explicit SecureQSettings(const QString &organization, const QString &application = QString(),
|
||||
QObject *parent = nullptr);
|
||||
|
||||
QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const;
|
||||
void setValue(const QString &key, const QVariant &value);
|
||||
Q_INVOKABLE QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const;
|
||||
Q_INVOKABLE void setValue(const QString &key, const QVariant &value);
|
||||
void remove(const QString &key);
|
||||
void sync();
|
||||
|
||||
QByteArray backupAppConfig() const;
|
||||
bool restoreAppConfig(const QByteArray &json);
|
||||
|
||||
void clearSettings();
|
||||
|
||||
private:
|
||||
QByteArray encryptText(const QByteArray &value) const;
|
||||
QByteArray decryptText(const QByteArray &ba) const;
|
||||
|
||||
@@ -37,6 +35,9 @@ private:
|
||||
static QByteArray getSecTag(const QString &tag);
|
||||
static void setSecTag(const QString &tag, const QByteArray &data);
|
||||
|
||||
void clearSettings();
|
||||
|
||||
private:
|
||||
QSettings m_settings;
|
||||
|
||||
mutable QHash<QString, QVariant> m_cache;
|
||||
@@ -52,7 +53,7 @@ private:
|
||||
|
||||
const QByteArray magicString { "EncData" }; // Magic keyword used for mark encrypted QByteArray
|
||||
|
||||
mutable QRecursiveMutex m_mutex;
|
||||
mutable QMutex mutex;
|
||||
};
|
||||
|
||||
#endif // SECUREQSETTINGS_H
|
||||
|
||||
@@ -21,10 +21,10 @@ Settings::Settings(QObject *parent) : QObject(parent), m_settings(ORGANIZATION_N
|
||||
{
|
||||
// Import old settings
|
||||
if (serversCount() == 0) {
|
||||
QString user = m_settings.value("Server/userName").toString();
|
||||
QString password = m_settings.value("Server/password").toString();
|
||||
QString serverName = m_settings.value("Server/serverName").toString();
|
||||
int port = m_settings.value("Server/serverPort").toInt();
|
||||
QString user = value("Server/userName").toString();
|
||||
QString password = value("Server/password").toString();
|
||||
QString serverName = value("Server/serverName").toString();
|
||||
int port = value("Server/serverPort").toInt();
|
||||
|
||||
if (!user.isEmpty() && !password.isEmpty() && !serverName.isEmpty()) {
|
||||
QJsonObject server;
|
||||
@@ -222,7 +222,7 @@ QString Settings::nextAvailableServerName() const
|
||||
|
||||
void Settings::setSaveLogs(bool enabled)
|
||||
{
|
||||
m_settings.setValue("Conf/saveLogs", enabled);
|
||||
setValue("Conf/saveLogs", enabled);
|
||||
#ifndef Q_OS_ANDROID
|
||||
if (!isSaveLogs()) {
|
||||
Logger::deInit();
|
||||
@@ -242,12 +242,12 @@ void Settings::setSaveLogs(bool enabled)
|
||||
|
||||
QDateTime Settings::getLogEnableDate()
|
||||
{
|
||||
return m_settings.value("Conf/logEnableDate").toDateTime();
|
||||
return value("Conf/logEnableDate").toDateTime();
|
||||
}
|
||||
|
||||
void Settings::setLogEnableDate(QDateTime date)
|
||||
{
|
||||
m_settings.setValue("Conf/logEnableDate", date);
|
||||
setValue("Conf/logEnableDate", date);
|
||||
}
|
||||
|
||||
QString Settings::routeModeString(RouteMode mode) const
|
||||
@@ -261,17 +261,17 @@ QString Settings::routeModeString(RouteMode mode) const
|
||||
|
||||
Settings::RouteMode Settings::routeMode() const
|
||||
{
|
||||
return static_cast<RouteMode>(m_settings.value("Conf/routeMode", 0).toInt());
|
||||
return static_cast<RouteMode>(value("Conf/routeMode", 0).toInt());
|
||||
}
|
||||
|
||||
bool Settings::isSitesSplitTunnelingEnabled() const
|
||||
{
|
||||
return m_settings.value("Conf/sitesSplitTunnelingEnabled", false).toBool();
|
||||
return value("Conf/sitesSplitTunnelingEnabled", false).toBool();
|
||||
}
|
||||
|
||||
void Settings::setSitesSplitTunnelingEnabled(bool enabled)
|
||||
{
|
||||
m_settings.setValue("Conf/sitesSplitTunnelingEnabled", enabled);
|
||||
setValue("Conf/sitesSplitTunnelingEnabled", enabled);
|
||||
}
|
||||
|
||||
bool Settings::addVpnSite(RouteMode mode, const QString &site, const QString &ip)
|
||||
@@ -359,12 +359,12 @@ void Settings::removeAllVpnSites(RouteMode mode)
|
||||
|
||||
QString Settings::primaryDns() const
|
||||
{
|
||||
return m_settings.value("Conf/primaryDns", cloudFlareNs1).toString();
|
||||
return value("Conf/primaryDns", cloudFlareNs1).toString();
|
||||
}
|
||||
|
||||
QString Settings::secondaryDns() const
|
||||
{
|
||||
return m_settings.value("Conf/secondaryDns", cloudFlareNs2).toString();
|
||||
return value("Conf/secondaryDns", cloudFlareNs2).toString();
|
||||
}
|
||||
|
||||
void Settings::clearSettings()
|
||||
@@ -386,18 +386,18 @@ QString Settings::appsRouteModeString(AppsRouteMode mode) const
|
||||
|
||||
Settings::AppsRouteMode Settings::getAppsRouteMode() const
|
||||
{
|
||||
return static_cast<AppsRouteMode>(m_settings.value("Conf/appsRouteMode", 0).toInt());
|
||||
return static_cast<AppsRouteMode>(value("Conf/appsRouteMode", 0).toInt());
|
||||
}
|
||||
|
||||
void Settings::setAppsRouteMode(AppsRouteMode mode)
|
||||
{
|
||||
m_settings.setValue("Conf/appsRouteMode", mode);
|
||||
setValue("Conf/appsRouteMode", mode);
|
||||
}
|
||||
|
||||
QVector<InstalledAppInfo> Settings::getVpnApps(AppsRouteMode mode) const
|
||||
{
|
||||
QVector<InstalledAppInfo> apps;
|
||||
auto appsArray = m_settings.value("Conf/" + appsRouteModeString(mode)).toJsonArray();
|
||||
auto appsArray = value("Conf/" + appsRouteModeString(mode)).toJsonArray();
|
||||
for (const auto &app : appsArray) {
|
||||
InstalledAppInfo appInfo;
|
||||
appInfo.appName = app.toObject().value("appName").toString();
|
||||
@@ -419,42 +419,43 @@ void Settings::setVpnApps(AppsRouteMode mode, const QVector<InstalledAppInfo> &a
|
||||
appInfo.insert("appPath", app.appPath);
|
||||
appsArray.push_back(appInfo);
|
||||
}
|
||||
m_settings.setValue("Conf/" + appsRouteModeString(mode), appsArray);
|
||||
setValue("Conf/" + appsRouteModeString(mode), appsArray);
|
||||
m_settings.sync();
|
||||
}
|
||||
|
||||
bool Settings::isAppsSplitTunnelingEnabled() const
|
||||
{
|
||||
return m_settings.value("Conf/appsSplitTunnelingEnabled", false).toBool();
|
||||
return value("Conf/appsSplitTunnelingEnabled", false).toBool();
|
||||
}
|
||||
|
||||
void Settings::setAppsSplitTunnelingEnabled(bool enabled)
|
||||
{
|
||||
m_settings.setValue("Conf/appsSplitTunnelingEnabled", enabled);
|
||||
setValue("Conf/appsSplitTunnelingEnabled", enabled);
|
||||
}
|
||||
|
||||
bool Settings::isKillSwitchEnabled() const
|
||||
{
|
||||
return m_settings.value("Conf/killSwitchEnabled", true).toBool();
|
||||
return value("Conf/killSwitchEnabled", true).toBool();
|
||||
}
|
||||
|
||||
void Settings::setKillSwitchEnabled(bool enabled)
|
||||
{
|
||||
m_settings.setValue("Conf/killSwitchEnabled", enabled);
|
||||
setValue("Conf/killSwitchEnabled", enabled);
|
||||
}
|
||||
|
||||
bool Settings::isStrictKillSwitchEnabled() const
|
||||
{
|
||||
return m_settings.value("Conf/strictKillSwitchEnabled", false).toBool();
|
||||
return value("Conf/strictKillSwitchEnabled", false).toBool();
|
||||
}
|
||||
|
||||
void Settings::setStrictKillSwitchEnabled(bool enabled)
|
||||
{
|
||||
m_settings.setValue("Conf/strictKillSwitchEnabled", enabled);
|
||||
setValue("Conf/strictKillSwitchEnabled", enabled);
|
||||
}
|
||||
|
||||
QString Settings::getInstallationUuid(const bool needCreate)
|
||||
{
|
||||
auto uuid = m_settings.value("Conf/installationUuid", "").toString();
|
||||
auto uuid = value("Conf/installationUuid", "").toString();
|
||||
if (needCreate && uuid.isEmpty()) {
|
||||
uuid = QUuid::createUuid().toString();
|
||||
|
||||
@@ -475,7 +476,7 @@ QString Settings::getInstallationUuid(const bool needCreate)
|
||||
|
||||
void Settings::setInstallationUuid(const QString &uuid)
|
||||
{
|
||||
m_settings.setValue("Conf/installationUuid", uuid);
|
||||
setValue("Conf/installationUuid", uuid);
|
||||
}
|
||||
|
||||
ServerCredentials Settings::defaultServerCredentials() const
|
||||
@@ -496,6 +497,28 @@ ServerCredentials Settings::serverCredentials(int index) const
|
||||
return credentials;
|
||||
}
|
||||
|
||||
QVariant Settings::value(const QString &key, const QVariant &defaultValue) const
|
||||
{
|
||||
QVariant returnValue;
|
||||
if (QThread::currentThread() == QCoreApplication::instance()->thread()) {
|
||||
returnValue = m_settings.value(key, defaultValue);
|
||||
} else {
|
||||
QMetaObject::invokeMethod(&m_settings, "value", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QVariant, returnValue),
|
||||
Q_ARG(const QString &, key), Q_ARG(const QVariant &, defaultValue));
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
void Settings::setValue(const QString &key, const QVariant &value)
|
||||
{
|
||||
if (QThread::currentThread() == QCoreApplication::instance()->thread()) {
|
||||
m_settings.setValue(key, value);
|
||||
} else {
|
||||
QMetaObject::invokeMethod(&m_settings, "setValue", Qt::BlockingQueuedConnection, Q_ARG(const QString &, key),
|
||||
Q_ARG(const QVariant &, value));
|
||||
}
|
||||
}
|
||||
|
||||
void Settings::resetGatewayEndpoint()
|
||||
{
|
||||
m_gatewayEndpoint = gatewayEndpoint;
|
||||
@@ -518,50 +541,50 @@ QString Settings::getGatewayEndpoint(bool isTestPurchase)
|
||||
|
||||
bool Settings::isDevGatewayEnv(bool isTestPurchase)
|
||||
{
|
||||
return isTestPurchase ? true : m_settings.value("Conf/devGatewayEnv", false).toBool();
|
||||
return isTestPurchase ? true : value("Conf/devGatewayEnv", false).toBool();
|
||||
}
|
||||
|
||||
void Settings::toggleDevGatewayEnv(bool enabled)
|
||||
{
|
||||
m_settings.setValue("Conf/devGatewayEnv", enabled);
|
||||
setValue("Conf/devGatewayEnv", enabled);
|
||||
}
|
||||
|
||||
bool Settings::isHomeAdLabelVisible()
|
||||
{
|
||||
return m_settings.value("Conf/homeAdLabelVisible", true).toBool();
|
||||
return value("Conf/homeAdLabelVisible", true).toBool();
|
||||
}
|
||||
|
||||
void Settings::disableHomeAdLabel()
|
||||
{
|
||||
m_settings.setValue("Conf/homeAdLabelVisible", false);
|
||||
setValue("Conf/homeAdLabelVisible", false);
|
||||
}
|
||||
|
||||
bool Settings::isPremV1MigrationReminderActive()
|
||||
{
|
||||
return m_settings.value("Conf/premV1MigrationReminderActive", true).toBool();
|
||||
return value("Conf/premV1MigrationReminderActive", true).toBool();
|
||||
}
|
||||
|
||||
void Settings::disablePremV1MigrationReminder()
|
||||
{
|
||||
m_settings.setValue("Conf/premV1MigrationReminderActive", false);
|
||||
setValue("Conf/premV1MigrationReminderActive", false);
|
||||
}
|
||||
|
||||
QStringList Settings::allowedDnsServers() const
|
||||
{
|
||||
return m_settings.value("Conf/allowedDnsServers").toStringList();
|
||||
return value("Conf/allowedDnsServers").toStringList();
|
||||
}
|
||||
|
||||
void Settings::setAllowedDnsServers(const QStringList &servers)
|
||||
{
|
||||
m_settings.setValue("Conf/allowedDnsServers", servers);
|
||||
setValue("Conf/allowedDnsServers", servers);
|
||||
}
|
||||
|
||||
QStringList Settings::readNewsIds() const
|
||||
{
|
||||
return m_settings.value("News/readIds").toStringList();
|
||||
return value("News/readIds").toStringList();
|
||||
}
|
||||
|
||||
void Settings::setReadNewsIds(const QStringList &ids)
|
||||
{
|
||||
m_settings.setValue("News/readIds", ids);
|
||||
setValue("News/readIds", ids);
|
||||
}
|
||||
|
||||
@@ -29,11 +29,11 @@ public:
|
||||
|
||||
QJsonArray serversArray() const
|
||||
{
|
||||
return QJsonDocument::fromJson(m_settings.value("Servers/serversList").toByteArray()).array();
|
||||
return QJsonDocument::fromJson(value("Servers/serversList").toByteArray()).array();
|
||||
}
|
||||
void setServersArray(const QJsonArray &servers)
|
||||
{
|
||||
m_settings.setValue("Servers/serversList", QJsonDocument(servers).toJson());
|
||||
setValue("Servers/serversList", QJsonDocument(servers).toJson());
|
||||
}
|
||||
|
||||
// Servers section
|
||||
@@ -45,11 +45,11 @@ public:
|
||||
|
||||
int defaultServerIndex() const
|
||||
{
|
||||
return m_settings.value("Servers/defaultServerIndex", 0).toInt();
|
||||
return value("Servers/defaultServerIndex", 0).toInt();
|
||||
}
|
||||
void setDefaultServer(int index)
|
||||
{
|
||||
m_settings.setValue("Servers/defaultServerIndex", index);
|
||||
setValue("Servers/defaultServerIndex", index);
|
||||
}
|
||||
QJsonObject defaultServer() const
|
||||
{
|
||||
@@ -78,34 +78,34 @@ public:
|
||||
// App settings section
|
||||
bool isAutoConnect() const
|
||||
{
|
||||
return m_settings.value("Conf/autoConnect", false).toBool();
|
||||
return value("Conf/autoConnect", false).toBool();
|
||||
}
|
||||
void setAutoConnect(bool enabled)
|
||||
{
|
||||
m_settings.setValue("Conf/autoConnect", enabled);
|
||||
setValue("Conf/autoConnect", enabled);
|
||||
}
|
||||
|
||||
bool isStartMinimized() const
|
||||
{
|
||||
return m_settings.value("Conf/startMinimized", false).toBool();
|
||||
return value("Conf/startMinimized", false).toBool();
|
||||
}
|
||||
void setStartMinimized(bool enabled)
|
||||
{
|
||||
m_settings.setValue("Conf/startMinimized", enabled);
|
||||
setValue("Conf/startMinimized", enabled);
|
||||
}
|
||||
|
||||
bool isNewsNotifications() const
|
||||
{
|
||||
return m_settings.value("Conf/newsNotifications", true).toBool();
|
||||
return value("Conf/newsNotifications", true).toBool();
|
||||
}
|
||||
void setNewsNotifications(bool enabled)
|
||||
{
|
||||
m_settings.setValue("Conf/newsNotifications", enabled);
|
||||
setValue("Conf/newsNotifications", enabled);
|
||||
}
|
||||
|
||||
bool isSaveLogs() const
|
||||
{
|
||||
return m_settings.value("Conf/saveLogs", false).toBool();
|
||||
return value("Conf/saveLogs", false).toBool();
|
||||
}
|
||||
void setSaveLogs(bool enabled);
|
||||
|
||||
@@ -122,18 +122,19 @@ public:
|
||||
QString routeModeString(RouteMode mode) const;
|
||||
|
||||
RouteMode routeMode() const;
|
||||
void setRouteMode(RouteMode mode) { m_settings.setValue("Conf/routeMode", mode); }
|
||||
void setRouteMode(RouteMode mode) { setValue("Conf/routeMode", mode); }
|
||||
|
||||
bool isSitesSplitTunnelingEnabled() const;
|
||||
void setSitesSplitTunnelingEnabled(bool enabled);
|
||||
|
||||
QVariantMap vpnSites(RouteMode mode) const
|
||||
{
|
||||
return m_settings.value("Conf/" + routeModeString(mode)).toMap();
|
||||
return value("Conf/" + routeModeString(mode)).toMap();
|
||||
}
|
||||
void setVpnSites(RouteMode mode, const QVariantMap &sites)
|
||||
{
|
||||
m_settings.setValue("Conf/" + routeModeString(mode), sites);
|
||||
setValue("Conf/" + routeModeString(mode), sites);
|
||||
m_settings.sync();
|
||||
}
|
||||
bool addVpnSite(RouteMode mode, const QString &site, const QString &ip = "");
|
||||
void addVpnSites(RouteMode mode, const QMap<QString, QString> &sites); // map <site, ip>
|
||||
@@ -146,11 +147,11 @@ public:
|
||||
|
||||
bool useAmneziaDns() const
|
||||
{
|
||||
return m_settings.value("Conf/useAmneziaDns", true).toBool();
|
||||
return value("Conf/useAmneziaDns", true).toBool();
|
||||
}
|
||||
void setUseAmneziaDns(bool enabled)
|
||||
{
|
||||
m_settings.setValue("Conf/useAmneziaDns", enabled);
|
||||
setValue("Conf/useAmneziaDns", enabled);
|
||||
}
|
||||
|
||||
QString primaryDns() const;
|
||||
@@ -159,13 +160,13 @@ public:
|
||||
// QString primaryDns() const { return m_primaryDns; }
|
||||
void setPrimaryDns(const QString &primaryDns)
|
||||
{
|
||||
m_settings.setValue("Conf/primaryDns", primaryDns);
|
||||
setValue("Conf/primaryDns", primaryDns);
|
||||
}
|
||||
|
||||
// QString secondaryDns() const { return m_secondaryDns; }
|
||||
void setSecondaryDns(const QString &secondaryDns)
|
||||
{
|
||||
m_settings.setValue("Conf/secondaryDns", secondaryDns);
|
||||
setValue("Conf/secondaryDns", secondaryDns);
|
||||
}
|
||||
|
||||
// static constexpr char openNicNs5[] = "94.103.153.176";
|
||||
@@ -187,16 +188,16 @@ public:
|
||||
};
|
||||
void setAppLanguage(QLocale locale)
|
||||
{
|
||||
m_settings.setValue("Conf/appLanguage", locale.name());
|
||||
setValue("Conf/appLanguage", locale.name());
|
||||
};
|
||||
|
||||
bool isScreenshotsEnabled() const
|
||||
{
|
||||
return m_settings.value("Conf/screenshotsEnabled", true).toBool();
|
||||
return value("Conf/screenshotsEnabled", true).toBool();
|
||||
}
|
||||
void setScreenshotsEnabled(bool enabled)
|
||||
{
|
||||
m_settings.setValue("Conf/screenshotsEnabled", enabled);
|
||||
setValue("Conf/screenshotsEnabled", enabled);
|
||||
emit screenshotsEnabledChanged(enabled);
|
||||
}
|
||||
|
||||
@@ -254,6 +255,9 @@ signals:
|
||||
void settingsCleared();
|
||||
|
||||
private:
|
||||
QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const;
|
||||
void setValue(const QString &key, const QVariant &value);
|
||||
|
||||
void setInstallationUuid(const QString &uuid);
|
||||
|
||||
mutable SecureQSettings m_settings;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -199,7 +199,7 @@
|
||||
<message>
|
||||
<location filename="../ui/models/api/apiServicesModel.cpp" line="116"/>
|
||||
<source>%1 $</source>
|
||||
<translation>%1 $</translation>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/models/api/apiServicesModel.cpp" line="118"/>
|
||||
@@ -672,32 +672,32 @@ Thank you for staying with us!</source>
|
||||
<translation>Порт</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="422"/>
|
||||
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="418"/>
|
||||
<source>Save</source>
|
||||
<translation>Сохранить</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="431"/>
|
||||
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="427"/>
|
||||
<source>Save settings?</source>
|
||||
<translation>Сохранить настройки?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="432"/>
|
||||
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="428"/>
|
||||
<source>Only the settings for this device will be changed</source>
|
||||
<translation>Будут изменены настройки только для этого устройства</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="433"/>
|
||||
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="429"/>
|
||||
<source>Continue</source>
|
||||
<translation>Продолжить</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="434"/>
|
||||
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="430"/>
|
||||
<source>Cancel</source>
|
||||
<translation>Отменить</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="438"/>
|
||||
<location filename="../ui/qml/Pages2/PageProtocolAwgClientSettings.qml" line="434"/>
|
||||
<source>Unable change settings while there is an active connection</source>
|
||||
<translation>Невозможно изменить настройки во время активного соединения</translation>
|
||||
</message>
|
||||
@@ -1651,7 +1651,7 @@ Thank you for staying with us!</source>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="204"/>
|
||||
<source>mailto:support@amnezia.org</source>
|
||||
<translation>mailto:support@amnezia.org</translation>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsAbout.qml" line="211"/>
|
||||
@@ -1775,72 +1775,72 @@ Thank you for staying with us!</source>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="22"/>
|
||||
<source>Windows</source>
|
||||
<translation>Windows</translation>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="29"/>
|
||||
<source>macOS</source>
|
||||
<translation>macOS</translation>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="36"/>
|
||||
<source>Android</source>
|
||||
<translation>Android</translation>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="43"/>
|
||||
<source>AndroidTV</source>
|
||||
<translation>Android TV</translation>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="50"/>
|
||||
<source>iOS</source>
|
||||
<translation>iOS</translation>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="57"/>
|
||||
<source>Linux</source>
|
||||
<translation>Linux</translation>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="64"/>
|
||||
<source>Routers</source>
|
||||
<translation>Маршрутизаторы</translation>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="23"/>
|
||||
<source>documentation/instructions/connect-amnezia-premium#windows</source>
|
||||
<translation>documentation/instructions/connect-amnezia-premium#windows</translation>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="30"/>
|
||||
<source>documentation/instructions/connect-amnezia-premium#macos</source>
|
||||
<translation>documentation/instructions/connect-amnezia-premium#macos</translation>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="37"/>
|
||||
<source>documentation/instructions/connect-amnezia-premium#android</source>
|
||||
<translation>documentation/instructions/connect-amnezia-premium#android</translation>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="44"/>
|
||||
<source>documentation/instructions/android_tv_connect/</source>
|
||||
<translation>documentation/instructions/android_tv_connect/</translation>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="51"/>
|
||||
<source>documentation/instructions/connect-amnezia-premium#ios</source>
|
||||
<translation>documentation/instructions/connect-amnezia-premium#ios</translation>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="58"/>
|
||||
<source>documentation/instructions/connect-amnezia-premium#linux</source>
|
||||
<translation>documentation/instructions/connect-amnezia-premium#linux</translation>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="65"/>
|
||||
<source>documentation/instructions/connect-amnezia-premium#routers</source>
|
||||
<translation>documentation/instructions/connect-amnezia-premium#routers</translation>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiInstructions.qml" line="101"/>
|
||||
@@ -2111,7 +2111,7 @@ Thank you for staying with us!</source>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="22"/>
|
||||
<source>Telegram</source>
|
||||
<translation>Telegram</translation>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="30"/>
|
||||
@@ -2141,7 +2141,7 @@ Thank you for staying with us!</source>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="110"/>
|
||||
<source>Support tag</source>
|
||||
<translation>Идентификатор поддержки</translation>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApiSupport.qml" line="120"/>
|
||||
@@ -2272,12 +2272,12 @@ Thank you for staying with us!</source>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApplication.qml" line="180"/>
|
||||
<source>News Notification</source>
|
||||
<translation>Уведомления о новостях</translation>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApplication.qml" line="181"/>
|
||||
<source>Show a notification icon for unread news</source>
|
||||
<translation>Показывать значок уведомления, если есть непрочитанные новости</translation>
|
||||
<source>Show notification icon when has unread news</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSettingsApplication.qml" line="221"/>
|
||||
@@ -3115,17 +3115,17 @@ Thank you for staying with us!</source>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml" line="113"/>
|
||||
<source>Charged to your Apple ID at confirmation. Renews automatically unless auto-renew is turned off at least 24 hours before period end. Manage in Apple ID settings.</source>
|
||||
<translation>Списание с Apple ID при подтверждении. Продление автоматическое, если автопродление не отключено минимум за 24 часа до окончания периода. Управление в настройках Apple ID.</translation>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml" line="125"/>
|
||||
<source>Subscribe Now</source>
|
||||
<translation>Подписаться сейчас</translation>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml" line="158"/>
|
||||
<source>By continuing, you agree to the <a href="%1" style="color: #FBB26A;">Terms of Use</a> and <a href="%2" style="color: #FBB26A;">Privacy Policy</a></source>
|
||||
<translation>Продолжая, вы соглашаетесь с <a href="%1" style="color: #FBB26A;">Условиями использования</a> и <a href="%2" style="color: #FBB26A;">Политикой конфиденциальности</a></translation>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml" line="186"/>
|
||||
@@ -3697,7 +3697,7 @@ Thank you for staying with us!</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="270"/>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="572"/>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="566"/>
|
||||
<source>Users</source>
|
||||
<translation>Пользователи</translation>
|
||||
</message>
|
||||
@@ -3707,72 +3707,72 @@ Thank you for staying with us!</source>
|
||||
<translation>Имя пользователя</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="588"/>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="582"/>
|
||||
<source>Search</source>
|
||||
<translation>Поиск</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="717"/>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="711"/>
|
||||
<source>Creation date: %1</source>
|
||||
<translation>Дата создания: %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="729"/>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="723"/>
|
||||
<source>Latest handshake: %1</source>
|
||||
<translation>Последнее рукопожатие: %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="741"/>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="735"/>
|
||||
<source>Data received: %1</source>
|
||||
<translation>Получено данных: %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="753"/>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="747"/>
|
||||
<source>Data sent: %1</source>
|
||||
<translation>Отправлено данных: %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="763"/>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="757"/>
|
||||
<source>Allowed IPs: %1</source>
|
||||
<translation>Разрешенные подсети: %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="778"/>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="772"/>
|
||||
<source>Rename</source>
|
||||
<translation>Переименовать</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="803"/>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="797"/>
|
||||
<source>Client name</source>
|
||||
<translation>Имя клиента</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="814"/>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="808"/>
|
||||
<source>Save</source>
|
||||
<translation>Сохранить</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="850"/>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="844"/>
|
||||
<source>Revoke</source>
|
||||
<translation>Отозвать</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="853"/>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="847"/>
|
||||
<source>Revoke the config for a user - %1?</source>
|
||||
<translation>Отозвать конфигурацию для пользователя - %1?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="854"/>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="848"/>
|
||||
<source>The user will no longer be able to connect to your server.</source>
|
||||
<translation>Пользователь больше не сможет подключаться к вашему серверу.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="855"/>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="849"/>
|
||||
<source>Continue</source>
|
||||
<translation>Продолжить</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="856"/>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="850"/>
|
||||
<source>Cancel</source>
|
||||
<translation>Отменить</translation>
|
||||
</message>
|
||||
@@ -3795,7 +3795,7 @@ Thank you for staying with us!</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="220"/>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="554"/>
|
||||
<location filename="../ui/qml/Pages2/PageShare.qml" line="548"/>
|
||||
<source>Share</source>
|
||||
<translation>Поделиться</translation>
|
||||
</message>
|
||||
@@ -4241,7 +4241,7 @@ Thank you for staying with us!</source>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="32"/>
|
||||
<source>Server error: Linux kernel is too old</source>
|
||||
<translation>Ошибка сервера: ядро Linux слишком старое</translation>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../core/errorstrings.cpp" line="35"/>
|
||||
@@ -5020,47 +5020,47 @@ FileZilla или другие SFTP-клиенты, а также смонтир
|
||||
<context>
|
||||
<name>SitesController</name>
|
||||
<message>
|
||||
<location filename="../ui/controllers/sitesController.cpp" line="22"/>
|
||||
<location filename="../ui/controllers/sitesController.cpp" line="24"/>
|
||||
<source>Hostname not look like ip adress or domain name</source>
|
||||
<translation>Имя хоста не похоже на IP-адрес или доменное имя</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/controllers/sitesController.cpp" line="52"/>
|
||||
<location filename="../ui/controllers/sitesController.cpp" line="66"/>
|
||||
<source>New site added: %1</source>
|
||||
<translation>Добавлен новый сайт: %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/controllers/sitesController.cpp" line="61"/>
|
||||
<location filename="../ui/controllers/sitesController.cpp" line="78"/>
|
||||
<source>Site removed: %1</source>
|
||||
<translation>Сайт удален: %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/controllers/sitesController.cpp" line="68"/>
|
||||
<location filename="../ui/controllers/sitesController.cpp" line="85"/>
|
||||
<source>Site list cleared!</source>
|
||||
<translation>Список сайтов очищен!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/controllers/sitesController.cpp" line="75"/>
|
||||
<location filename="../ui/controllers/sitesController.cpp" line="92"/>
|
||||
<source>Can't open file: %1</source>
|
||||
<translation>Невозможно открыть файл: %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/controllers/sitesController.cpp" line="81"/>
|
||||
<location filename="../ui/controllers/sitesController.cpp" line="98"/>
|
||||
<source>Failed to parse JSON data from file: %1</source>
|
||||
<translation>Не удалось разобрать JSON-данные из файла: %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/controllers/sitesController.cpp" line="86"/>
|
||||
<location filename="../ui/controllers/sitesController.cpp" line="103"/>
|
||||
<source>The JSON data is not an array in file: %1</source>
|
||||
<translation>JSON-данные не являются массивом в файле: %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/controllers/sitesController.cpp" line="114"/>
|
||||
<location filename="../ui/controllers/sitesController.cpp" line="133"/>
|
||||
<source>Import completed</source>
|
||||
<translation>Импорт завершен</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ui/controllers/sitesController.cpp" line="133"/>
|
||||
<location filename="../ui/controllers/sitesController.cpp" line="152"/>
|
||||
<source>Export completed</source>
|
||||
<translation>Экспорт завершен</translation>
|
||||
</message>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -366,8 +366,6 @@ bool ApiConfigsController::fillAvailableServices()
|
||||
{
|
||||
QJsonObject apiPayload;
|
||||
apiPayload[configKey::osVersion] = QSysInfo::productType();
|
||||
apiPayload[configKey::appVersion] = QString(APP_VERSION);
|
||||
apiPayload[apiDefs::key::cliName] = QString(APPLICATION_NAME);
|
||||
apiPayload[apiDefs::key::appLanguage] = m_settings->getAppLanguage().name().split("_").first();
|
||||
|
||||
QByteArray responseBody;
|
||||
@@ -449,7 +447,7 @@ bool ApiConfigsController::importService()
|
||||
importSerivceFromAppStore();
|
||||
return true;
|
||||
}
|
||||
} else if (m_apiServicesModel->getSelectedServiceType() == serviceType::amneziaFree) {
|
||||
} else {
|
||||
importServiceFromGateway();
|
||||
return true;
|
||||
}
|
||||
@@ -723,7 +721,6 @@ bool ApiConfigsController::updateServiceFromGateway(const int serverIndex, const
|
||||
}
|
||||
|
||||
bool isTestPurchase = apiConfig.value(apiDefs::key::isTestPurchase).toBool(false);
|
||||
bool wasSubscriptionExpired = m_serversModel->data(serverIndex, ServersModel::IsSubscriptionExpiredRole).toBool();
|
||||
QByteArray responseBody;
|
||||
ErrorCode errorCode = executeRequest(QString("%1v1/config"), apiPayload, responseBody, isTestPurchase);
|
||||
|
||||
@@ -750,11 +747,6 @@ bool ApiConfigsController::updateServiceFromGateway(const int serverIndex, const
|
||||
newServerConfig.insert(config_key::nameOverriddenByUser, true);
|
||||
}
|
||||
m_serversModel->editServer(newServerConfig, serverIndex);
|
||||
|
||||
if (wasSubscriptionExpired) {
|
||||
emit subscriptionRefreshNeeded();
|
||||
}
|
||||
|
||||
if (reloadServiceConfig) {
|
||||
emit reloadServerFromApiFinished(tr("API config reloaded"));
|
||||
} else if (newCountryName.isEmpty()) {
|
||||
@@ -764,11 +756,7 @@ bool ApiConfigsController::updateServiceFromGateway(const int serverIndex, const
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
if (errorCode == ErrorCode::ApiSubscriptionExpiredError) {
|
||||
emit subscriptionExpiredOnServer();
|
||||
} else {
|
||||
emit errorOccurred(errorCode);
|
||||
}
|
||||
emit errorOccurred(errorCode);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,8 +43,6 @@ public slots:
|
||||
|
||||
signals:
|
||||
void errorOccurred(ErrorCode errorCode);
|
||||
void subscriptionExpiredOnServer();
|
||||
void subscriptionRefreshNeeded();
|
||||
|
||||
void installServerFromApiFinished(const QString &message);
|
||||
void changeApiCountryFinished(const QString &message);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "apiSettingsController.h"
|
||||
|
||||
#include <QEventLoop>
|
||||
#include <QJsonDocument>
|
||||
#include <QTimer>
|
||||
|
||||
#include "core/api/apiUtils.h"
|
||||
@@ -78,13 +77,6 @@ bool ApiSettingsController::getAccountInfo(bool reload)
|
||||
QJsonObject accountInfo = QJsonDocument::fromJson(responseBody).object();
|
||||
m_apiAccountInfoModel->updateModel(accountInfo, serverConfig);
|
||||
|
||||
QString subscriptionEndDate = accountInfo.value(apiDefs::key::subscriptionEndDate).toString();
|
||||
if (!subscriptionEndDate.isEmpty()) {
|
||||
apiConfig.insert(apiDefs::key::subscriptionEndDate, subscriptionEndDate);
|
||||
serverConfig.insert(configKey::apiConfig, apiConfig);
|
||||
m_serversModel->editServer(serverConfig, processedIndex);
|
||||
}
|
||||
|
||||
if (reload) {
|
||||
updateApiCountryModel();
|
||||
updateApiDevicesModel();
|
||||
@@ -93,42 +85,6 @@ bool ApiSettingsController::getAccountInfo(bool reload)
|
||||
return true;
|
||||
}
|
||||
|
||||
void ApiSettingsController::getRenewalLink()
|
||||
{
|
||||
auto processedIndex = m_serversModel->getProcessedServerIndex();
|
||||
auto serverConfig = m_serversModel->getServerConfig(processedIndex);
|
||||
auto apiConfig = serverConfig.value(configKey::apiConfig).toObject();
|
||||
auto authData = serverConfig.value(configKey::authData).toObject();
|
||||
|
||||
bool isTestPurchase = apiConfig.value(apiDefs::key::isTestPurchase).toBool(false);
|
||||
auto gatewayController = QSharedPointer<GatewayController>::create(m_settings->getGatewayEndpoint(isTestPurchase),
|
||||
m_settings->isDevGatewayEnv(isTestPurchase),
|
||||
requestTimeoutMsecs,
|
||||
m_settings->isStrictKillSwitchEnabled());
|
||||
|
||||
QJsonObject apiPayload;
|
||||
apiPayload[configKey::userCountryCode] = apiConfig.value(configKey::userCountryCode).toString();
|
||||
apiPayload[configKey::serviceType] = apiConfig.value(configKey::serviceType).toString();
|
||||
apiPayload[configKey::authData] = authData;
|
||||
apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION);
|
||||
apiPayload[apiDefs::key::appLanguage] = m_settings->getAppLanguage().name().split("_").first();
|
||||
|
||||
auto future = gatewayController->postAsync(QString("%1v1/renewal_link"), apiPayload);
|
||||
future.then(this, [this, gatewayController](QPair<ErrorCode, QByteArray> result) {
|
||||
auto [errorCode, responseBody] = result;
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
emit errorOccurred(errorCode);
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonObject responseJson = QJsonDocument::fromJson(responseBody).object();
|
||||
QString url = responseJson.value("renewal_url").toString();
|
||||
if (!url.isEmpty()) {
|
||||
emit renewalLinkReceived(url);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ApiSettingsController::updateApiCountryModel()
|
||||
{
|
||||
m_apiCountryModel->updateModel(m_apiAccountInfoModel->getAvailableCountries(), "");
|
||||
|
||||
@@ -21,11 +21,9 @@ public slots:
|
||||
bool getAccountInfo(bool reload);
|
||||
void updateApiCountryModel();
|
||||
void updateApiDevicesModel();
|
||||
void getRenewalLink();
|
||||
|
||||
signals:
|
||||
void errorOccurred(ErrorCode errorCode);
|
||||
void renewalLinkReceived(const QString &url);
|
||||
|
||||
private:
|
||||
QSharedPointer<ServersModel> m_serversModel;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <QFileInfo>
|
||||
#include <QImage>
|
||||
#include <QStandardPaths>
|
||||
|
||||
#include "core/controllers/vpnConfigurationController.h"
|
||||
#include "core/qrCodeUtils.h"
|
||||
#include "core/serialization/serialization.h"
|
||||
@@ -169,7 +170,8 @@ void ExportController::generateWireGuardConfig(const QString &clientName)
|
||||
m_config.append(line + "\n");
|
||||
}
|
||||
|
||||
m_qrCodes = qrCodeUtils::generateQrCodeImageSeries(m_config.toUtf8());
|
||||
auto qr = qrCodeUtils::generateQrCode(m_config.toUtf8());
|
||||
m_qrCodes << qrCodeUtils::svgToBase64(QString::fromStdString(toSvgString(qr, 1)));
|
||||
|
||||
emit exportConfigChanged();
|
||||
}
|
||||
@@ -189,7 +191,8 @@ void ExportController::generateAwgConfig(const QString &clientName)
|
||||
m_config.append(line + "\n");
|
||||
}
|
||||
|
||||
m_qrCodes = qrCodeUtils::generateQrCodeImageSeries(m_config.toUtf8());
|
||||
auto qr = qrCodeUtils::generateQrCode(m_config.toUtf8());
|
||||
m_qrCodes << qrCodeUtils::svgToBase64(QString::fromStdString(toSvgString(qr, 1)));
|
||||
|
||||
emit exportConfigChanged();
|
||||
}
|
||||
|
||||
@@ -83,8 +83,8 @@ void InstallController::install(DockerContainer container, int port, TransportPr
|
||||
|
||||
int s1 = QRandomGenerator::global()->bounded(15, 150);
|
||||
int s2 = QRandomGenerator::global()->bounded(15, 150);
|
||||
int s3 = QRandomGenerator::global()->bounded(1, 64);
|
||||
int s4 = QRandomGenerator::global()->bounded(1, 20);
|
||||
int s3 = QRandomGenerator::global()->bounded(0, 64);
|
||||
int s4 = QRandomGenerator::global()->bounded(0, 20);
|
||||
|
||||
// Ensure all values are unique and don't create equal packet sizes
|
||||
QSet<int> usedValues;
|
||||
@@ -97,12 +97,12 @@ void InstallController::install(DockerContainer container, int port, TransportPr
|
||||
|
||||
while (usedValues.contains(s3) || s1 + AwgConstant::messageInitiationSize == s3 + AwgConstant::messageCookieReplySize
|
||||
|| s2 + AwgConstant::messageResponseSize == s3 + AwgConstant::messageCookieReplySize) {
|
||||
s3 = QRandomGenerator::global()->bounded(1, 64);
|
||||
s3 = QRandomGenerator::global()->bounded(0, 64);
|
||||
}
|
||||
usedValues.insert(s3);
|
||||
|
||||
while (usedValues.contains(s4)) {
|
||||
s4 = QRandomGenerator::global()->bounded(1, 20);
|
||||
s4 = QRandomGenerator::global()->bounded(0, 20);
|
||||
}
|
||||
|
||||
QString initPacketJunkSize = QString::number(s1);
|
||||
@@ -987,94 +987,79 @@ void InstallController::addEmptyServer()
|
||||
emit installServerFinished(tr("Server added successfully"));
|
||||
}
|
||||
|
||||
void InstallController::validateConfig()
|
||||
bool InstallController::isConfigValid()
|
||||
{
|
||||
int serverIndex = m_serversModel->getDefaultServerIndex();
|
||||
QJsonObject serverConfigObject = m_serversModel->getServerConfig(serverIndex);
|
||||
|
||||
if (apiUtils::isServerFromApi(serverConfigObject)) {
|
||||
emit configValidated(true);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!m_serversModel->data(serverIndex, ServersModel::Roles::HasInstalledContainers).toBool()) {
|
||||
emit noInstalledContainers();
|
||||
emit configValidated(false);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
DockerContainer container = qvariant_cast<DockerContainer>(m_serversModel->data(serverIndex, ServersModel::Roles::DefaultContainerRole));
|
||||
|
||||
if (container == DockerContainer::None) {
|
||||
emit installationErrorOccurred(ErrorCode::NoInstalledContainersError);
|
||||
emit configValidated(false);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
|
||||
VpnConfigurationsController vpnConfigurationController(m_settings, serverController);
|
||||
|
||||
QJsonObject containerConfig = m_containersModel->getContainerConfig(container);
|
||||
ServerCredentials credentials = m_serversModel->getServerCredentials(serverIndex);
|
||||
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
|
||||
|
||||
auto isProtocolConfigExists = [](const QJsonObject &containerConfig, const DockerContainer container) {
|
||||
for (Proto protocol : ContainerProps::protocolsForContainer(container)) {
|
||||
QString protocolConfig =
|
||||
containerConfig.value(ProtocolProps::protoToString(protocol)).toObject().value(config_key::last_config).toString();
|
||||
QFutureWatcher<ErrorCode> watcher;
|
||||
|
||||
if (protocolConfig.isEmpty()) {
|
||||
return false;
|
||||
QFuture<ErrorCode> future = QtConcurrent::run([this, container, &credentials, &containerConfig, &serverController]() {
|
||||
ErrorCode errorCode = ErrorCode::NoError;
|
||||
|
||||
auto isProtocolConfigExists = [](const QJsonObject &containerConfig, const DockerContainer container) {
|
||||
for (Proto protocol : ContainerProps::protocolsForContainer(container)) {
|
||||
QString protocolConfig =
|
||||
containerConfig.value(ProtocolProps::protoToString(protocol)).toObject().value(config_key::last_config).toString();
|
||||
|
||||
if (protocolConfig.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
if (!isProtocolConfigExists(containerConfig, container)) {
|
||||
VpnConfigurationsController vpnConfigurationController(m_settings, serverController);
|
||||
errorCode = vpnConfigurationController.createProtocolConfigForContainer(credentials, container, containerConfig);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return errorCode;
|
||||
}
|
||||
m_serversModel->updateContainerConfig(container, containerConfig);
|
||||
|
||||
errorCode = m_clientManagementModel->appendClient(container, credentials, containerConfig,
|
||||
QString("Admin [%1]").arg(QSysInfo::prettyProductName()), serverController);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return errorCode;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
return errorCode;
|
||||
});
|
||||
|
||||
if (isProtocolConfigExists(containerConfig, container)) {
|
||||
emit configValidated(true);
|
||||
return;
|
||||
QEventLoop wait;
|
||||
connect(&watcher, &QFutureWatcher<ErrorCode>::finished, &wait, &QEventLoop::quit);
|
||||
watcher.setFuture(future);
|
||||
wait.exec();
|
||||
|
||||
ErrorCode errorCode = watcher.result();
|
||||
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
emit installationErrorOccurred(errorCode);
|
||||
return false;
|
||||
}
|
||||
|
||||
struct ValidationResult {
|
||||
ErrorCode errorCode = ErrorCode::NoError;
|
||||
QJsonObject containerConfig;
|
||||
};
|
||||
|
||||
QFuture<ValidationResult> future =
|
||||
QtConcurrent::run([settings = m_settings, serverController, credentials, containerConfig, container]() mutable {
|
||||
ValidationResult result;
|
||||
result.containerConfig = containerConfig;
|
||||
|
||||
VpnConfigurationsController vpnConfigurationController(settings, serverController);
|
||||
result.errorCode = vpnConfigurationController.createProtocolConfigForContainer(credentials, container,
|
||||
result.containerConfig);
|
||||
return result;
|
||||
});
|
||||
|
||||
auto *watcher = new QFutureWatcher<ValidationResult>(this);
|
||||
connect(watcher, &QFutureWatcher<ValidationResult>::finished, this,
|
||||
[this, watcher, container, credentials, serverController]() {
|
||||
auto result = watcher->result();
|
||||
watcher->deleteLater();
|
||||
|
||||
if (result.errorCode != ErrorCode::NoError) {
|
||||
emit installationErrorOccurred(result.errorCode);
|
||||
emit configValidated(false);
|
||||
return;
|
||||
}
|
||||
|
||||
m_serversModel->updateContainerConfig(container, result.containerConfig);
|
||||
|
||||
ErrorCode appendError = m_clientManagementModel->appendClient(
|
||||
container, credentials, result.containerConfig,
|
||||
QString("Admin [%1]").arg(QSysInfo::prettyProductName()), serverController);
|
||||
|
||||
if (appendError != ErrorCode::NoError) {
|
||||
emit installationErrorOccurred(appendError);
|
||||
emit configValidated(false);
|
||||
return;
|
||||
}
|
||||
|
||||
emit configValidated(true);
|
||||
});
|
||||
watcher->setFuture(future);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InstallController::isUpdateDockerContainerRequired(const DockerContainer container, const QJsonObject &oldConfig,
|
||||
@@ -1085,7 +1070,7 @@ bool InstallController::isUpdateDockerContainerRequired(const DockerContainer co
|
||||
const QJsonObject &oldProtoConfig = oldConfig.value(ProtocolProps::protoToString(mainProto)).toObject();
|
||||
const QJsonObject &newProtoConfig = newConfig.value(ProtocolProps::protoToString(mainProto)).toObject();
|
||||
|
||||
if (ContainerProps::isAwgContainer(container)) {
|
||||
if (container == DockerContainer::Awg2) {
|
||||
const AwgConfig oldConfig(oldProtoConfig);
|
||||
const AwgConfig newConfig(newProtoConfig);
|
||||
|
||||
|
||||
@@ -50,10 +50,9 @@ public slots:
|
||||
|
||||
void addEmptyServer();
|
||||
|
||||
void validateConfig();
|
||||
bool isConfigValid();
|
||||
|
||||
signals:
|
||||
void configValidated(bool isValid);
|
||||
void installContainerFinished(const QString &finishMessage, bool isServiceInstall);
|
||||
void installServerFinished(const QString &finishMessage);
|
||||
|
||||
|
||||
@@ -45,8 +45,6 @@ SettingsController::SettingsController(const QSharedPointer<ServersModel> &serve
|
||||
emit safeAreaBottomMarginChanged();
|
||||
emit safeAreaTopMarginChanged();
|
||||
});
|
||||
connect(AndroidController::instance(), &AndroidController::activityPaused, this, &SettingsController::activityPaused);
|
||||
connect(AndroidController::instance(), &AndroidController::activityResumed, this, &SettingsController::activityResumed);
|
||||
#endif
|
||||
|
||||
m_isDevModeEnabled = m_settings->isDevGatewayEnv();
|
||||
@@ -160,7 +158,6 @@ void SettingsController::clearLogs()
|
||||
|
||||
qInfo().noquote() << QString("Started %1 version %2 %3").arg(APPLICATION_NAME, APP_VERSION, GIT_COMMIT_HASH);
|
||||
qInfo().noquote() << QString("%1 (%2)").arg(QSysInfo::prettyProductName(), QSysInfo::currentCpuArchitecture());
|
||||
qInfo().noquote() << QString("SSL backend: %1").arg(QSslSocket::sslLibraryVersionString());
|
||||
}
|
||||
|
||||
void SettingsController::backupAppConfig(const QString &fileName)
|
||||
@@ -180,11 +177,12 @@ void SettingsController::backupAppConfig(const QString &fileName)
|
||||
|
||||
void SettingsController::restoreAppConfig(const QString &fileName)
|
||||
{
|
||||
QByteArray data;
|
||||
if (!SystemController::readFile(fileName, data)) {
|
||||
emit changeSettingsErrorOccurred(tr("Can't open file: %1").arg(fileName));
|
||||
return;
|
||||
}
|
||||
QFile file(fileName);
|
||||
|
||||
file.open(QIODevice::ReadOnly);
|
||||
|
||||
QByteArray data = file.readAll();
|
||||
|
||||
restoreAppConfigFromData(data);
|
||||
}
|
||||
|
||||
|
||||
@@ -141,9 +141,6 @@ signals:
|
||||
void safeAreaTopMarginChanged();
|
||||
void safeAreaBottomMarginChanged();
|
||||
|
||||
void activityPaused();
|
||||
void activityResumed();
|
||||
|
||||
void isHomeAdLabelVisibleChanged(bool visible);
|
||||
void startMinimizedChanged();
|
||||
|
||||
|
||||
@@ -7,8 +7,10 @@
|
||||
#include "systemController.h"
|
||||
#include "core/networkUtilities.h"
|
||||
|
||||
SitesController::SitesController(const std::shared_ptr<Settings> &settings, const QSharedPointer<SitesModel> &sitesModel, QObject *parent)
|
||||
: QObject(parent), m_settings(settings), m_sitesModel(sitesModel)
|
||||
SitesController::SitesController(const std::shared_ptr<Settings> &settings,
|
||||
const QSharedPointer<VpnConnection> &vpnConnection,
|
||||
const QSharedPointer<SitesModel> &sitesModel, QObject *parent)
|
||||
: QObject(parent), m_settings(settings), m_vpnConnection(vpnConnection), m_sitesModel(sitesModel)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -32,20 +34,32 @@ void SitesController::addSite(QString hostname)
|
||||
hostname = hostname.split("/", Qt::SkipEmptyParts).first();
|
||||
}
|
||||
|
||||
const auto &resolveCallback = [this](const QHostInfo &hostInfo) {
|
||||
const auto &processSite = [this](const QString &hostname, const QString &ip) {
|
||||
m_sitesModel->addSite(hostname, ip);
|
||||
|
||||
if (!ip.isEmpty()) {
|
||||
QMetaObject::invokeMethod(m_vpnConnection.get(), "addRoutes", Qt::QueuedConnection,
|
||||
Q_ARG(QStringList, QStringList() << ip));
|
||||
} else if (NetworkUtilities::ipAddressWithSubnetRegExp().exactMatch(hostname)) {
|
||||
QMetaObject::invokeMethod(m_vpnConnection.get(), "addRoutes", Qt::QueuedConnection,
|
||||
Q_ARG(QStringList, QStringList() << hostname));
|
||||
}
|
||||
};
|
||||
|
||||
const auto &resolveCallback = [this, processSite](const QHostInfo &hostInfo) {
|
||||
const QList<QHostAddress> &addresses = hostInfo.addresses();
|
||||
for (const QHostAddress &addr : hostInfo.addresses()) {
|
||||
if (addr.protocol() == QAbstractSocket::NetworkLayerProtocol::IPv4Protocol) {
|
||||
m_sitesModel->addSite(hostInfo.hostName(), addr.toString());
|
||||
processSite(hostInfo.hostName(), addr.toString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (NetworkUtilities::ipAddressWithSubnetRegExp().exactMatch(hostname)) {
|
||||
m_sitesModel->addSite(hostname, "");
|
||||
processSite(hostname, "");
|
||||
} else {
|
||||
m_sitesModel->addSite(hostname, "");
|
||||
processSite(hostname, "");
|
||||
QHostInfo::lookupHost(hostname, this, resolveCallback);
|
||||
}
|
||||
|
||||
@@ -58,6 +72,9 @@ void SitesController::removeSite(int index)
|
||||
auto hostname = m_sitesModel->data(modelIndex, SitesModel::Roles::UrlRole).toString();
|
||||
m_sitesModel->removeSite(modelIndex);
|
||||
|
||||
QMetaObject::invokeMethod(m_vpnConnection.get(), "deleteRoutes", Qt::QueuedConnection,
|
||||
Q_ARG(QStringList, QStringList() << hostname));
|
||||
|
||||
emit finished(tr("Site removed: %1").arg(hostname));
|
||||
}
|
||||
|
||||
@@ -111,6 +128,8 @@ void SitesController::importSites(const QString &fileName, bool replaceExisting)
|
||||
|
||||
m_sitesModel->addSites(sites, replaceExisting);
|
||||
|
||||
QMetaObject::invokeMethod(m_vpnConnection.get(), "addRoutes", Qt::QueuedConnection, Q_ARG(QStringList, ips));
|
||||
|
||||
emit finished(tr("Import completed"));
|
||||
}
|
||||
|
||||
|
||||
@@ -11,8 +11,9 @@ class SitesController : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SitesController(const std::shared_ptr<Settings> &settings, const QSharedPointer<SitesModel> &sitesModel,
|
||||
QObject *parent = nullptr);
|
||||
explicit SitesController(const std::shared_ptr<Settings> &settings,
|
||||
const QSharedPointer<VpnConnection> &vpnConnection,
|
||||
const QSharedPointer<SitesModel> &sitesModel, QObject *parent = nullptr);
|
||||
|
||||
public slots:
|
||||
void addSite(QString hostname);
|
||||
@@ -30,6 +31,8 @@ signals:
|
||||
|
||||
private:
|
||||
std::shared_ptr<Settings> m_settings;
|
||||
|
||||
QSharedPointer<VpnConnection> m_vpnConnection;
|
||||
QSharedPointer<SitesModel> m_sitesModel;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "apiAccountInfoModel.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include "core/api/apiUtils.h"
|
||||
@@ -33,7 +32,7 @@ QVariant ApiAccountInfoModel::data(const QModelIndex &index, int role) const
|
||||
}
|
||||
|
||||
return apiUtils::isSubscriptionExpired(m_accountInfoData.subscriptionEndDate) ? tr("<p><a style=\"color: #EB5757;\">Inactive</a>")
|
||||
: tr("<p><a style=\"color: #28c840;\">Active</a>");
|
||||
: tr("Active");
|
||||
}
|
||||
case EndDateRole: {
|
||||
if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaFreeV3) {
|
||||
@@ -53,9 +52,7 @@ QVariant ApiAccountInfoModel::data(const QModelIndex &index, int role) const
|
||||
}
|
||||
case IsComponentVisibleRole: {
|
||||
return m_accountInfoData.configType == apiDefs::ConfigType::AmneziaPremiumV2
|
||||
|| m_accountInfoData.configType == apiDefs::ConfigType::AmneziaTrialV2
|
||||
|| m_accountInfoData.configType == apiDefs::ConfigType::ExternalPremium
|
||||
|| m_accountInfoData.configType == apiDefs::ConfigType::ExternalTrial;
|
||||
|| m_accountInfoData.configType == apiDefs::ConfigType::ExternalPremium;
|
||||
}
|
||||
case HasExpiredWorkerRole: {
|
||||
for (int i = 0; i < m_issuedConfigsInfo.size(); i++) {
|
||||
@@ -76,18 +73,6 @@ QVariant ApiAccountInfoModel::data(const QModelIndex &index, int role) const
|
||||
}
|
||||
return false;
|
||||
}
|
||||
case IsSubscriptionExpiredRole: {
|
||||
if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaFreeV3) return false;
|
||||
if (m_accountInfoData.subscriptionEndDate.isEmpty()) return false;
|
||||
return apiUtils::isSubscriptionExpired(m_accountInfoData.subscriptionEndDate);
|
||||
}
|
||||
case IsSubscriptionExpiringSoonRole: {
|
||||
if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaFreeV3) return false;
|
||||
if (m_accountInfoData.subscriptionEndDate.isEmpty()) return false;
|
||||
if (apiUtils::isSubscriptionExpired(m_accountInfoData.subscriptionEndDate)) return false;
|
||||
QDateTime endDate = QDateTime::fromString(m_accountInfoData.subscriptionEndDate, Qt::ISODateWithMs);
|
||||
return endDate <= QDateTime::currentDateTimeUtc().addDays(10);
|
||||
}
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
@@ -179,8 +164,6 @@ QHash<int, QByteArray> ApiAccountInfoModel::roleNames() const
|
||||
roles[IsComponentVisibleRole] = "isComponentVisible";
|
||||
roles[HasExpiredWorkerRole] = "hasExpiredWorker";
|
||||
roles[IsProtocolSelectionSupportedRole] = "isProtocolSelectionSupported";
|
||||
roles[IsSubscriptionExpiredRole] = "isSubscriptionExpired";
|
||||
roles[IsSubscriptionExpiringSoonRole] = "isSubscriptionExpiringSoon";
|
||||
|
||||
return roles;
|
||||
}
|
||||
|
||||
@@ -19,9 +19,7 @@ public:
|
||||
EndDateRole,
|
||||
IsComponentVisibleRole,
|
||||
HasExpiredWorkerRole,
|
||||
IsProtocolSelectionSupportedRole,
|
||||
IsSubscriptionExpiredRole,
|
||||
IsSubscriptionExpiringSoonRole
|
||||
IsProtocolSelectionSupportedRole
|
||||
};
|
||||
|
||||
explicit ApiAccountInfoModel(QObject *parent = nullptr);
|
||||
@@ -33,6 +31,7 @@ public:
|
||||
public slots:
|
||||
void updateModel(const QJsonObject &accountInfoObject, const QJsonObject &serverConfig);
|
||||
QVariant data(const QString &roleString);
|
||||
|
||||
QJsonArray getAvailableCountries();
|
||||
QJsonArray getIssuedConfigsInfo();
|
||||
|
||||
|
||||
@@ -41,7 +41,6 @@ namespace
|
||||
{
|
||||
constexpr char amneziaFree[] = "amnezia-free";
|
||||
constexpr char amneziaPremium[] = "amnezia-premium";
|
||||
constexpr char amneziaTrial[] = "amnezia-trial";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +69,7 @@ QVariant ApiServicesModel::data(const QModelIndex &index, int role) const
|
||||
}
|
||||
case CardDescriptionRole: {
|
||||
auto speed = apiServiceData.serviceInfo.speed;
|
||||
if (serviceType == serviceType::amneziaPremium || serviceType == serviceType::amneziaTrial) {
|
||||
if (serviceType == serviceType::amneziaPremium) {
|
||||
return apiServiceData.serviceInfo.cardDescription.arg(speed);
|
||||
} else if (serviceType == serviceType::amneziaFree) {
|
||||
QString description = apiServiceData.serviceInfo.cardDescription;
|
||||
@@ -125,10 +124,8 @@ QVariant ApiServicesModel::data(const QModelIndex &index, int role) const
|
||||
case OrderRole: {
|
||||
if (serviceType == serviceType::amneziaPremium) {
|
||||
return 0;
|
||||
} else if (serviceType == serviceType::amneziaTrial) {
|
||||
return 1;
|
||||
} else if (serviceType == serviceType::amneziaFree) {
|
||||
return 2;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,12 +141,10 @@ void AwgConfigModel::updateModel(const QJsonObject &config)
|
||||
serverProtocolConfig.value(config_key::initPacketJunkSize).toString(protocols::awg::defaultInitPacketJunkSize);
|
||||
m_serverProtocolConfig[config_key::responsePacketJunkSize] =
|
||||
serverProtocolConfig.value(config_key::responsePacketJunkSize).toString(protocols::awg::defaultResponsePacketJunkSize);
|
||||
if (protocolVersion == protocols::awg::awgV2) {
|
||||
m_serverProtocolConfig[config_key::cookieReplyPacketJunkSize] =
|
||||
serverProtocolConfig.value(config_key::cookieReplyPacketJunkSize).toString(protocols::awg::defaultCookieReplyPacketJunkSize);
|
||||
m_serverProtocolConfig[config_key::transportPacketJunkSize] =
|
||||
serverProtocolConfig.value(config_key::transportPacketJunkSize).toString(protocols::awg::defaultTransportPacketJunkSize);
|
||||
}
|
||||
m_serverProtocolConfig[config_key::cookieReplyPacketJunkSize] =
|
||||
serverProtocolConfig.value(config_key::cookieReplyPacketJunkSize).toString(protocols::awg::defaultCookieReplyPacketJunkSize);
|
||||
m_serverProtocolConfig[config_key::transportPacketJunkSize] =
|
||||
serverProtocolConfig.value(config_key::transportPacketJunkSize).toString(protocols::awg::defaultTransportPacketJunkSize);
|
||||
m_serverProtocolConfig[config_key::initPacketMagicHeader] =
|
||||
serverProtocolConfig.value(config_key::initPacketMagicHeader).toString(protocols::awg::defaultInitPacketMagicHeader);
|
||||
m_serverProtocolConfig[config_key::responsePacketMagicHeader] =
|
||||
|
||||
@@ -179,20 +179,6 @@ QVariant ServersModel::data(const QModelIndex &index, int role) const
|
||||
case AdEndpointRole: {
|
||||
return apiConfig.value(apiDefs::key::serviceInfo).toObject().value(apiDefs::key::adEndpoint).toString();
|
||||
}
|
||||
case IsSubscriptionExpiredRole: {
|
||||
if (configVersion != apiDefs::ConfigSource::AmneziaGateway) return false;
|
||||
QString endDate = apiConfig.value(apiDefs::key::subscriptionEndDate).toString();
|
||||
if (endDate.isEmpty()) return false;
|
||||
return apiUtils::isSubscriptionExpired(endDate);
|
||||
}
|
||||
case IsSubscriptionExpiringSoonRole: {
|
||||
if (configVersion != apiDefs::ConfigSource::AmneziaGateway) return false;
|
||||
QString endDate = apiConfig.value(apiDefs::key::subscriptionEndDate).toString();
|
||||
if (endDate.isEmpty()) return false;
|
||||
if (apiUtils::isSubscriptionExpired(endDate)) return false;
|
||||
QDateTime endDateTime = QDateTime::fromString(endDate, Qt::ISODateWithMs);
|
||||
return endDateTime <= QDateTime::currentDateTimeUtc().addDays(10);
|
||||
}
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
@@ -457,9 +443,6 @@ QHash<int, QByteArray> ServersModel::roleNames() const
|
||||
roles[AdDescriptionRole] = "adDescription";
|
||||
roles[AdEndpointRole] = "adEndpoint";
|
||||
|
||||
roles[IsSubscriptionExpiredRole] = "isSubscriptionExpired";
|
||||
roles[IsSubscriptionExpiringSoonRole] = "isSubscriptionExpiringSoon";
|
||||
|
||||
return roles;
|
||||
}
|
||||
|
||||
|
||||
@@ -52,9 +52,6 @@ public:
|
||||
AdDescriptionRole,
|
||||
AdEndpointRole,
|
||||
|
||||
IsSubscriptionExpiredRole,
|
||||
IsSubscriptionExpiringSoonRole,
|
||||
|
||||
HasAmneziaDns
|
||||
};
|
||||
|
||||
|
||||
@@ -126,18 +126,6 @@ ListViewType {
|
||||
}
|
||||
}
|
||||
|
||||
CaptionTextType {
|
||||
visible: isServerFromGatewayApi && (isSubscriptionExpired || isSubscriptionExpiringSoon)
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 64
|
||||
Layout.bottomMargin: 8
|
||||
|
||||
text: isSubscriptionExpired ? qsTr("Subscription expired. Please renew.") : qsTr("Subscription expiring soon.")
|
||||
color: isSubscriptionExpired ? AmneziaStyle.color.vibrantRed : AmneziaStyle.color.goldenApricot
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
DividerType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 0
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import PageEnum 1.0
|
||||
import Style 1.0
|
||||
|
||||
import "../Controls2"
|
||||
import "../Controls2/TextTypes"
|
||||
|
||||
DrawerType2 {
|
||||
id: root
|
||||
|
||||
expandedStateContent: ColumnLayout {
|
||||
id: content
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
spacing: 0
|
||||
|
||||
onImplicitHeightChanged: {
|
||||
root.expandedHeight = content.implicitHeight + 32 + SettingsController.safeAreaBottomMargin
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
implicitHeight: titleText.implicitHeight
|
||||
|
||||
Header2TextType {
|
||||
id: titleText
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
text: qsTr("Amnezia Premium subscription has expired")
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
}
|
||||
}
|
||||
|
||||
ParagraphTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
|
||||
text: qsTr("Renew your subscription to continue using VPN")
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
|
||||
text: qsTr("Renew")
|
||||
|
||||
defaultColor: AmneziaStyle.color.paleGray
|
||||
hoveredColor: AmneziaStyle.color.lightGray
|
||||
pressedColor: AmneziaStyle.color.mutedGray
|
||||
textColor: AmneziaStyle.color.midnightBlack
|
||||
|
||||
clickedFunc: function() {
|
||||
ApiSettingsController.getRenewalLink()
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.topMargin: 8
|
||||
Layout.bottomMargin: 8
|
||||
|
||||
implicitHeight: 25
|
||||
|
||||
defaultColor: AmneziaStyle.color.transparent
|
||||
hoveredColor: AmneziaStyle.color.translucentWhite
|
||||
pressedColor: AmneziaStyle.color.sheerWhite
|
||||
textColor: AmneziaStyle.color.goldenApricot
|
||||
|
||||
text: qsTr("Support")
|
||||
|
||||
clickedFunc: function() {
|
||||
root.closeTriggered()
|
||||
PageController.goToPage(PageEnum.PageSettingsApiSupport)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
|
||||
import Style 1.0
|
||||
|
||||
@@ -38,7 +37,6 @@ Item {
|
||||
property int borderFocusedWidth: 1
|
||||
|
||||
property string rightImageColor: AmneziaStyle.color.paleGray
|
||||
property string leftImageColor: ""
|
||||
|
||||
property bool descriptionOnTop: false
|
||||
property bool hideDescription: true
|
||||
@@ -142,14 +140,6 @@ Item {
|
||||
|
||||
anchors.centerIn: parent
|
||||
source: leftImageSource
|
||||
visible: leftImageColor === ""
|
||||
}
|
||||
|
||||
ColorOverlay {
|
||||
anchors.fill: leftImage
|
||||
source: leftImage
|
||||
color: leftImageColor
|
||||
visible: leftImageColor !== ""
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,10 +8,9 @@ Item {
|
||||
id: root
|
||||
|
||||
property StackView stackView: StackView.view
|
||||
property bool enableTimer: true
|
||||
|
||||
onVisibleChanged: {
|
||||
if (visible && enableTimer) {
|
||||
if (visible) {
|
||||
timer.start()
|
||||
}
|
||||
}
|
||||
@@ -25,6 +24,6 @@ Item {
|
||||
FocusController.setFocusOnDefaultItem()
|
||||
}
|
||||
repeat: false // Stop the timer after one trigger
|
||||
running: enableTimer // Start the timer
|
||||
running: true // Start the timer
|
||||
}
|
||||
}
|
||||
|
||||
@@ -330,8 +330,6 @@ PageType {
|
||||
AwgTextField {
|
||||
id: cookieReplyPacketJunkSizeTextField
|
||||
|
||||
visible: isAwg2
|
||||
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
@@ -344,8 +342,6 @@ PageType {
|
||||
AwgTextField {
|
||||
id: transportPacketJunkSizeTextField
|
||||
|
||||
visible: isAwg2
|
||||
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
|
||||
@@ -18,23 +18,12 @@ PageType {
|
||||
id: root
|
||||
|
||||
property var processedServer
|
||||
property bool subscriptionExpired: false
|
||||
property bool subscriptionExpiringSoon: false
|
||||
function updateSubscriptionState() {
|
||||
root.subscriptionExpired = ServersModel.getProcessedServerData("isSubscriptionExpired")
|
||||
root.subscriptionExpiringSoon = ServersModel.getProcessedServerData("isSubscriptionExpiringSoon")
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
root.updateSubscriptionState()
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: ServersModel
|
||||
|
||||
function onProcessedServerChanged() {
|
||||
root.processedServer = proxyServersModel.get(0)
|
||||
root.updateSubscriptionState()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,11 +76,12 @@ PageType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.bottomMargin: 4
|
||||
Layout.bottomMargin: 10
|
||||
|
||||
actionButtonImage: "qrc:/images/controls/settings.svg"
|
||||
|
||||
headerText: root.processedServer.name
|
||||
descriptionText: qsTr("Location for connection")
|
||||
|
||||
actionButtonFunction: function() {
|
||||
PageController.showBusyIndicator(true)
|
||||
@@ -104,50 +94,6 @@ PageType {
|
||||
PageController.goToPage(PageEnum.PageSettingsApiServerInfo)
|
||||
}
|
||||
}
|
||||
|
||||
CaptionTextType {
|
||||
visible: root.subscriptionExpired || root.subscriptionExpiringSoon
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.topMargin: 4
|
||||
|
||||
text: root.subscriptionExpired ? qsTr("Subscription expired") : qsTr("Subscription expiring soon")
|
||||
color: root.subscriptionExpired ? AmneziaStyle.color.vibrantRed : AmneziaStyle.color.goldenApricot
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
visible: root.subscriptionExpired || root.subscriptionExpiringSoon
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.topMargin: 8
|
||||
Layout.bottomMargin: 4
|
||||
|
||||
defaultColor: AmneziaStyle.color.paleGray
|
||||
hoveredColor: AmneziaStyle.color.lightGray
|
||||
pressedColor: AmneziaStyle.color.mutedGray
|
||||
textColor: AmneziaStyle.color.midnightBlack
|
||||
|
||||
text: qsTr("Renew subscription")
|
||||
|
||||
clickedFunc: function() {
|
||||
ApiSettingsController.getRenewalLink()
|
||||
}
|
||||
}
|
||||
|
||||
CaptionTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.topMargin: (root.subscriptionExpired || root.subscriptionExpiringSoon) ? 8 : 4
|
||||
Layout.bottomMargin: 8
|
||||
|
||||
text: qsTr("Location for connection")
|
||||
color: AmneziaStyle.color.mutedGray
|
||||
}
|
||||
}
|
||||
|
||||
delegate: ColumnLayout {
|
||||
|
||||
@@ -2,7 +2,6 @@ import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Dialogs
|
||||
import Qt5Compat.GraphicalEffects
|
||||
|
||||
import SortFilterProxyModel 0.2
|
||||
|
||||
@@ -53,26 +52,6 @@ PageType {
|
||||
|
||||
property var processedServer
|
||||
|
||||
property bool isSubscriptionExpired: false
|
||||
property bool isSubscriptionExpiringSoon: false
|
||||
|
||||
function updateSubscriptionState() {
|
||||
root.isSubscriptionExpired = ApiAccountInfoModel.data("isSubscriptionExpired")
|
||||
root.isSubscriptionExpiringSoon = ApiAccountInfoModel.data("isSubscriptionExpiringSoon")
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
root.updateSubscriptionState()
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: ApiAccountInfoModel
|
||||
|
||||
function onModelReset() {
|
||||
root.updateSubscriptionState()
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: ServersModel
|
||||
|
||||
@@ -129,66 +108,12 @@ PageType {
|
||||
actionButtonImage: "qrc:/images/controls/edit-3.svg"
|
||||
|
||||
headerText: root.processedServer.name
|
||||
descriptionText: ApiAccountInfoModel.data("serviceDescription")
|
||||
|
||||
actionButtonFunction: function() {
|
||||
serverNameEditDrawer.openTriggered()
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
visible: root.isSubscriptionExpired || root.isSubscriptionExpiringSoon
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.topMargin: 4
|
||||
|
||||
text: root.isSubscriptionExpired
|
||||
? qsTr("Subscription expired")
|
||||
: qsTr("Subscription expiring soon")
|
||||
|
||||
color: root.isSubscriptionExpired
|
||||
? AmneziaStyle.color.vibrantRed
|
||||
: AmneziaStyle.color.goldenApricot
|
||||
|
||||
font.pixelSize: 14
|
||||
font.weight: Font.Medium
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
ParagraphTextType {
|
||||
visible: ApiAccountInfoModel.data("serviceDescription") !== ""
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.topMargin: 16
|
||||
Layout.bottomMargin: root.isSubscriptionExpired || root.isSubscriptionExpiringSoon ? 0 : 10
|
||||
|
||||
text: ApiAccountInfoModel.data("serviceDescription")
|
||||
color: AmneziaStyle.color.mutedGray
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
visible: root.isSubscriptionExpired || root.isSubscriptionExpiringSoon
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.topMargin: 8
|
||||
Layout.bottomMargin: 8
|
||||
|
||||
text: qsTr("Renew subscription")
|
||||
|
||||
defaultColor: AmneziaStyle.color.paleGray
|
||||
hoveredColor: AmneziaStyle.color.lightGray
|
||||
pressedColor: AmneziaStyle.color.mutedGray
|
||||
textColor: AmneziaStyle.color.midnightBlack
|
||||
|
||||
clickedFunc: function() {
|
||||
ApiSettingsController.getRenewalLink()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delegate: ColumnLayout {
|
||||
@@ -226,54 +151,6 @@ PageType {
|
||||
|
||||
readonly property bool isVisibleForAmneziaFree: ApiAccountInfoModel.data("isComponentVisible")
|
||||
|
||||
Item {
|
||||
visible: !root.isSubscriptionExpired && !root.isSubscriptionExpiringSoon
|
||||
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: renewRow.implicitHeight + 32
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: ApiSettingsController.getRenewalLink()
|
||||
}
|
||||
|
||||
Row {
|
||||
id: renewRow
|
||||
anchors.centerIn: parent
|
||||
spacing: 12
|
||||
|
||||
Item {
|
||||
width: renewIcon.implicitWidth
|
||||
height: renewIcon.implicitHeight
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Image {
|
||||
id: renewIcon
|
||||
source: "qrc:/images/controls/refresh-cw.svg"
|
||||
}
|
||||
|
||||
ColorOverlay {
|
||||
anchors.fill: renewIcon
|
||||
source: renewIcon
|
||||
color: AmneziaStyle.color.goldenApricot
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
text: qsTr("Renew subscription")
|
||||
color: AmneziaStyle.color.goldenApricot
|
||||
font.pixelSize: 18
|
||||
font.weight: Font.Medium
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {
|
||||
visible: !root.isSubscriptionExpired && !root.isSubscriptionExpiringSoon
|
||||
}
|
||||
|
||||
SwitcherType {
|
||||
id: switcher
|
||||
|
||||
@@ -300,14 +177,10 @@ PageType {
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {
|
||||
visible: footer.isVisibleForAmneziaFree
|
||||
}
|
||||
|
||||
WarningType {
|
||||
id: warning
|
||||
|
||||
Layout.topMargin: 24
|
||||
Layout.topMargin: 32
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.fillWidth: true
|
||||
@@ -331,7 +204,7 @@ PageType {
|
||||
id: vpnKey
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: warning.visible ? 16 : 0
|
||||
Layout.topMargin: warning.visible ? 16 : 32
|
||||
|
||||
visible: footer.isVisibleForAmneziaFree
|
||||
|
||||
|
||||
@@ -178,7 +178,7 @@ PageType {
|
||||
Layout.margins: 16
|
||||
|
||||
text: qsTr("News Notification")
|
||||
descriptionText: qsTr("Show a notification icon for unread news")
|
||||
descriptionText: qsTr("Show notification icon when has unread news")
|
||||
|
||||
checked: SettingsController.isNewsNotificationsEnabled()
|
||||
onToggled: function() {
|
||||
|
||||
@@ -1,226 +1,226 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Dialogs
|
||||
|
||||
import PageEnum 1.0
|
||||
import Style 1.0
|
||||
|
||||
import "./"
|
||||
import "../Controls2"
|
||||
import "../Controls2/TextTypes"
|
||||
import "../Config"
|
||||
import "../Components"
|
||||
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 20 + SettingsController.safeAreaTopMargin
|
||||
|
||||
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.topMargin: 8
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.bottomMargin: 32
|
||||
|
||||
headerText: ApiServicesModel.getSelectedServiceData("name")
|
||||
descriptionText: ApiServicesModel.getSelectedServiceData("serviceDescription")
|
||||
}
|
||||
}
|
||||
|
||||
model: inputFields
|
||||
spacing: 0
|
||||
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
LabelWithImageType {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: 16
|
||||
|
||||
imageSource: imagePath
|
||||
leftText: lText
|
||||
rightText: rText
|
||||
|
||||
visible: isVisible
|
||||
}
|
||||
}
|
||||
|
||||
footer: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
spacing: 0
|
||||
|
||||
ParagraphTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
|
||||
onLinkActivated: function(link) {
|
||||
Qt.openUrlExternally(link)
|
||||
}
|
||||
textFormat: Text.RichText
|
||||
text: {
|
||||
var text = ApiServicesModel.getSelectedServiceData("features")
|
||||
return text.replace("%1", LanguageModel.getCurrentSiteUrl("free")).replace("/free", "") // todo link should come from gateway
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.NoButton
|
||||
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
}
|
||||
}
|
||||
|
||||
ParagraphTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
visible: (Qt.platform.os === "ios" || IsMacOsNeBuild) && ApiServicesModel.getSelectedServiceType() === "amnezia-premium"
|
||||
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
textFormat: Text.PlainText
|
||||
color: AmneziaStyle.color.mutedGray
|
||||
font.pixelSize: 12
|
||||
|
||||
text: qsTr("Charged to your Apple ID at confirmation. Renews automatically unless auto-renew is turned off at least 24 hours before period end. Manage in Apple ID settings.")
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: continueButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
Layout.bottomMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: ApiServicesModel.getSelectedServiceType() === "amnezia-premium" ? qsTr("Subscribe Now") : (ApiServicesModel.getSelectedServiceType() === "amnezia-trial" ? qsTr("Try Trial") : qsTr("Connect"))
|
||||
|
||||
clickedFunc: function() {
|
||||
PageController.showBusyIndicator(true)
|
||||
var result = ApiConfigsController.importService()
|
||||
PageController.showBusyIndicator(false)
|
||||
|
||||
if (!result) {
|
||||
var endpoint = ApiServicesModel.getStoreEndpoint()
|
||||
Qt.openUrlExternally(endpoint)
|
||||
PageController.closePage()
|
||||
PageController.closePage()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ParagraphTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.bottomMargin: 32
|
||||
|
||||
visible: (Qt.platform.os === "ios" || IsMacOsNeBuild) && ApiServicesModel.getSelectedServiceType() === "amnezia-premium"
|
||||
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
textFormat: Text.RichText
|
||||
color: AmneziaStyle.color.mutedGray
|
||||
font.pixelSize: 12
|
||||
|
||||
text: {
|
||||
var termsUrl = "https://www.apple.com/legal/internet-services/itunes/dev/stdeula/"
|
||||
var privacyUrl = LanguageModel.getCurrentSiteUrl("policy")
|
||||
return qsTr("By continuing, you agree to the <a href=\"%1\" style=\"color: #FBB26A;\">Terms of Use</a> and <a href=\"%2\" style=\"color: #FBB26A;\">Privacy Policy</a>").arg(termsUrl).arg(privacyUrl)
|
||||
}
|
||||
|
||||
onLinkActivated: function(link) {
|
||||
Qt.openUrlExternally(link)
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.NoButton
|
||||
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property list<QtObject> inputFields: [
|
||||
region,
|
||||
price,
|
||||
timeLimit,
|
||||
speed,
|
||||
features
|
||||
]
|
||||
|
||||
QtObject {
|
||||
id: region
|
||||
|
||||
readonly property string imagePath: "qrc:/images/controls/map-pin.svg"
|
||||
readonly property string lText: qsTr("For the region")
|
||||
readonly property string rText: ApiServicesModel.getSelectedServiceData("region")
|
||||
property bool isVisible: true
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: price
|
||||
|
||||
readonly property string imagePath: "qrc:/images/controls/tag.svg"
|
||||
readonly property string lText: qsTr("Price")
|
||||
readonly property string rText: ApiServicesModel.getSelectedServiceData("price")
|
||||
property bool isVisible: true
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: timeLimit
|
||||
|
||||
readonly property string imagePath: "qrc:/images/controls/history.svg"
|
||||
readonly property string lText: qsTr("Work period")
|
||||
readonly property string rText: ApiServicesModel.getSelectedServiceData("timeLimit")
|
||||
property bool isVisible: rText !== ""
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: speed
|
||||
|
||||
readonly property string imagePath: "qrc:/images/controls/gauge.svg"
|
||||
readonly property string lText: qsTr("Speed")
|
||||
readonly property string rText: ApiServicesModel.getSelectedServiceData("speed")
|
||||
property bool isVisible: true
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: features
|
||||
|
||||
readonly property string imagePath: "qrc:/images/controls/info.svg"
|
||||
readonly property string lText: qsTr("Features")
|
||||
readonly property string rText: ""
|
||||
property bool isVisible: true
|
||||
}
|
||||
}
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Dialogs
|
||||
|
||||
import PageEnum 1.0
|
||||
import Style 1.0
|
||||
|
||||
import "./"
|
||||
import "../Controls2"
|
||||
import "../Controls2/TextTypes"
|
||||
import "../Config"
|
||||
import "../Components"
|
||||
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 20 + SettingsController.safeAreaTopMargin
|
||||
|
||||
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.topMargin: 8
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.bottomMargin: 32
|
||||
|
||||
headerText: ApiServicesModel.getSelectedServiceData("name")
|
||||
descriptionText: ApiServicesModel.getSelectedServiceData("serviceDescription")
|
||||
}
|
||||
}
|
||||
|
||||
model: inputFields
|
||||
spacing: 0
|
||||
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
LabelWithImageType {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: 16
|
||||
|
||||
imageSource: imagePath
|
||||
leftText: lText
|
||||
rightText: rText
|
||||
|
||||
visible: isVisible
|
||||
}
|
||||
}
|
||||
|
||||
footer: ColumnLayout {
|
||||
width: listView.width
|
||||
|
||||
spacing: 0
|
||||
|
||||
ParagraphTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
|
||||
onLinkActivated: function(link) {
|
||||
Qt.openUrlExternally(link)
|
||||
}
|
||||
textFormat: Text.RichText
|
||||
text: {
|
||||
var text = ApiServicesModel.getSelectedServiceData("features")
|
||||
return text.replace("%1", LanguageModel.getCurrentSiteUrl("free")).replace("/free", "") // todo link should come from gateway
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.NoButton
|
||||
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
}
|
||||
}
|
||||
|
||||
ParagraphTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
visible: (Qt.platform.os === "ios" || IsMacOsNeBuild) && ApiServicesModel.getSelectedServiceType() === "amnezia-premium"
|
||||
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
textFormat: Text.PlainText
|
||||
color: AmneziaStyle.color.mutedGray
|
||||
font.pixelSize: 12
|
||||
|
||||
text: qsTr("Charged to your Apple ID at confirmation. Renews automatically unless auto-renew is turned off at least 24 hours before period end. Manage in Apple ID settings.")
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: continueButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
Layout.bottomMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
text: ApiServicesModel.getSelectedServiceType() === "amnezia-premium" ? qsTr("Subscribe Now") : qsTr("Connect")
|
||||
|
||||
clickedFunc: function() {
|
||||
PageController.showBusyIndicator(true)
|
||||
var result = ApiConfigsController.importService()
|
||||
PageController.showBusyIndicator(false)
|
||||
|
||||
if (!result) {
|
||||
var endpoint = ApiServicesModel.getStoreEndpoint()
|
||||
Qt.openUrlExternally(endpoint)
|
||||
PageController.closePage()
|
||||
PageController.closePage()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ParagraphTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.bottomMargin: 32
|
||||
|
||||
visible: (Qt.platform.os === "ios" || IsMacOsNeBuild) && ApiServicesModel.getSelectedServiceType() === "amnezia-premium"
|
||||
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
textFormat: Text.RichText
|
||||
color: AmneziaStyle.color.mutedGray
|
||||
font.pixelSize: 12
|
||||
|
||||
text: {
|
||||
var termsUrl = "https://www.apple.com/legal/internet-services/itunes/dev/stdeula/"
|
||||
var privacyUrl = LanguageModel.getCurrentSiteUrl("policy")
|
||||
return qsTr("By continuing, you agree to the <a href=\"%1\" style=\"color: #FBB26A;\">Terms of Use</a> and <a href=\"%2\" style=\"color: #FBB26A;\">Privacy Policy</a>").arg(termsUrl).arg(privacyUrl)
|
||||
}
|
||||
|
||||
onLinkActivated: function(link) {
|
||||
Qt.openUrlExternally(link)
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.NoButton
|
||||
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property list<QtObject> inputFields: [
|
||||
region,
|
||||
price,
|
||||
timeLimit,
|
||||
speed,
|
||||
features
|
||||
]
|
||||
|
||||
QtObject {
|
||||
id: region
|
||||
|
||||
readonly property string imagePath: "qrc:/images/controls/map-pin.svg"
|
||||
readonly property string lText: qsTr("For the region")
|
||||
readonly property string rText: ApiServicesModel.getSelectedServiceData("region")
|
||||
property bool isVisible: true
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: price
|
||||
|
||||
readonly property string imagePath: "qrc:/images/controls/tag.svg"
|
||||
readonly property string lText: qsTr("Price")
|
||||
readonly property string rText: ApiServicesModel.getSelectedServiceData("price")
|
||||
property bool isVisible: true
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: timeLimit
|
||||
|
||||
readonly property string imagePath: "qrc:/images/controls/history.svg"
|
||||
readonly property string lText: qsTr("Work period")
|
||||
readonly property string rText: ApiServicesModel.getSelectedServiceData("timeLimit")
|
||||
property bool isVisible: rText !== ""
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: speed
|
||||
|
||||
readonly property string imagePath: "qrc:/images/controls/gauge.svg"
|
||||
readonly property string lText: qsTr("Speed")
|
||||
readonly property string rText: ApiServicesModel.getSelectedServiceData("speed")
|
||||
property bool isVisible: true
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: features
|
||||
|
||||
readonly property string imagePath: "qrc:/images/controls/info.svg"
|
||||
readonly property string lText: qsTr("Features")
|
||||
readonly property string rText: ""
|
||||
property bool isVisible: true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,23 +79,11 @@ PageType {
|
||||
}
|
||||
|
||||
textField.onTextChanged: {
|
||||
if (headerText === qsTr("Password or SSH private key")) {
|
||||
if (headerText == qsTr("Password or SSH private key")) {
|
||||
buttonImageSource = textField.text !== "" ? imageSource : ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WarningType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.topMargin: 8
|
||||
|
||||
visible: title === qsTr("Password or SSH private key")
|
||||
backGroundColor: AmneziaStyle.color.translucentWhite
|
||||
iconPath: "qrc:/images/controls/alert-circle.svg"
|
||||
textString: qsTr("SSH key requirements: supported ED25519 or RSA in PEM. Paste the private key including BEGIN/END lines. If your key doesn’t work, generate a compatible one.")
|
||||
}
|
||||
}
|
||||
|
||||
footer: ColumnLayout {
|
||||
|
||||
@@ -13,7 +13,6 @@ import "../Components"
|
||||
|
||||
PageType {
|
||||
id: root
|
||||
enableTimer: (SettingsController.isOnTv()) ? false : true
|
||||
|
||||
ColumnLayout {
|
||||
id: content
|
||||
@@ -46,22 +45,4 @@ PageType {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
interval: 250
|
||||
running: SettingsController.isOnTv()
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
startButton.forceActiveFocus()
|
||||
if (startButton.activeFocus) {
|
||||
running = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onVisibleChanged: {
|
||||
if (visible && SettingsController.isOnTv()) {
|
||||
startButton.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -505,13 +505,7 @@ PageType {
|
||||
exportTypeSelector.currentIndex = 0
|
||||
}
|
||||
selectedIndex = exportTypeSelector.currentIndex
|
||||
if (model.length > 0 && model[selectedIndex] && model[selectedIndex].name !== undefined) {
|
||||
exportTypeSelectorListView.selectedText = model[selectedIndex].name
|
||||
exportTypeSelector.text = model[selectedIndex].name
|
||||
} else {
|
||||
exportTypeSelectorListView.selectedText = ""
|
||||
exportTypeSelector.text = ""
|
||||
}
|
||||
exportTypeSelector.text = selectedText
|
||||
}
|
||||
|
||||
rootWidth: root.width
|
||||
|
||||
@@ -278,6 +278,7 @@ PageType {
|
||||
}
|
||||
|
||||
Keys.onPressed: function(event) {
|
||||
console.debug(">>>> ", event.key, " Event is caught by StartPage")
|
||||
switch (event.key) {
|
||||
case Qt.Key_Tab:
|
||||
case Qt.Key_Down:
|
||||
@@ -303,7 +304,7 @@ PageType {
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
|
||||
// Also adjust TabBar position when keyboard appears (Android 14+ workaround)
|
||||
anchors.bottomMargin: SettingsController.imeHeight
|
||||
|
||||
|
||||
@@ -21,27 +21,15 @@ Window {
|
||||
function onStateChanged() {
|
||||
if (Qt.platform.os === "android") {
|
||||
if (Qt.application.state === Qt.ApplicationActive) {
|
||||
root.visible = true
|
||||
refreshTimer.restart()
|
||||
} else if (Qt.application.state === Qt.ApplicationSuspended ||
|
||||
Qt.application.state === Qt.ApplicationInactive) {
|
||||
console.log("QML: Application going to background, state:", Qt.application.state)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Hide the window immediately when Android Activity.onPause() fires so that
|
||||
// Qt's render loop stops before the EGL surface is disconnected. This
|
||||
// prevents "QRhiGles2: Failed to make context current" and the resulting
|
||||
// black screen that appears after swiping home and returning.
|
||||
Connections {
|
||||
target: SettingsController
|
||||
function onActivityPaused() {
|
||||
if (Qt.platform.os === "android") root.visible = false
|
||||
}
|
||||
function onActivityResumed() {
|
||||
if (Qt.platform.os === "android") root.visible = true
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: refreshTimer
|
||||
interval: 150
|
||||
@@ -68,11 +56,6 @@ Window {
|
||||
PageController.closeWindow()
|
||||
}
|
||||
|
||||
onSceneGraphError: function(error, message) {
|
||||
// Prevent qFatal crash on Android when EGL context is lost
|
||||
console.warn("Scene graph error:", error, message)
|
||||
}
|
||||
|
||||
title: "AmneziaVPN"
|
||||
|
||||
Item { // This item is needed for focus handling
|
||||
@@ -288,34 +271,6 @@ Window {
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
objectName: "subscriptionExpiredDrawerItem"
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
SubscriptionExpiredDrawer {
|
||||
id: subscriptionExpiredDrawer
|
||||
|
||||
anchors.fill: parent
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: ApiConfigsController
|
||||
|
||||
function onSubscriptionExpiredOnServer() {
|
||||
subscriptionExpiredDrawer.openTriggered()
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: ApiSettingsController
|
||||
|
||||
function onRenewalLinkReceived(url) {
|
||||
Qt.openUrlExternally(url)
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
objectName: "busyIndicatorItem"
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ VpnConnection::VpnConnection(std::shared_ptr<Settings> settings, QObject *parent
|
||||
{
|
||||
#if defined(Q_OS_IOS) || defined(MACOS_NE)
|
||||
m_checkTimer.setInterval(1000);
|
||||
connect(IosController::Instance(), &IosController::connectionStateChanged, this, &VpnConnection::setConnectionState);
|
||||
connect(IosController::Instance(), &IosController::connectionStateChanged, this, &VpnConnection::onConnectionStateChanged);
|
||||
connect(IosController::Instance(), &IosController::bytesChanged, this, &VpnConnection::onBytesChanged);
|
||||
#endif
|
||||
}
|
||||
@@ -464,7 +464,7 @@ void VpnConnection::disconnectFromVpn()
|
||||
*connection = connect(AndroidController::instance(), &AndroidController::vpnStateChanged, this,
|
||||
[this, connection](AndroidController::ConnectionState state) {
|
||||
if (state == AndroidController::ConnectionState::DISCONNECTED) {
|
||||
setConnectionState(Vpn::ConnectionState::Disconnected);
|
||||
onConnectionStateChanged(Vpn::ConnectionState::Disconnected);
|
||||
disconnect(*connection);
|
||||
delete connection;
|
||||
}
|
||||
|
||||
9
cmake/conan_bootstrap.cmake
Normal file
9
cmake/conan_bootstrap.cmake
Normal file
@@ -0,0 +1,9 @@
|
||||
find_program(CONAN_COMMAND "conan" REQUIRED HINTS /opt/homebrew/bin)
|
||||
|
||||
file(GLOB_RECURSE LOCAL_RECIPES "${CMAKE_SOURCE_DIR}/recipes/*/conanfile.py")
|
||||
foreach(RECIPE ${LOCAL_RECIPES})
|
||||
get_filename_component(RECIPE_DIR ${RECIPE} DIRECTORY)
|
||||
execute_process(
|
||||
COMMAND ${CONAN_COMMAND} export ${RECIPE_DIR}
|
||||
)
|
||||
endforeach()
|
||||
722
cmake/conan_provider.cmake
Normal file
722
cmake/conan_provider.cmake
Normal file
@@ -0,0 +1,722 @@
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2024 JFrog
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
set(CONAN_MINIMUM_VERSION 2.0.5)
|
||||
|
||||
# Create a new policy scope and set the minimum required cmake version so the
|
||||
# features behind a policy setting like if(... IN_LIST ...) behaves as expected
|
||||
# even if the parent project does not specify a minimum cmake version or a minimum
|
||||
# version less than this module requires (e.g. 3.0) before the first project() call.
|
||||
# (see: https://cmake.org/cmake/help/latest/variable/CMAKE_PROJECT_TOP_LEVEL_INCLUDES.html)
|
||||
#
|
||||
# The policy-affecting calls like cmake_policy(SET...) or `cmake_minimum_required` only
|
||||
# affects the current policy scope, i.e. between the PUSH and POP in this case.
|
||||
#
|
||||
# https://cmake.org/cmake/help/book/mastering-cmake/chapter/Policies.html#the-policy-stack
|
||||
cmake_policy(PUSH)
|
||||
cmake_minimum_required(VERSION 3.24)
|
||||
|
||||
|
||||
function(detect_os os os_api_level os_sdk os_subsystem os_version)
|
||||
# it could be cross compilation
|
||||
message(STATUS "CMake-Conan: cmake_system_name=${CMAKE_SYSTEM_NAME}")
|
||||
if(CMAKE_SYSTEM_NAME AND NOT CMAKE_SYSTEM_NAME STREQUAL "Generic")
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
|
||||
set(${os} Macos PARENT_SCOPE)
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "QNX")
|
||||
set(${os} Neutrino PARENT_SCOPE)
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "CYGWIN")
|
||||
set(${os} Windows PARENT_SCOPE)
|
||||
set(${os_subsystem} cygwin PARENT_SCOPE)
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "^MSYS")
|
||||
set(${os} Windows PARENT_SCOPE)
|
||||
set(${os_subsystem} msys2 PARENT_SCOPE)
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
|
||||
# https://github.com/emscripten-core/emscripten/blob/4.0.6/cmake/Modules/Platform/Emscripten.cmake#L17C1-L17C34
|
||||
set(${os} Emscripten PARENT_SCOPE)
|
||||
else()
|
||||
set(${os} ${CMAKE_SYSTEM_NAME} PARENT_SCOPE)
|
||||
endif()
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Android")
|
||||
if(DEFINED ANDROID_PLATFORM)
|
||||
string(REGEX MATCH "[0-9]+" _os_api_level ${ANDROID_PLATFORM})
|
||||
elseif(DEFINED CMAKE_SYSTEM_VERSION)
|
||||
set(_os_api_level ${CMAKE_SYSTEM_VERSION})
|
||||
endif()
|
||||
message(STATUS "CMake-Conan: android api level=${_os_api_level}")
|
||||
set(${os_api_level} ${_os_api_level} PARENT_SCOPE)
|
||||
endif()
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "Darwin|iOS|tvOS|watchOS")
|
||||
# CMAKE_OSX_SYSROOT contains the full path to the SDK for MakeFile/Ninja
|
||||
# generators, but just has the original input string for Xcode.
|
||||
if(NOT IS_DIRECTORY ${CMAKE_OSX_SYSROOT})
|
||||
set(_os_sdk ${CMAKE_OSX_SYSROOT})
|
||||
else()
|
||||
if(CMAKE_OSX_SYSROOT MATCHES Simulator)
|
||||
set(apple_platform_suffix simulator)
|
||||
else()
|
||||
set(apple_platform_suffix os)
|
||||
endif()
|
||||
if(CMAKE_OSX_SYSROOT MATCHES AppleTV)
|
||||
set(_os_sdk "appletv${apple_platform_suffix}")
|
||||
elseif(CMAKE_OSX_SYSROOT MATCHES iPhone)
|
||||
set(_os_sdk "iphone${apple_platform_suffix}")
|
||||
elseif(CMAKE_OSX_SYSROOT MATCHES Watch)
|
||||
set(_os_sdk "watch${apple_platform_suffix}")
|
||||
endif()
|
||||
endif()
|
||||
if(DEFINED os_sdk)
|
||||
message(STATUS "CMake-Conan: cmake_osx_sysroot=${CMAKE_OSX_SYSROOT}")
|
||||
set(${os_sdk} ${_os_sdk} PARENT_SCOPE)
|
||||
endif()
|
||||
if(DEFINED CMAKE_OSX_DEPLOYMENT_TARGET)
|
||||
message(STATUS "CMake-Conan: cmake_osx_deployment_target=${CMAKE_OSX_DEPLOYMENT_TARGET}")
|
||||
set(${os_version} ${CMAKE_OSX_DEPLOYMENT_TARGET} PARENT_SCOPE)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
|
||||
function(detect_arch arch)
|
||||
# CMAKE_OSX_ARCHITECTURES can contain multiple architectures, but Conan only supports one.
|
||||
# Therefore this code only finds one. If the recipes support multiple architectures, the
|
||||
# build will work. Otherwise, there will be a linker error for the missing architecture(s).
|
||||
if(DEFINED CMAKE_OSX_ARCHITECTURES)
|
||||
string(REPLACE " " ";" apple_arch_list "${CMAKE_OSX_ARCHITECTURES}")
|
||||
list(LENGTH apple_arch_list apple_arch_count)
|
||||
if(apple_arch_count GREATER 1)
|
||||
message(WARNING "CMake-Conan: Multiple architectures detected, this will only work if Conan recipe(s) produce fat binaries.")
|
||||
endif()
|
||||
endif()
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "Darwin|iOS|tvOS|watchOS" AND NOT CMAKE_OSX_ARCHITECTURES STREQUAL "")
|
||||
set(host_arch ${CMAKE_OSX_ARCHITECTURES})
|
||||
elseif(MSVC)
|
||||
set(host_arch ${CMAKE_CXX_COMPILER_ARCHITECTURE_ID})
|
||||
else()
|
||||
set(host_arch ${CMAKE_SYSTEM_PROCESSOR})
|
||||
endif()
|
||||
if(host_arch MATCHES "aarch64|arm64|ARM64")
|
||||
list(APPEND _arch armv8)
|
||||
endif()
|
||||
if(host_arch MATCHES "armv7|armv7-a|armv7l|ARMV7")
|
||||
list(APPEND _arch armv7)
|
||||
endif()
|
||||
if(host_arch MATCHES armv7s)
|
||||
list(APPEND _arch armv7s)
|
||||
endif()
|
||||
if(host_arch MATCHES "i686|i386|X86")
|
||||
list(APPEND _arch x86)
|
||||
endif()
|
||||
if(host_arch MATCHES "AMD64|amd64|x86_64|x64")
|
||||
list(APPEND _arch x86_64)
|
||||
endif()
|
||||
if(EMSCRIPTEN)
|
||||
# https://github.com/emscripten-core/emscripten/blob/4.0.6/cmake/Modules/Platform/Emscripten.cmake#L294C1-L294C80
|
||||
list(APPEND _arch wasm)
|
||||
endif()
|
||||
message(STATUS "CMake-Conan: cmake_system_processor=${_arch}")
|
||||
list(JOIN _arch "|" _arch)
|
||||
set(${arch} ${_arch} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
|
||||
function(detect_cxx_standard compiler cxx_standard)
|
||||
set(${cxx_standard} ${CMAKE_CXX_STANDARD} PARENT_SCOPE)
|
||||
if(CMAKE_CXX_EXTENSIONS)
|
||||
if(compiler STREQUAL "msvc")
|
||||
set(${cxx_standard} "${CMAKE_CXX_STANDARD}" PARENT_SCOPE)
|
||||
else()
|
||||
set(${cxx_standard} "gnu${CMAKE_CXX_STANDARD}" PARENT_SCOPE)
|
||||
endif()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
|
||||
macro(detect_gnu_libstdcxx)
|
||||
# _conan_is_gnu_libstdcxx true if GNU libstdc++
|
||||
check_cxx_source_compiles("
|
||||
#include <cstddef>
|
||||
#if !defined(__GLIBCXX__) && !defined(__GLIBCPP__)
|
||||
static_assert(false);
|
||||
#endif
|
||||
int main(){}" _conan_is_gnu_libstdcxx)
|
||||
|
||||
# _conan_gnu_libstdcxx_is_cxx11_abi true if C++11 ABI
|
||||
check_cxx_source_compiles("
|
||||
#include <string>
|
||||
static_assert(sizeof(std::string) != sizeof(void*), \"using libstdc++\");
|
||||
int main () {}" _conan_gnu_libstdcxx_is_cxx11_abi)
|
||||
|
||||
set(_conan_gnu_libstdcxx_suffix "")
|
||||
if(_conan_gnu_libstdcxx_is_cxx11_abi)
|
||||
set(_conan_gnu_libstdcxx_suffix "11")
|
||||
endif()
|
||||
unset (_conan_gnu_libstdcxx_is_cxx11_abi)
|
||||
endmacro()
|
||||
|
||||
|
||||
macro(detect_libcxx)
|
||||
# _conan_is_libcxx true if LLVM libc++
|
||||
check_cxx_source_compiles("
|
||||
#include <cstddef>
|
||||
#if !defined(_LIBCPP_VERSION)
|
||||
static_assert(false);
|
||||
#endif
|
||||
int main(){}" _conan_is_libcxx)
|
||||
endmacro()
|
||||
|
||||
|
||||
function(detect_lib_cxx lib_cxx)
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Android")
|
||||
message(STATUS "CMake-Conan: android_stl=${CMAKE_ANDROID_STL_TYPE}")
|
||||
set(${lib_cxx} ${CMAKE_ANDROID_STL_TYPE} PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
include(CheckCXXSourceCompiles)
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
||||
detect_gnu_libstdcxx()
|
||||
set(${lib_cxx} "libstdc++${_conan_gnu_libstdcxx_suffix}" PARENT_SCOPE)
|
||||
elseif(CMAKE_CXX_COMPILER_ID MATCHES "AppleClang")
|
||||
set(${lib_cxx} "libc++" PARENT_SCOPE)
|
||||
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT CMAKE_SYSTEM_NAME MATCHES "Windows")
|
||||
# Check for libc++
|
||||
detect_libcxx()
|
||||
if(_conan_is_libcxx)
|
||||
set(${lib_cxx} "libc++" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Check for libstdc++
|
||||
detect_gnu_libstdcxx()
|
||||
if(_conan_is_gnu_libstdcxx)
|
||||
set(${lib_cxx} "libstdc++${_conan_gnu_libstdcxx_suffix}" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# TODO: it would be an error if we reach this point
|
||||
elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
|
||||
# Do nothing - compiler.runtime and compiler.runtime_type
|
||||
# should be handled separately: https://github.com/conan-io/cmake-conan/pull/516
|
||||
return()
|
||||
else()
|
||||
# TODO: unable to determine, ask user to provide a full profile file instead
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
|
||||
function(detect_compiler compiler compiler_version compiler_runtime compiler_runtime_type)
|
||||
if(DEFINED CMAKE_CXX_COMPILER_ID)
|
||||
set(_compiler ${CMAKE_CXX_COMPILER_ID})
|
||||
set(_compiler_version ${CMAKE_CXX_COMPILER_VERSION})
|
||||
else()
|
||||
if(NOT DEFINED CMAKE_C_COMPILER_ID)
|
||||
message(FATAL_ERROR "C or C++ compiler not defined")
|
||||
endif()
|
||||
set(_compiler ${CMAKE_C_COMPILER_ID})
|
||||
set(_compiler_version ${CMAKE_C_COMPILER_VERSION})
|
||||
endif()
|
||||
|
||||
message(STATUS "CMake-Conan: CMake compiler=${_compiler}")
|
||||
message(STATUS "CMake-Conan: CMake compiler version=${_compiler_version}")
|
||||
|
||||
if(_compiler MATCHES MSVC)
|
||||
set(_compiler "msvc")
|
||||
string(SUBSTRING ${MSVC_VERSION} 0 3 _compiler_version)
|
||||
# Configure compiler.runtime and compiler.runtime_type settings for MSVC
|
||||
if(CMAKE_MSVC_RUNTIME_LIBRARY)
|
||||
set(_msvc_runtime_library ${CMAKE_MSVC_RUNTIME_LIBRARY})
|
||||
else()
|
||||
set(_msvc_runtime_library MultiThreaded$<$<CONFIG:Debug>:Debug>DLL) # default value documented by CMake
|
||||
endif()
|
||||
|
||||
set(_KNOWN_MSVC_RUNTIME_VALUES "")
|
||||
list(APPEND _KNOWN_MSVC_RUNTIME_VALUES MultiThreaded MultiThreadedDLL)
|
||||
list(APPEND _KNOWN_MSVC_RUNTIME_VALUES MultiThreadedDebug MultiThreadedDebugDLL)
|
||||
list(APPEND _KNOWN_MSVC_RUNTIME_VALUES MultiThreaded$<$<CONFIG:Debug>:Debug> MultiThreaded$<$<CONFIG:Debug>:Debug>DLL)
|
||||
|
||||
# only accept the 6 possible values, otherwise we don't don't know to map this
|
||||
if(NOT _msvc_runtime_library IN_LIST _KNOWN_MSVC_RUNTIME_VALUES)
|
||||
message(FATAL_ERROR "CMake-Conan: unable to map MSVC runtime: ${_msvc_runtime_library} to Conan settings")
|
||||
endif()
|
||||
|
||||
# Runtime is "dynamic" in all cases if it ends in DLL
|
||||
if(_msvc_runtime_library MATCHES ".*DLL$")
|
||||
set(_compiler_runtime "dynamic")
|
||||
else()
|
||||
set(_compiler_runtime "static")
|
||||
endif()
|
||||
message(STATUS "CMake-Conan: CMake compiler.runtime=${_compiler_runtime}")
|
||||
|
||||
# Only define compiler.runtime_type when explicitly requested
|
||||
# If a generator expression is used, let Conan handle it conditional on build_type
|
||||
if(NOT _msvc_runtime_library MATCHES "<CONFIG:Debug>:Debug>")
|
||||
if(_msvc_runtime_library MATCHES "Debug")
|
||||
set(_compiler_runtime_type "Debug")
|
||||
else()
|
||||
set(_compiler_runtime_type "Release")
|
||||
endif()
|
||||
message(STATUS "CMake-Conan: CMake compiler.runtime_type=${_compiler_runtime_type}")
|
||||
endif()
|
||||
|
||||
unset(_KNOWN_MSVC_RUNTIME_VALUES)
|
||||
|
||||
elseif(_compiler MATCHES AppleClang)
|
||||
set(_compiler "apple-clang")
|
||||
string(REPLACE "." ";" VERSION_LIST ${_compiler_version})
|
||||
list(GET VERSION_LIST 0 _compiler_version)
|
||||
elseif(_compiler MATCHES Clang)
|
||||
set(_compiler "clang")
|
||||
string(REPLACE "." ";" VERSION_LIST ${_compiler_version})
|
||||
list(GET VERSION_LIST 0 _compiler_version)
|
||||
elseif(_compiler MATCHES GNU)
|
||||
set(_compiler "gcc")
|
||||
string(REPLACE "." ";" VERSION_LIST ${_compiler_version})
|
||||
list(GET VERSION_LIST 0 _compiler_version)
|
||||
endif()
|
||||
|
||||
message(STATUS "CMake-Conan: [settings] compiler=${_compiler}")
|
||||
message(STATUS "CMake-Conan: [settings] compiler.version=${_compiler_version}")
|
||||
if (_compiler_runtime)
|
||||
message(STATUS "CMake-Conan: [settings] compiler.runtime=${_compiler_runtime}")
|
||||
endif()
|
||||
if (_compiler_runtime_type)
|
||||
message(STATUS "CMake-Conan: [settings] compiler.runtime_type=${_compiler_runtime_type}")
|
||||
endif()
|
||||
|
||||
set(${compiler} ${_compiler} PARENT_SCOPE)
|
||||
set(${compiler_version} ${_compiler_version} PARENT_SCOPE)
|
||||
set(${compiler_runtime} ${_compiler_runtime} PARENT_SCOPE)
|
||||
set(${compiler_runtime_type} ${_compiler_runtime_type} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
|
||||
function(detect_build_type build_type)
|
||||
get_property(multiconfig_generator GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
||||
if(NOT multiconfig_generator)
|
||||
# Only set when we know we are in a single-configuration generator
|
||||
# Note: we may want to fail early if `CMAKE_BUILD_TYPE` is not defined
|
||||
set(${build_type} ${CMAKE_BUILD_TYPE} PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
|
||||
macro(set_conan_compiler_if_appleclang lang command output_variable)
|
||||
if(CMAKE_${lang}_COMPILER_ID STREQUAL "AppleClang")
|
||||
execute_process(COMMAND xcrun --find ${command}
|
||||
OUTPUT_VARIABLE _xcrun_out OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
cmake_path(GET _xcrun_out PARENT_PATH _xcrun_toolchain_path)
|
||||
cmake_path(GET CMAKE_${lang}_COMPILER PARENT_PATH _compiler_parent_path)
|
||||
if ("${_xcrun_toolchain_path}" STREQUAL "${_compiler_parent_path}")
|
||||
set(${output_variable} "")
|
||||
endif()
|
||||
unset(_xcrun_out)
|
||||
unset(_xcrun_toolchain_path)
|
||||
unset(_compiler_parent_path)
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
|
||||
macro(append_compiler_executables_configuration)
|
||||
set(_conan_c_compiler "")
|
||||
set(_conan_cpp_compiler "")
|
||||
set(_conan_rc_compiler "")
|
||||
set(_conan_compilers_list "")
|
||||
if(CMAKE_C_COMPILER)
|
||||
set(_conan_c_compiler "\"c\":\"${CMAKE_C_COMPILER}\"")
|
||||
set_conan_compiler_if_appleclang(C cc _conan_c_compiler)
|
||||
list(APPEND _conan_compilers_list ${_conan_c_compiler})
|
||||
else()
|
||||
message(WARNING "CMake-Conan: The C compiler is not defined. "
|
||||
"Please define CMAKE_C_COMPILER or enable the C language.")
|
||||
endif()
|
||||
if(CMAKE_CXX_COMPILER)
|
||||
set(_conan_cpp_compiler "\"cpp\":\"${CMAKE_CXX_COMPILER}\"")
|
||||
set_conan_compiler_if_appleclang(CXX c++ _conan_cpp_compiler)
|
||||
list(APPEND _conan_compilers_list ${_conan_cpp_compiler})
|
||||
else()
|
||||
message(WARNING "CMake-Conan: The C++ compiler is not defined. "
|
||||
"Please define CMAKE_CXX_COMPILER or enable the C++ language.")
|
||||
endif()
|
||||
if(CMAKE_RC_COMPILER)
|
||||
set(_conan_rc_compiler "\"rc\":\"${CMAKE_RC_COMPILER}\"")
|
||||
list(APPEND _conan_compilers_list ${_conan_rc_compiler})
|
||||
# Not necessary to warn if RC not defined
|
||||
endif()
|
||||
if(NOT "x${_conan_compilers_list}" STREQUAL "x")
|
||||
if (NOT CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
string(REPLACE ";" "," _conan_compilers_list "${_conan_compilers_list}")
|
||||
string(APPEND profile "tools.build:compiler_executables={${_conan_compilers_list}}\n")
|
||||
endif()
|
||||
endif()
|
||||
unset(_conan_c_compiler)
|
||||
unset(_conan_cpp_compiler)
|
||||
unset(_conan_rc_compiler)
|
||||
unset(_conan_compilers_list)
|
||||
endmacro()
|
||||
|
||||
|
||||
function(detect_host_profile output_file)
|
||||
detect_os(os os_api_level os_sdk os_subsystem os_version)
|
||||
detect_arch(arch)
|
||||
detect_compiler(compiler compiler_version compiler_runtime compiler_runtime_type)
|
||||
detect_cxx_standard(${compiler} compiler_cppstd)
|
||||
detect_lib_cxx(compiler_libcxx)
|
||||
detect_build_type(build_type)
|
||||
|
||||
set(profile "")
|
||||
string(APPEND profile "[settings]\n")
|
||||
if(arch)
|
||||
string(APPEND profile arch=${arch} "\n")
|
||||
endif()
|
||||
if(os)
|
||||
string(APPEND profile os=${os} "\n")
|
||||
endif()
|
||||
if(os_api_level)
|
||||
string(APPEND profile os.api_level=${os_api_level} "\n")
|
||||
endif()
|
||||
if(os_version)
|
||||
string(APPEND profile os.version=${os_version} "\n")
|
||||
endif()
|
||||
if(os_sdk)
|
||||
string(APPEND profile os.sdk=${os_sdk} "\n")
|
||||
endif()
|
||||
if(os_subsystem)
|
||||
string(APPEND profile os.subsystem=${os_subsystem} "\n")
|
||||
endif()
|
||||
if(compiler)
|
||||
string(APPEND profile compiler=${compiler} "\n")
|
||||
endif()
|
||||
if(compiler_version)
|
||||
string(APPEND profile compiler.version=${compiler_version} "\n")
|
||||
endif()
|
||||
if(compiler_runtime)
|
||||
string(APPEND profile compiler.runtime=${compiler_runtime} "\n")
|
||||
endif()
|
||||
if(compiler_runtime_type)
|
||||
string(APPEND profile compiler.runtime_type=${compiler_runtime_type} "\n")
|
||||
endif()
|
||||
if(compiler_cppstd)
|
||||
string(APPEND profile compiler.cppstd=${compiler_cppstd} "\n")
|
||||
endif()
|
||||
if(compiler_libcxx)
|
||||
string(APPEND profile compiler.libcxx=${compiler_libcxx} "\n")
|
||||
endif()
|
||||
if(build_type)
|
||||
string(APPEND profile "build_type=${build_type}\n")
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED output_file)
|
||||
set(file_name "${CMAKE_BINARY_DIR}/profile")
|
||||
else()
|
||||
set(file_name ${output_file})
|
||||
endif()
|
||||
|
||||
string(APPEND profile "[conf]\n")
|
||||
string(APPEND profile "tools.cmake.cmaketoolchain:generator=${CMAKE_GENERATOR}\n")
|
||||
|
||||
# propagate compilers via profile
|
||||
append_compiler_executables_configuration()
|
||||
|
||||
if(os STREQUAL "Android")
|
||||
string(APPEND profile "tools.android:ndk_path=${CMAKE_ANDROID_NDK}\n")
|
||||
endif()
|
||||
|
||||
message(STATUS "CMake-Conan: Creating profile ${file_name}")
|
||||
file(WRITE ${file_name} ${profile})
|
||||
message(STATUS "CMake-Conan: Profile: \n${profile}")
|
||||
endfunction()
|
||||
|
||||
|
||||
function(conan_profile_detect_default)
|
||||
message(STATUS "CMake-Conan: Checking if a default profile exists")
|
||||
execute_process(COMMAND ${CONAN_COMMAND} profile path default
|
||||
RESULT_VARIABLE return_code
|
||||
OUTPUT_VARIABLE conan_stdout
|
||||
ERROR_VARIABLE conan_stderr
|
||||
ECHO_ERROR_VARIABLE # show the text output regardless
|
||||
ECHO_OUTPUT_VARIABLE
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||
if(NOT ${return_code} EQUAL "0")
|
||||
message(STATUS "CMake-Conan: The default profile doesn't exist, detecting it.")
|
||||
execute_process(COMMAND ${CONAN_COMMAND} profile detect
|
||||
RESULT_VARIABLE return_code
|
||||
OUTPUT_VARIABLE conan_stdout
|
||||
ERROR_VARIABLE conan_stderr
|
||||
ECHO_ERROR_VARIABLE # show the text output regardless
|
||||
ECHO_OUTPUT_VARIABLE
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
|
||||
function(conan_install)
|
||||
set(conan_output_folder ${CMAKE_BINARY_DIR}/conan)
|
||||
# Invoke "conan install" with the provided arguments
|
||||
set(conan_args -of=${conan_output_folder})
|
||||
message(STATUS "CMake-Conan: conan install ${CMAKE_SOURCE_DIR} ${conan_args} ${ARGN}")
|
||||
|
||||
|
||||
# In case there was not a valid cmake executable in the PATH, we inject the
|
||||
# same we used to invoke the provider to the PATH
|
||||
if(DEFINED PATH_TO_CMAKE_BIN)
|
||||
set(old_path $ENV{PATH})
|
||||
set(ENV{PATH} "$ENV{PATH}:${PATH_TO_CMAKE_BIN}")
|
||||
endif()
|
||||
|
||||
execute_process(COMMAND ${CONAN_COMMAND} install ${CMAKE_SOURCE_DIR} ${conan_args} ${ARGN} --format=json
|
||||
RESULT_VARIABLE return_code
|
||||
OUTPUT_VARIABLE conan_stdout
|
||||
ERROR_VARIABLE conan_stderr
|
||||
ECHO_ERROR_VARIABLE # show the text output regardless
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
if(DEFINED PATH_TO_CMAKE_BIN)
|
||||
set(ENV{PATH} "${old_path}")
|
||||
endif()
|
||||
|
||||
if(NOT "${return_code}" STREQUAL "0")
|
||||
message(FATAL_ERROR "Conan install failed='${return_code}'")
|
||||
endif()
|
||||
|
||||
# the files are generated in a folder that depends on the layout used, if
|
||||
# one is specified, but we don't know a priori where this is.
|
||||
# TODO: this can be made more robust if Conan can provide this in the json output
|
||||
string(JSON conan_generators_folder GET "${conan_stdout}" graph nodes 0 generators_folder)
|
||||
cmake_path(CONVERT ${conan_generators_folder} TO_CMAKE_PATH_LIST conan_generators_folder)
|
||||
|
||||
message(STATUS "CMake-Conan: CONAN_GENERATORS_FOLDER=${conan_generators_folder}")
|
||||
set_property(GLOBAL PROPERTY CONAN_GENERATORS_FOLDER "${conan_generators_folder}")
|
||||
# reconfigure on conanfile changes
|
||||
string(JSON conanfile GET "${conan_stdout}" graph nodes 0 label)
|
||||
message(STATUS "CMake-Conan: CONANFILE=${CMAKE_SOURCE_DIR}/${conanfile}")
|
||||
set_property(DIRECTORY ${CMAKE_SOURCE_DIR} APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${CMAKE_SOURCE_DIR}/${conanfile}")
|
||||
# success
|
||||
set_property(GLOBAL PROPERTY CONAN_INSTALL_SUCCESS TRUE)
|
||||
|
||||
endfunction()
|
||||
|
||||
|
||||
function(conan_get_version conan_command conan_current_version)
|
||||
execute_process(
|
||||
COMMAND ${conan_command} --version
|
||||
OUTPUT_VARIABLE conan_output
|
||||
RESULT_VARIABLE conan_result
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
if(conan_result)
|
||||
message(FATAL_ERROR "CMake-Conan: Error when trying to run Conan")
|
||||
endif()
|
||||
|
||||
string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" conan_version ${conan_output})
|
||||
set(${conan_current_version} ${conan_version} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
|
||||
function(conan_version_check)
|
||||
set(options )
|
||||
set(one_value_args MINIMUM CURRENT)
|
||||
set(multi_value_args )
|
||||
cmake_parse_arguments(conan_version_check
|
||||
"${options}" "${one_value_args}" "${multi_value_args}" ${ARGN})
|
||||
|
||||
if(NOT conan_version_check_MINIMUM)
|
||||
message(FATAL_ERROR "CMake-Conan: Required parameter MINIMUM not set!")
|
||||
endif()
|
||||
if(NOT conan_version_check_CURRENT)
|
||||
message(FATAL_ERROR "CMake-Conan: Required parameter CURRENT not set!")
|
||||
endif()
|
||||
|
||||
if(conan_version_check_CURRENT VERSION_LESS conan_version_check_MINIMUM)
|
||||
message(FATAL_ERROR "CMake-Conan: Conan version must be ${conan_version_check_MINIMUM} or later")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
|
||||
macro(construct_profile_argument argument_variable profile_list)
|
||||
set(${argument_variable} "")
|
||||
if("${profile_list}" STREQUAL "CONAN_HOST_PROFILE")
|
||||
set(_arg_flag "--profile:host=")
|
||||
elseif("${profile_list}" STREQUAL "CONAN_BUILD_PROFILE")
|
||||
set(_arg_flag "--profile:build=")
|
||||
endif()
|
||||
|
||||
set(_profile_list "${${profile_list}}")
|
||||
list(TRANSFORM _profile_list REPLACE "auto-cmake" "${CMAKE_BINARY_DIR}/conan_host_profile")
|
||||
list(TRANSFORM _profile_list PREPEND ${_arg_flag})
|
||||
set(${argument_variable} ${_profile_list})
|
||||
|
||||
unset(_arg_flag)
|
||||
unset(_profile_list)
|
||||
endmacro()
|
||||
|
||||
|
||||
macro(conan_provide_dependency method package_name)
|
||||
set_property(GLOBAL PROPERTY CONAN_PROVIDE_DEPENDENCY_INVOKED TRUE)
|
||||
get_property(_conan_install_success GLOBAL PROPERTY CONAN_INSTALL_SUCCESS)
|
||||
if(NOT _conan_install_success)
|
||||
find_program(CONAN_COMMAND "conan" REQUIRED)
|
||||
conan_get_version(${CONAN_COMMAND} CONAN_CURRENT_VERSION)
|
||||
conan_version_check(MINIMUM ${CONAN_MINIMUM_VERSION} CURRENT ${CONAN_CURRENT_VERSION})
|
||||
message(STATUS "CMake-Conan: first find_package() found. Installing dependencies with Conan")
|
||||
if("default" IN_LIST CONAN_HOST_PROFILE OR "default" IN_LIST CONAN_BUILD_PROFILE)
|
||||
conan_profile_detect_default()
|
||||
endif()
|
||||
if("auto-cmake" IN_LIST CONAN_HOST_PROFILE)
|
||||
detect_host_profile(${CMAKE_BINARY_DIR}/conan_host_profile)
|
||||
endif()
|
||||
construct_profile_argument(_host_profile_flags CONAN_HOST_PROFILE)
|
||||
construct_profile_argument(_build_profile_flags CONAN_BUILD_PROFILE)
|
||||
if(EXISTS "${CMAKE_SOURCE_DIR}/conanfile.py")
|
||||
file(READ "${CMAKE_SOURCE_DIR}/conanfile.py" outfile)
|
||||
if(NOT "${outfile}" MATCHES ".*CMakeConfigDeps.*")
|
||||
message(WARNING "Cmake-conan: CMakeConfigDeps generator was not defined in the conanfile")
|
||||
endif()
|
||||
elseif (EXISTS "${CMAKE_SOURCE_DIR}/conanfile.txt")
|
||||
file(READ "${CMAKE_SOURCE_DIR}/conanfile.txt" outfile)
|
||||
if(NOT "${outfile}" MATCHES ".*CMakeConfigDeps.*")
|
||||
message(WARNING "Cmake-conan: CMakeConfigDeps generator was not defined in the conanfile")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
get_property(_multiconfig_generator GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
||||
|
||||
if(DEFINED CONAN_INSTALL_BUILD_CONFIGURATIONS)
|
||||
# Configurations are specified by the project or user
|
||||
set(_build_configs "${CONAN_INSTALL_BUILD_CONFIGURATIONS}")
|
||||
list(LENGTH _build_configs _build_configs_length)
|
||||
if(NOT _multiconfig_generator AND _build_configs_length GREATER 1)
|
||||
message(FATAL_ERROR "cmake-conan: when using a single-config CMake generator, "
|
||||
"please only specify a single configuration in CONAN_INSTALL_BUILD_CONFIGURATIONS")
|
||||
endif()
|
||||
unset(_build_configs_length)
|
||||
else()
|
||||
# No configuration overrides, provide sensible defaults
|
||||
if(_multiconfig_generator)
|
||||
set(_build_configs Release Debug)
|
||||
else()
|
||||
set(_build_configs ${CMAKE_BUILD_TYPE})
|
||||
endif()
|
||||
|
||||
endif()
|
||||
|
||||
list(JOIN _build_configs ", " _build_configs_msg)
|
||||
message(STATUS "CMake-Conan: Installing configuration(s): ${_build_configs_msg}")
|
||||
foreach(_build_config IN LISTS _build_configs)
|
||||
set(_self_build_config "")
|
||||
if(NOT _multiconfig_generator AND NOT _build_config STREQUAL "${CMAKE_BUILD_TYPE}")
|
||||
set(_self_build_config -s &:build_type=${CMAKE_BUILD_TYPE})
|
||||
endif()
|
||||
conan_install(${_host_profile_flags} ${_build_profile_flags} -s build_type=${_build_config} ${_self_build_config} ${CONAN_INSTALL_ARGS})
|
||||
endforeach()
|
||||
|
||||
get_property(_conan_generators_folder GLOBAL PROPERTY CONAN_GENERATORS_FOLDER)
|
||||
if(EXISTS "${_conan_generators_folder}/conan_cmakedeps_paths.cmake")
|
||||
message(STATUS "CMake-Conan: Loading conan_cmakedeps_paths.cmake file")
|
||||
include(${_conan_generators_folder}/conan_cmakedeps_paths.cmake)
|
||||
endif()
|
||||
|
||||
unset(_self_build_config)
|
||||
unset(_multiconfig_generator)
|
||||
unset(_build_configs)
|
||||
unset(_build_configs_msg)
|
||||
unset(_host_profile_flags)
|
||||
unset(_build_profile_flags)
|
||||
unset(_conan_install_success)
|
||||
else()
|
||||
message(STATUS "CMake-Conan: find_package(${ARGV1}) found, 'conan install' already ran")
|
||||
unset(_conan_install_success)
|
||||
endif()
|
||||
|
||||
get_property(_conan_generators_folder GLOBAL PROPERTY CONAN_GENERATORS_FOLDER)
|
||||
|
||||
# Ensure that we consider Conan-provided packages ahead of any other,
|
||||
# irrespective of other settings that modify the search order or search paths
|
||||
# This follows the guidelines from the find_package documentation
|
||||
# (https://cmake.org/cmake/help/latest/command/find_package.html):
|
||||
# find_package (<PackageName> PATHS paths... NO_DEFAULT_PATH)
|
||||
# find_package (<PackageName>)
|
||||
|
||||
# Filter out `REQUIRED` from the argument list, as the first call may fail
|
||||
set(_find_args_${package_name} "${ARGN}")
|
||||
list(REMOVE_ITEM _find_args_${package_name} "REQUIRED")
|
||||
if(NOT "MODULE" IN_LIST _find_args_${package_name})
|
||||
find_package(${package_name} ${_find_args_${package_name}} BYPASS_PROVIDER PATHS "${_conan_generators_folder}" NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH)
|
||||
unset(_find_args_${package_name})
|
||||
endif()
|
||||
|
||||
# Invoke find_package a second time - if the first call succeeded,
|
||||
# this will simply reuse the result. If not, fall back to CMake default search
|
||||
# behaviour, also allowing modules to be searched.
|
||||
if(NOT ${package_name}_FOUND)
|
||||
list(FIND CMAKE_MODULE_PATH "${_conan_generators_folder}" _index)
|
||||
if(_index EQUAL -1)
|
||||
list(PREPEND CMAKE_MODULE_PATH "${_conan_generators_folder}")
|
||||
endif()
|
||||
unset(_index)
|
||||
find_package(${package_name} ${ARGN} BYPASS_PROVIDER)
|
||||
list(REMOVE_ITEM CMAKE_MODULE_PATH "${_conan_generators_folder}")
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
|
||||
cmake_language(
|
||||
SET_DEPENDENCY_PROVIDER conan_provide_dependency
|
||||
SUPPORTED_METHODS FIND_PACKAGE
|
||||
)
|
||||
|
||||
|
||||
macro(conan_provide_dependency_check)
|
||||
set(_conan_provide_dependency_invoked FALSE)
|
||||
get_property(_conan_provide_dependency_invoked GLOBAL PROPERTY CONAN_PROVIDE_DEPENDENCY_INVOKED)
|
||||
if(NOT _conan_provide_dependency_invoked)
|
||||
message(WARNING "Conan is correctly configured as dependency provider, "
|
||||
"but Conan has not been invoked. Please add at least one "
|
||||
"call to `find_package()`.")
|
||||
if(DEFINED CONAN_COMMAND)
|
||||
# supress warning in case `CONAN_COMMAND` was specified but unused.
|
||||
set(_conan_command ${CONAN_COMMAND})
|
||||
unset(_conan_command)
|
||||
endif()
|
||||
endif()
|
||||
unset(_conan_provide_dependency_invoked)
|
||||
endmacro()
|
||||
|
||||
|
||||
# Add a deferred call at the end of processing the top-level directory
|
||||
# to check if the dependency provider was invoked at all.
|
||||
cmake_language(DEFER DIRECTORY "${CMAKE_SOURCE_DIR}" CALL conan_provide_dependency_check)
|
||||
|
||||
# Configurable variables for Conan profiles
|
||||
set(CONAN_HOST_PROFILE "default;auto-cmake" CACHE STRING "Conan host profile")
|
||||
set(CONAN_BUILD_PROFILE "default" CACHE STRING "Conan build profile")
|
||||
set(CONAN_INSTALL_ARGS "--build=missing" CACHE STRING "Command line arguments for conan install")
|
||||
|
||||
find_program(_cmake_program NAMES cmake NO_PACKAGE_ROOT_PATH NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH NO_CMAKE_FIND_ROOT_PATH)
|
||||
if(NOT _cmake_program)
|
||||
get_filename_component(PATH_TO_CMAKE_BIN "${CMAKE_COMMAND}" DIRECTORY)
|
||||
set(PATH_TO_CMAKE_BIN "${PATH_TO_CMAKE_BIN}" CACHE INTERNAL "Path where the CMake executable is")
|
||||
endif()
|
||||
|
||||
cmake_policy(POP)
|
||||
42
conanfile.py
Normal file
42
conanfile.py
Normal file
@@ -0,0 +1,42 @@
|
||||
from conan import ConanFile
|
||||
|
||||
class AmneziaVPN(ConanFile):
|
||||
settings = "os", "compiler", "build_type", "arch"
|
||||
generators = "CMakeConfigDeps"
|
||||
|
||||
options = {
|
||||
"macos_ne": [True, False]
|
||||
}
|
||||
default_options = {
|
||||
"macos_ne": False
|
||||
}
|
||||
|
||||
def requirements(self):
|
||||
os = str(self.settings.os)
|
||||
|
||||
has_ne = os == "iOS" or (os == "Macos" and self.options.macos_ne)
|
||||
has_service = os == "Windows" or os == "Linux" or (os == "Macos" and not has_ne)
|
||||
|
||||
if has_service:
|
||||
if os == "Windows":
|
||||
self.requires("awg-windows/1.0.0")
|
||||
else:
|
||||
self.requires("awg-go/0.2.16")
|
||||
|
||||
self.requires("amnezia-xray-bindings/1.1.0")
|
||||
self.requires("tun2socks/2.6.0")
|
||||
self.requires("openvpn/2.7.0")
|
||||
self.requires("v2ray-rules-dat/[*]")
|
||||
|
||||
if has_ne:
|
||||
self.requires("awg-apple/2.0.1")
|
||||
self.requires("hev-socks5-tunnel/2.14.4", options={"as_framework": True})
|
||||
self.requires("openvpnadapter/1.0.0")
|
||||
|
||||
if os == "Android":
|
||||
self.requires("amnezia-libxray/1.0.0")
|
||||
self.requires("awg-android/1.1.7")
|
||||
self.requires("openvpn-pt-android/1.0.0")
|
||||
|
||||
self.requires("libssh/0.11.3@amnezia")
|
||||
self.requires("openssl/3.5.5")
|
||||
@@ -14,6 +14,8 @@ sc stop AmneziaVPN-service
|
||||
sc delete AmneziaVPN-service
|
||||
sc stop AmneziaWGTunnel$AmneziaVPN
|
||||
sc delete AmneziaWGTunnel$AmneziaVPN
|
||||
sc stop AmneziaVPNSplitTunnel
|
||||
sc delete AmneziaVPNSplitTunnel
|
||||
taskkill /IM "AmneziaVPN-service.exe" /F
|
||||
taskkill /IM "AmneziaVPN.exe" /F
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@ sc stop AmneziaVPN-service
|
||||
sc delete AmneziaVPN-service
|
||||
sc stop AmneziaWGTunnel$AmneziaVPN
|
||||
sc delete AmneziaWGTunnel$AmneziaVPN
|
||||
sc stop AmneziaVPNSplitTunnel
|
||||
sc delete AmneziaVPNSplitTunnel
|
||||
taskkill /IM "AmneziaVPN-service.exe" /F
|
||||
taskkill /IM "AmneziaVPN.exe" /F
|
||||
|
||||
|
||||
48
recipes/amnezia-libxray/conanfile.py
Normal file
48
recipes/amnezia-libxray/conanfile.py
Normal file
@@ -0,0 +1,48 @@
|
||||
from conan import ConanFile
|
||||
from conan.tools.files import get, copy
|
||||
from conan.tools.layout import basic_layout
|
||||
from conan.errors import ConanInvalidConfiguration
|
||||
|
||||
import os
|
||||
import stat
|
||||
|
||||
class AmneziaLibxray(ConanFile):
|
||||
name = "amnezia-libxray"
|
||||
version = "1.0.0"
|
||||
|
||||
settings = "os", "arch"
|
||||
options = {"page_16k": [True, False]}
|
||||
default_options = {"page_16k": True}
|
||||
|
||||
def layout(self):
|
||||
basic_layout(self, build_folder=".")
|
||||
|
||||
def build_requirements(self):
|
||||
self.tool_requires("go/1.26.0")
|
||||
|
||||
def validate(self):
|
||||
if self.settings.os != "Android":
|
||||
raise ConanInvalidConfiguration(f"{self.name} v{self.version} does not support {self.settings.os}")
|
||||
|
||||
def source(self):
|
||||
get(self, "https://github.com/amnezia-vpn/amnezia-libxray/archive/refs/tags/v1.0.0.zip",
|
||||
sha256="0c50c5acd5063a9fc3cfbb5b3e11481d30cfa3762b3cb1d72130248ff498e9df", strip_root=True
|
||||
)
|
||||
|
||||
def _patch_sources(self):
|
||||
build_path = os.path.join(self.build_folder, "build.sh")
|
||||
build_stat = os.stat(build_path)
|
||||
os.chmod(build_path, build_stat.st_mode | stat.S_IEXEC)
|
||||
|
||||
def build(self):
|
||||
self._patch_sources()
|
||||
cgo_ldflags = 'CGO_LDFLAGS="-Wl,-z,max-page-size=16384" ' if self.options.page_16k else ""
|
||||
self.run(f'{cgo_ldflags}./build.sh android')
|
||||
|
||||
def package(self):
|
||||
copy(self, "libxray.aar", src=self.build_folder, dst=os.path.join(self.package_folder, "aar"))
|
||||
|
||||
def package_info(self):
|
||||
self.cpp_info.set_property("cmake_extra_variables", {
|
||||
"AMNEZIA_LIBXRAY_PATH": os.path.join(self.package_folder, "aar", "libxray.aar"),
|
||||
})
|
||||
90
recipes/amnezia-xray-bindings/conanfile.py
Normal file
90
recipes/amnezia-xray-bindings/conanfile.py
Normal file
@@ -0,0 +1,90 @@
|
||||
# conanfile.py
|
||||
from conan import ConanFile
|
||||
from conan.tools.files import get, copy, collect_libs, chdir
|
||||
from conan.tools.layout import basic_layout
|
||||
from conan.errors import ConanInvalidConfiguration
|
||||
from conan.tools.gnu import Autotools, AutotoolsToolchain
|
||||
from conan.tools.env import Environment
|
||||
|
||||
import os
|
||||
|
||||
class AmneziaXrayBindings(ConanFile):
|
||||
name = "amnezia-xray-bindings"
|
||||
version = "1.1.0"
|
||||
|
||||
settings = "os", "arch"
|
||||
|
||||
@property
|
||||
def _goos(self):
|
||||
return {
|
||||
"Linux": "linux",
|
||||
"iOS": "ios",
|
||||
"Macos": "darwin",
|
||||
"Windows": "windows"
|
||||
}.get(str(self.settings.os))
|
||||
|
||||
@property
|
||||
def _goarch(self):
|
||||
return {
|
||||
"x86": "386",
|
||||
"x86_64": "amd64",
|
||||
"armv8": "arm64"
|
||||
}.get(str(self.settings.arch))
|
||||
|
||||
@property
|
||||
def _is_windows(self):
|
||||
return str(self.settings.os).startswith("Windows")
|
||||
|
||||
def validate(self):
|
||||
if not self._goos or not self._goarch:
|
||||
raise ConanInvalidConfiguration(
|
||||
f"{self.name} v{self.version} does not support {self.settings.os} {self.settings.arch}"
|
||||
)
|
||||
|
||||
def layout(self):
|
||||
basic_layout(self)
|
||||
|
||||
def build_requirements(self):
|
||||
self.tool_requires("go/1.26.0")
|
||||
|
||||
def source(self):
|
||||
get(self, "https://github.com/amnezia-vpn/amnezia-xray-bindings/archive/v1.1.0.zip",
|
||||
sha256="6ea768ec7002cedd422a39aea17704b888acaf794432aa5937cfc92fb6d80eb5", strip_root=True)
|
||||
|
||||
def generate(self):
|
||||
tc = AutotoolsToolchain(self)
|
||||
env = Environment()
|
||||
env.define("ARCH", self._goarch)
|
||||
env.define("GOARCH", self._goarch)
|
||||
env.define("GOOS", self._goos)
|
||||
env.define("CGO_LDFLAGS", tc.ldflags)
|
||||
env.define("CGO_CFLAGS", tc.cflags)
|
||||
if self._is_windows:
|
||||
env.define("OS", "windows")
|
||||
tc.generate(env)
|
||||
|
||||
def build(self):
|
||||
with chdir(self, self.source_folder):
|
||||
autotools = Autotools(self)
|
||||
autotools.make()
|
||||
|
||||
def _rename_libs(self):
|
||||
# workaround of bad naming strategy in amnezia-xray-bindings
|
||||
# TODO: change it and kick out the code below
|
||||
lib_dir = os.path.join(self.package_folder, "lib")
|
||||
for fname in os.listdir(lib_dir):
|
||||
if not fname.startswith("lib"):
|
||||
src = os.path.join(lib_dir, fname)
|
||||
dst = os.path.join(lib_dir, "lib" + fname)
|
||||
os.rename(src, dst)
|
||||
|
||||
def package(self):
|
||||
copy(self, "*.h", src=self.build_folder, dst=os.path.join(self.package_folder, "include"))
|
||||
copy(self, "*.a", src=self.build_folder, dst=os.path.join(self.package_folder, "lib"))
|
||||
copy(self, "*.dll", src=self.build_folder, dst=os.path.join(self.package_folder, "lib"))
|
||||
copy(self, "*.lib", src=self.build_folder, dst=os.path.join(self.package_folder, "lib"))
|
||||
self._rename_libs()
|
||||
|
||||
def package_info(self):
|
||||
self.cpp_info.set_property("cmake_target_name", "amnezia::xray-bindings")
|
||||
self.cpp_info.libs = collect_libs(self)
|
||||
131
recipes/android-openssl/conanfile.py
Normal file
131
recipes/android-openssl/conanfile.py
Normal file
@@ -0,0 +1,131 @@
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
import shutil
|
||||
|
||||
from conan import ConanFile
|
||||
from conan.tools.files import copy
|
||||
from conan.tools.scm import Git
|
||||
from conan.errors import ConanInvalidConfiguration, ConanException
|
||||
|
||||
|
||||
class AndroidOpenSSL(ConanFile):
|
||||
name = "android-openssl"
|
||||
version = "3.5.5"
|
||||
settings = "os", "arch"
|
||||
options = {"page_16k": [True, False]}
|
||||
default_options = {"page_16k": True}
|
||||
|
||||
# Conan arch → OpenSSL Configure target
|
||||
_arch_map = {
|
||||
"armv8": "android-arm64",
|
||||
"armv7": "android-arm",
|
||||
"x86_64": "android-x86_64",
|
||||
"x86": "android-x86",
|
||||
}
|
||||
|
||||
def validate(self):
|
||||
if self.settings.os != "Android":
|
||||
raise ConanInvalidConfiguration(f"{self.name} only supports Android")
|
||||
if str(self.settings.arch) not in self._arch_map:
|
||||
raise ConanInvalidConfiguration(f"Unsupported arch: {self.settings.arch}")
|
||||
|
||||
def source(self):
|
||||
git = Git(self)
|
||||
git.clone(
|
||||
url="https://github.com/openssl/openssl.git",
|
||||
target=".",
|
||||
args=["--branch", f"openssl-{self.version}", "--depth", "1"]
|
||||
)
|
||||
|
||||
def _ndk(self):
|
||||
ndk = self.conf.get("tools.android:ndk_path", check_type=str) or \
|
||||
os.environ.get("ANDROID_NDK_ROOT", "")
|
||||
if not ndk:
|
||||
raise ConanException(
|
||||
"Android NDK not found. Set tools.android:ndk_path conf or ANDROID_NDK_ROOT env."
|
||||
)
|
||||
return ndk
|
||||
|
||||
def _toolchain_bin(self):
|
||||
host = "linux-x86_64" if platform.system() == "Linux" else "darwin-x86_64"
|
||||
return os.path.join(self._ndk(), "toolchains", "llvm", "prebuilt", host, "bin")
|
||||
|
||||
def _patch_makefile(self, makefile_path, page_16k=False):
|
||||
"""Rename libcrypto.so → libcrypto_3.so and libssl.so → libssl_3.so
|
||||
throughout the generated Makefile so the built files already carry the
|
||||
correct names and sonames — no patchelf required.
|
||||
Optionally append the 16KB page-size linker flag to LDFLAGS."""
|
||||
with open(makefile_path, "r") as f:
|
||||
content = f.read()
|
||||
|
||||
# Replace every standalone libcrypto.so / libssl.so reference.
|
||||
# \b word-boundary keeps us from matching libcrypto.so.3 style names.
|
||||
content = re.sub(r"\blibcrypto\.so\b", "libcrypto_3.so", content)
|
||||
content = re.sub(r"\blibssl\.so\b", "libssl_3.so", content)
|
||||
|
||||
if page_16k:
|
||||
page_flag = "-Wl,-z,max-page-size=16384"
|
||||
content = re.sub(
|
||||
r"^(LDFLAGS\s*=)(.*?)$",
|
||||
lambda m: m.group(0) + " " + page_flag if page_flag not in m.group(0) else m.group(0),
|
||||
content,
|
||||
flags=re.MULTILINE,
|
||||
)
|
||||
|
||||
with open(makefile_path, "w") as f:
|
||||
f.write(content)
|
||||
|
||||
def build(self):
|
||||
ndk = self._ndk()
|
||||
toolchain_bin = self._toolchain_bin()
|
||||
target = self._arch_map[str(self.settings.arch)]
|
||||
api_level = str(self.settings.os.api_level)
|
||||
jobs = os.cpu_count() or 4
|
||||
|
||||
env_prefix = f'PATH="{toolchain_bin}:$PATH" ANDROID_NDK_ROOT="{ndk}"'
|
||||
|
||||
configure_cmd = (
|
||||
f"./Configure {target} shared no-tests"
|
||||
f" -D__ANDROID_API__={api_level}"
|
||||
)
|
||||
|
||||
self.run(f"{env_prefix} {configure_cmd}", cwd=self.source_folder)
|
||||
|
||||
self._patch_makefile(
|
||||
os.path.join(self.source_folder, "Makefile"),
|
||||
page_16k=bool(self.options.page_16k),
|
||||
)
|
||||
|
||||
self.run(
|
||||
f"{env_prefix} make -j{jobs} SHLIB_VERSION_NUMBER= build_libs",
|
||||
cwd=self.source_folder
|
||||
)
|
||||
|
||||
def package(self):
|
||||
lib_dst = os.path.join(self.package_folder, "lib")
|
||||
os.makedirs(lib_dst, exist_ok=True)
|
||||
|
||||
copy(self, "libcrypto_3.so", src=self.source_folder, dst=lib_dst, keep_path=False)
|
||||
copy(self, "libssl_3.so", src=self.source_folder, dst=lib_dst, keep_path=False)
|
||||
|
||||
# Static libs (still named without suffix)
|
||||
copy(self, "libcrypto.a", src=self.source_folder, dst=lib_dst, keep_path=False)
|
||||
copy(self, "libssl.a", src=self.source_folder, dst=lib_dst, keep_path=False)
|
||||
|
||||
# Headers
|
||||
copy(self, "include/openssl/*.h", src=self.source_folder, dst=self.package_folder)
|
||||
|
||||
def package_info(self):
|
||||
# Expose the same CMake targets as the upstream openssl package so
|
||||
# consumers (libssh, main app) need no changes to their target_link_libraries.
|
||||
self.cpp_info.components["crypto"].set_property("cmake_target_name", "OpenSSL::Crypto")
|
||||
self.cpp_info.components["crypto"].libs = ["crypto_3"]
|
||||
self.cpp_info.components["crypto"].includedirs = ["include"]
|
||||
self.cpp_info.components["crypto"].libdirs = ["lib"]
|
||||
|
||||
self.cpp_info.components["ssl"].set_property("cmake_target_name", "OpenSSL::SSL")
|
||||
self.cpp_info.components["ssl"].libs = ["ssl_3"]
|
||||
self.cpp_info.components["ssl"].includedirs = []
|
||||
self.cpp_info.components["ssl"].libdirs = ["lib"]
|
||||
self.cpp_info.components["ssl"].requires = ["crypto"]
|
||||
94
recipes/awg-android/conanfile.py
Normal file
94
recipes/awg-android/conanfile.py
Normal file
@@ -0,0 +1,94 @@
|
||||
from conan import ConanFile
|
||||
from conan.tools.cmake import cmake_layout, CMake, CMakeToolchain
|
||||
from conan.tools.env import VirtualBuildEnv
|
||||
from conan.tools.files import copy, replace_in_file
|
||||
from conan.errors import ConanInvalidConfiguration
|
||||
from conan.tools.scm import Git
|
||||
|
||||
import os
|
||||
import platform
|
||||
|
||||
class AwgAndroid(ConanFile):
|
||||
name = "awg-android"
|
||||
version = "1.1.7"
|
||||
settings = "os", "arch", "build_type", "compiler"
|
||||
package_type = "shared-library"
|
||||
options = {"page_16k": [True, False]}
|
||||
default_options = {"page_16k": True}
|
||||
|
||||
def layout(self):
|
||||
cmake_layout(self)
|
||||
|
||||
def build_requirements(self):
|
||||
self.tool_requires("cmake/[>=3.4.1 <4]")
|
||||
|
||||
def validate(self):
|
||||
if self.settings.os != "Android":
|
||||
raise ConanInvalidConfiguration(f"{self.name} v{self.version} does not support {self.settings.os}")
|
||||
|
||||
def source(self):
|
||||
git = Git(self)
|
||||
git.clone(
|
||||
url="https://github.com/amnezia-vpn/amneziawg-android.git",
|
||||
target=".",
|
||||
args=["--recurse-submodules", "--branch", f"v{self.version}"]
|
||||
)
|
||||
|
||||
def generate(self):
|
||||
tc = CMakeToolchain(self)
|
||||
tc.variables["GRADLE_USER_HOME"] = os.path.join(self.build_folder, "gradle_user_home")
|
||||
tc.variables["CMAKE_LIBRARY_OUTPUT_DIRECTORY"] = os.path.join(self.build_folder, "out")
|
||||
if self.options.page_16k:
|
||||
# Use cache_variables to override NDK toolchain defaults (extra_ldflags sets _INIT
|
||||
# variants which the NDK toolchain can override). CMAKE_SHARED_LINKER_FLAGS is passed
|
||||
# directly to the Go Makefile as LDFLAGS, which flows into CGO_LDFLAGS.
|
||||
page_flag = "-Wl,-z,max-page-size=16384"
|
||||
tc.cache_variables["CMAKE_EXE_LINKER_FLAGS"] = page_flag
|
||||
tc.cache_variables["CMAKE_SHARED_LINKER_FLAGS"] = page_flag
|
||||
tc.generate()
|
||||
|
||||
vbe = VirtualBuildEnv(self)
|
||||
vbe.generate()
|
||||
|
||||
def _patch_sources(self):
|
||||
if platform.system() == 'Darwin':
|
||||
replace_in_file(self,
|
||||
os.path.join(self.source_folder, "tunnel", "tools", "libwg-go", "Makefile"),
|
||||
'flock "$@.lock" -c \' \\\n',
|
||||
"",
|
||||
)
|
||||
replace_in_file(self,
|
||||
os.path.join(self.source_folder, "tunnel", "tools", "libwg-go", "Makefile"),
|
||||
'mv "$@.tmp" "$@"\'',
|
||||
'mv "$@.tmp" "$@"',
|
||||
)
|
||||
replace_in_file(self,
|
||||
os.path.join(self.source_folder, "tunnel", "tools", "libwg-go", "Makefile"),
|
||||
'touch "$@"\'',
|
||||
'touch "$@"',
|
||||
)
|
||||
replace_in_file(self,
|
||||
os.path.join(self.source_folder, "tunnel", "tools", "libwg-go", "Makefile"),
|
||||
'sha256sum -c',
|
||||
'shasum -a 256 -c'
|
||||
)
|
||||
|
||||
def build(self):
|
||||
self._patch_sources()
|
||||
cmake = CMake(self)
|
||||
cmake.configure(build_script_folder=os.path.join(self.source_folder, "tunnel", "tools"))
|
||||
cmake.build(target=["libwg-go.so", "libwg.so", "libwg-quick.so"])
|
||||
|
||||
def package(self):
|
||||
copy(self, "libwg-go.h", src=os.path.join(self.build_folder, "out"), dst=os.path.join(self.package_folder, "include"))
|
||||
copy(self, "libwg-go.so", src=os.path.join(self.build_folder, "out"), dst=os.path.join(self.package_folder, "lib"))
|
||||
copy(self, "libwg.so", src=os.path.join(self.build_folder, "out"), dst=os.path.join(self.package_folder, "bin"))
|
||||
copy(self, "libwg-quick.so", src=os.path.join(self.build_folder, "out"), dst=os.path.join(self.package_folder, "bin"))
|
||||
|
||||
def package_info(self):
|
||||
self.cpp_info.set_property("cmake_target_name", "amnezia::awg-android")
|
||||
self.cpp_info.libs = [ "wg-go" ]
|
||||
self.cpp_info.set_property("cmake_extra_variables", {
|
||||
"AMNEZIA_ANDROID_LIBWG_PATH": os.path.join(self.package_folder, "bin", "libwg.so"),
|
||||
"AMNEZIA_ANDROID_LIBWG_QUICK_PATH": os.path.join(self.package_folder, "bin", "libwg-quick.so"),
|
||||
})
|
||||
68
recipes/awg-apple/conanfile.py
Normal file
68
recipes/awg-apple/conanfile.py
Normal file
@@ -0,0 +1,68 @@
|
||||
from conan import ConanFile
|
||||
from conan.errors import ConanInvalidConfiguration
|
||||
from conan.tools.layout import basic_layout
|
||||
from conan.tools.files import get, copy, collect_libs, chdir
|
||||
from conan.tools.apple import is_apple_os
|
||||
from conan.tools.gnu import AutotoolsToolchain, Autotools
|
||||
|
||||
import os
|
||||
|
||||
class AwgApple(ConanFile):
|
||||
name = "awg-apple"
|
||||
version = "2.0.1"
|
||||
settings = "os", "arch"
|
||||
options = {
|
||||
"as_framework": [True, False]
|
||||
}
|
||||
default_options = {
|
||||
"as_framework": False
|
||||
}
|
||||
|
||||
@property
|
||||
def _goarch(self):
|
||||
arch_map = {
|
||||
"armv8": "arm64",
|
||||
"x86_64": "x86_64",
|
||||
}
|
||||
archs = str(self.settings.arch).split("|")
|
||||
return " ".join(arch_map.get(arch, arch) for arch in archs)
|
||||
|
||||
def build_requirements(self):
|
||||
self.tool_requires("go/1.26.0")
|
||||
|
||||
def layout(self):
|
||||
basic_layout(self, build_folder=os.path.join(self.folders.source, "Sources/WireGuardKitGo"))
|
||||
|
||||
def validate(self):
|
||||
if not is_apple_os(self):
|
||||
raise ConanInvalidConfiguration(
|
||||
f"{self.name} v{self.version} does not support {self.settings.os}"
|
||||
)
|
||||
|
||||
def source(self):
|
||||
get(self, f"https://github.com/amnezia-vpn/amneziawg-apple/archive/refs/tags/v{self.version}.zip",
|
||||
sha256="9fe4f8cfbb6a751558b54b7979db3a5ea46e49731912aae99f093e84a1433e97", strip_root=True
|
||||
)
|
||||
|
||||
def generate(self):
|
||||
tc = AutotoolsToolchain(self)
|
||||
sdk = self.settings.get_safe("os.sdk", "macosx")
|
||||
tc.make_args = [
|
||||
f"ARCHS={self._goarch}",
|
||||
f"PLATFORM_NAME={sdk}"
|
||||
]
|
||||
tc.generate()
|
||||
|
||||
def build(self):
|
||||
autotools = Autotools(self)
|
||||
autotools.make()
|
||||
autotools.make("version-header")
|
||||
|
||||
def package(self):
|
||||
copy(self, "wireguard.h", src=self.build_folder, dst=os.path.join(self.package_folder, "include"))
|
||||
copy(self, "*.h", src=os.path.join(self.build_folder, "out"), dst=os.path.join(self.package_folder, "include"))
|
||||
copy(self, "*.a", src=os.path.join(self.build_folder, "out"), dst=os.path.join(self.package_folder, "lib"))
|
||||
|
||||
def package_info(self):
|
||||
self.cpp_info.set_property("cmake_target_name", "amnezia::awg-apple")
|
||||
self.cpp_info.libs = collect_libs(self)
|
||||
66
recipes/awg-go/conanfile.py
Normal file
66
recipes/awg-go/conanfile.py
Normal file
@@ -0,0 +1,66 @@
|
||||
from conan import ConanFile
|
||||
from conan.errors import ConanInvalidConfiguration
|
||||
from conan.tools.layout import basic_layout
|
||||
from conan.tools.files import get, copy
|
||||
from conan.tools.gnu import Autotools, AutotoolsToolchain
|
||||
from conan.tools.env import Environment
|
||||
|
||||
import os
|
||||
|
||||
class AwgGo(ConanFile):
|
||||
name = "awg-go"
|
||||
version = "0.2.16"
|
||||
package_type = "application"
|
||||
settings = "os", "arch"
|
||||
|
||||
@property
|
||||
def _goos(self):
|
||||
return {
|
||||
"Linux": "linux",
|
||||
"Macos": "darwin",
|
||||
"Windows": "windows"
|
||||
}.get(str(self.settings.os))
|
||||
|
||||
@property
|
||||
def _goarch(self):
|
||||
return {
|
||||
"x86": "386",
|
||||
"x86_64": "amd64",
|
||||
"armv8": "arm64"
|
||||
}.get(str(self.settings.arch))
|
||||
|
||||
def build_requirements(self):
|
||||
self.tool_requires("go/1.26.0")
|
||||
|
||||
def validate(self):
|
||||
if not self._goos or not self._goarch:
|
||||
raise ConanInvalidConfiguration(
|
||||
f"{self.name} v{self.version} does not support {self.settings.os} {self.settings.arch}"
|
||||
)
|
||||
|
||||
def source(self):
|
||||
get(self, f"https://github.com/amnezia-vpn/amneziawg-go/archive/refs/tags/v{self.version}.zip",
|
||||
sha256="34da7d4189f215f3930de441548bc2a0c89d54d347a4fb85cb9c715fce6413aa", strip_root=True
|
||||
)
|
||||
|
||||
def layout(self):
|
||||
basic_layout(self, build_folder=".")
|
||||
|
||||
def generate(self):
|
||||
tc = AutotoolsToolchain(self)
|
||||
env = Environment()
|
||||
env.define("GOOS", self._goos)
|
||||
env.define("GOARCH", self._goarch)
|
||||
tc.generate(env)
|
||||
|
||||
def build(self):
|
||||
at = Autotools(self)
|
||||
at.make()
|
||||
|
||||
def package(self):
|
||||
copy(self, "amneziawg-go", src=self.build_folder, dst=self.package_folder)
|
||||
|
||||
def package_info(self):
|
||||
self.cpp_info.exe = True
|
||||
self.cpp_info.location = os.path.join(self.package_folder, "amneziawg-go")
|
||||
self.cpp_info.set_property("cmake_target_name", "amnezia::awg-go")
|
||||
0
recipes/awg-windows/conanfile.py
Normal file
0
recipes/awg-windows/conanfile.py
Normal file
26
recipes/go/conandata.yml
Normal file
26
recipes/go/conandata.yml
Normal file
@@ -0,0 +1,26 @@
|
||||
sources:
|
||||
"1.26.0":
|
||||
Macos:
|
||||
x86_64:
|
||||
url: "https://go.dev/dl/go1.26.0.darwin-amd64.tar.gz"
|
||||
sha256: "1ca28b7703cbea05a65b2a1d92d6b308610ef92f8824578a0874f2e60c9d5a22"
|
||||
armv8:
|
||||
url: "https://go.dev/dl/go1.26.0.darwin-arm64.tar.gz"
|
||||
sha256: "b1640525dfe68f066d56f200bef7bf4dce955a1a893bd061de6754c211431023"
|
||||
Linux:
|
||||
x86:
|
||||
url: "https://go.dev/dl/go1.26.0.linux-386.tar.gz"
|
||||
sha256: "35e2ec7a7ae6905a1fae5459197b70e3fcbc5e0a786a7d6ba8e49bcd38ad2e26"
|
||||
x86_64:
|
||||
url: "https://go.dev/dl/go1.26.0.linux-amd64.tar.gz"
|
||||
sha256: "aac1b08a0fb0c4e0a7c1555beb7b59180b05dfc5a3d62e40e9de90cd42f88235"
|
||||
armv8:
|
||||
url: "https://go.dev/dl/go1.26.0.linux-arm64.tar.gz"
|
||||
sha256: "bd03b743eb6eb4193ea3c3fd3956546bf0e3ca5b7076c8226334afe6b75704cd"
|
||||
Windows:
|
||||
x86:
|
||||
url: "https://go.dev/dl/go1.26.0.windows-386.zip"
|
||||
sha256: "50674f3d6a071fa1a4c1d76dc37fafa0330df87d84087a262fee020da5396b6b"
|
||||
x86_64:
|
||||
url: "https://go.dev/dl/go1.26.0.windows-amd64.zip"
|
||||
sha256: "9bbe0fc64236b2b51f6255c05c4232532b8ecc0e6d2e00950bd3021d8a4d07d4"
|
||||
19
recipes/go/conanfile.py
Normal file
19
recipes/go/conanfile.py
Normal file
@@ -0,0 +1,19 @@
|
||||
from conan import ConanFile
|
||||
from conan.tools.files import get, copy
|
||||
|
||||
import os
|
||||
|
||||
class Golang(ConanFile):
|
||||
name = "go"
|
||||
version = "1.26.0"
|
||||
|
||||
settings = "os", "arch"
|
||||
|
||||
def build(self):
|
||||
get(self, **self.conan_data["sources"][str(self.version)][str(self.settings.os)][str(self.settings.arch)])
|
||||
|
||||
def package(self):
|
||||
copy(self, "*", src=os.path.join(self.source_folder, "go"), dst=self.package_folder)
|
||||
|
||||
def package_info(self):
|
||||
self.cpp_info.bindirs = ["bin"]
|
||||
98
recipes/hev-socks5-tunnel/conanfile.py
Normal file
98
recipes/hev-socks5-tunnel/conanfile.py
Normal file
@@ -0,0 +1,98 @@
|
||||
from conan import ConanFile
|
||||
from conan.tools.layout import basic_layout
|
||||
from conan.tools.scm import Git
|
||||
from conan.tools.files import copy, collect_libs
|
||||
from conan.internal.model.pkg_type import PackageType
|
||||
from conan.tools.gnu import AutotoolsToolchain, Autotools
|
||||
from conan.tools.apple import is_apple_os
|
||||
|
||||
import os
|
||||
import shutil
|
||||
|
||||
|
||||
required_conan_version = ">=2.26"
|
||||
|
||||
|
||||
class HevSocks5Tunnel(ConanFile):
|
||||
name = "hev-socks5-tunnel"
|
||||
version = "2.14.4"
|
||||
settings = "os", "arch"
|
||||
options = {
|
||||
"shared": [True, False],
|
||||
"as_framework": [True, False],
|
||||
}
|
||||
default_options = {
|
||||
"shared": False,
|
||||
"as_framework": False
|
||||
}
|
||||
|
||||
def config_options(self):
|
||||
if not is_apple_os(self):
|
||||
del self.options.as_framework
|
||||
|
||||
def configure(self):
|
||||
if self.options.get_safe("as_framework"):
|
||||
self.options.shared = False
|
||||
|
||||
def layout(self):
|
||||
basic_layout(self, build_folder=".")
|
||||
|
||||
def source(self):
|
||||
git = Git(self)
|
||||
git.clone(
|
||||
url="https://github.com/heiher/hev-socks5-tunnel.git",
|
||||
target=".",
|
||||
args=["--recurse-submodules", "--branch", self.version]
|
||||
)
|
||||
|
||||
def generate(self):
|
||||
tc = AutotoolsToolchain(self)
|
||||
tc.generate()
|
||||
|
||||
def build(self):
|
||||
autotools = Autotools(self)
|
||||
autotools.make("shared" if self.options.shared else "static")
|
||||
|
||||
if self.options.get_safe("as_framework"):
|
||||
lib_path = os.path.join(self.build_folder, "bin", "libhev-socks5-tunnel.a")
|
||||
self.run(
|
||||
f"libtool -static -o {lib_path}"
|
||||
f" {lib_path}"
|
||||
f" {os.path.join(self.build_folder, "third-part", "lwip", "bin", "liblwip.a")}"
|
||||
f" {os.path.join(self.build_folder, "third-part", "yaml", "bin", "libyaml.a")}"
|
||||
f" {os.path.join(self.build_folder, "third-part", "hev-task-system", "bin", "libhev-task-system.a")}"
|
||||
)
|
||||
|
||||
include_dir = os.path.join(self.build_folder, "framework_include")
|
||||
copy(self, "hev-main.h", src=os.path.join(self.source_folder, "src"), dst=include_dir)
|
||||
copy(self, "module.modulemap", src=os.path.join(self.source_folder), dst=include_dir)
|
||||
|
||||
self.run('xcodebuild -create-xcframework'
|
||||
f' -library {lib_path}'
|
||||
f' -headers {include_dir}'
|
||||
f' -output {os.path.join(self.build_folder, "HevSocks5Tunnel.xcframework")}'
|
||||
)
|
||||
|
||||
def package(self):
|
||||
if self.options.get_safe("as_framework"):
|
||||
shutil.copytree(src=os.path.join(self.build_folder, "HevSocks5Tunnel.xcframework"),
|
||||
dst=os.path.join(self.package_folder, "HevSocks5Tunnel.xcframework"))
|
||||
else:
|
||||
copy(self, "hev-main.h", src=os.path.join(self.source_folder, "src"), dst=os.path.join(self.package_folder, "include"))
|
||||
copy(self, "*.a", src=os.path.join(self.build_folder, "bin"), dst=os.path.join(self.package_folder, "lib"))
|
||||
copy(self, "*.so", src=os.path.join(self.build_folder, "bin"), dst=os.path.join(self.package_folder, "lib"))
|
||||
copy(self, "*.a", src=os.path.join(self.build_folder, "bin", "third-part", "lwip"), dst=os.path.join(self.package_folder, "lib"))
|
||||
copy(self, "*.so", src=os.path.join(self.build_folder, "bin", "third-part", "lwip"), dst=os.path.join(self.package_folder, "lib"))
|
||||
copy(self, "*.a", src=os.path.join(self.build_folder, "bin", "third-part", "yaml"), dst=os.path.join(self.package_folder, "lib"))
|
||||
copy(self, "*.so", src=os.path.join(self.build_folder, "bin", "third-part", "yaml"), dst=os.path.join(self.package_folder, "lib"))
|
||||
copy(self, "*.a", src=os.path.join(self.build_folder, "bin", "third-part", "hev-task-system"), dst=os.path.join(self.package_folder, "lib"))
|
||||
copy(self, "*.so", src=os.path.join(self.build_folder, "bin", "third-part", "hev-task-system"), dst=os.path.join(self.package_folder, "lib"))
|
||||
|
||||
def package_info(self):
|
||||
self.cpp_info.set_property("cmake_target_name", "heiher::hev-socks5-tunnel")
|
||||
if self.options.get_safe("as_framework"):
|
||||
self.cpp_info.type = PackageType.STATIC
|
||||
self.cpp_info.package_framework = True
|
||||
self.cpp_info.location = os.path.join(self.package_folder, "HevSocks5Tunnel.xcframework")
|
||||
else:
|
||||
self.cpp_info.libraries = collect_libs(self)
|
||||
142
recipes/libssh/conanfile.py
Normal file
142
recipes/libssh/conanfile.py
Normal file
@@ -0,0 +1,142 @@
|
||||
from conan import ConanFile
|
||||
from conan.errors import ConanInvalidConfiguration
|
||||
from conan.tools.cmake import CMakeToolchain, CMake, cmake_layout, CMakeDeps
|
||||
from conan.tools.files import copy, get, rmdir
|
||||
from conan.tools.microsoft import is_msvc_static_runtime, is_msvc
|
||||
from conan.tools.scm import Version
|
||||
import os
|
||||
|
||||
|
||||
required_conan_version = ">=2.21"
|
||||
|
||||
|
||||
class LibSSHRecipe(ConanFile):
|
||||
name = "libssh"
|
||||
version = "0.11.3"
|
||||
user = "amnezia"
|
||||
license = "LGPL-2.1"
|
||||
homepage = "https://www.libssh.org/"
|
||||
description = "multiplatform C library implementing the SSHv2 protocol on client and server side"
|
||||
topics = ("ssh", "shell", "ssh2", "connection")
|
||||
package_type = "library"
|
||||
settings = "os", "compiler", "build_type", "arch"
|
||||
options = {
|
||||
"shared": [True, False],
|
||||
"fPIC": [True, False],
|
||||
"with_zlib": [True, False],
|
||||
"crypto_backend": ["openssl", "gcrypt", "mbedtls"],
|
||||
"with_symbol_versioning": [True, False],
|
||||
"page_16k": [True, False],
|
||||
}
|
||||
default_options = {
|
||||
"shared": False,
|
||||
"fPIC": True,
|
||||
"with_zlib": True,
|
||||
"crypto_backend": "openssl",
|
||||
"with_symbol_versioning": True,
|
||||
"page_16k": True,
|
||||
}
|
||||
|
||||
def config_options(self):
|
||||
if self.settings.os == "Windows":
|
||||
del self.options.fPIC
|
||||
del self.options.with_symbol_versioning
|
||||
|
||||
def configure(self):
|
||||
if self.options.shared:
|
||||
self.options.rm_safe("fPIC")
|
||||
self.settings.rm_safe("compiler.libcxx")
|
||||
self.settings.rm_safe("compiler.cppstd")
|
||||
|
||||
def layout(self):
|
||||
cmake_layout(self, src_folder="src")
|
||||
|
||||
def requirements(self):
|
||||
if self.options.with_zlib:
|
||||
self.requires("zlib/[>=1.2.11 <2]")
|
||||
if self.options.crypto_backend == "openssl":
|
||||
if self.settings.os == "Android":
|
||||
self.requires("android-openssl/3.5.5")
|
||||
else:
|
||||
self.requires("openssl/[>=1.1 <4]")
|
||||
elif self.options.crypto_backend == "gcrypt":
|
||||
self.requires("libgcrypt/[>=1.8.4 <2]")
|
||||
elif self.options.crypto_backend == "mbedtls":
|
||||
self.requires("mbedtls/3.6.0")
|
||||
|
||||
def validate(self):
|
||||
if self.options.crypto_backend == "mbedtls" and not self.dependencies["mbedtls"].options.enable_threading:
|
||||
raise ConanInvalidConfiguration(f"{self.ref} requires '-o mbedtls/*:enable_threading=True' when using '-o libssh/*:crypto_backend=mbedtls'")
|
||||
|
||||
def source(self):
|
||||
get(self, "https://www.libssh.org/files/0.11/libssh-0.11.3.tar.xz",
|
||||
sha256="7d8a1361bb094ec3f511964e78a5a4dba689b5986e112afabe4f4d0d6c6125c3", strip_root=True
|
||||
)
|
||||
|
||||
def generate(self):
|
||||
tc = CMakeToolchain(self)
|
||||
tc.cache_variables["CLIENT_TESTING"] = False
|
||||
tc.cache_variables["SERVER_TESTING"] = False
|
||||
tc.cache_variables["WITH_EXAMPLES"] = False
|
||||
tc.cache_variables["WITH_GCRYPT"] = self.options.crypto_backend == "gcrypt"
|
||||
tc.cache_variables["WITH_GSSAPI"] = False
|
||||
tc.cache_variables["WITH_MBEDTLS"] = self.options.crypto_backend == "mbedtls"
|
||||
tc.cache_variables["WITH_NACL"] = False
|
||||
tc.cache_variables["WITH_SYMBOL_VERSIONING"] = self.options.get_safe("with_symbol_versioning", True)
|
||||
tc.variables["WITH_ZLIB"] = self.options.with_zlib
|
||||
if is_msvc(self):
|
||||
tc.cache_variables["USE_MSVC_RUNTIME_LIBRARY_DLL"] = not is_msvc_static_runtime(self)
|
||||
tc.cache_variables["CMAKE_POLICY_DEFAULT_CMP0077"] = "NEW"
|
||||
tc.cache_variables["CMAKE_TRY_COMPILE_CONFIGURATION"] = str(self.settings.build_type)
|
||||
if self.settings.os == "Android":
|
||||
if Version(self.settings.get_safe("os.api_level")) < 24:
|
||||
tc.cache_variables["HAVE_IFADDRS_H"] = False
|
||||
tc.cache_variables["HAVE_GLOB_H"] = False
|
||||
tc.preprocessor_definitions["S_IWRITE"] = "S_IRUSR"
|
||||
tc.preprocessor_definitions["S_IWRITE"] = "S_IWUSR"
|
||||
tc.preprocessor_definitions["S_IEXEC"] = "S_IXUSR"
|
||||
if self.options.page_16k:
|
||||
tc.cache_variables["CMAKE_SHARED_LINKER_FLAGS"] = "-Wl,-z,max-page-size=16384"
|
||||
tc.cache_variables["CMAKE_MODULE_LINKER_FLAGS"] = "-Wl,-z,max-page-size=16384"
|
||||
tc.generate()
|
||||
|
||||
deps = CMakeDeps(self)
|
||||
deps.set_property("libgcrypt", "cmake_file_name", "GCrypt")
|
||||
deps.set_property("libgcrypt", "cmake_additional_variables_prefixes", ["GCRYPT"])
|
||||
deps.set_property("libgcrypt", "cmake_extra_variables", {"GCRYPT_FOUND": "TRUE"})
|
||||
deps.set_property("mbedtls", "cmake_additional_variables_prefixes", ["MBEDTLS"])
|
||||
deps.set_property("mbedtls", "cmake_extra_variables", {"MBEDTLS_FOUND": "TRUE"})
|
||||
deps.generate()
|
||||
|
||||
def build(self):
|
||||
cmake = CMake(self)
|
||||
cmake.configure()
|
||||
cmake.build()
|
||||
|
||||
def package(self):
|
||||
copy(self, pattern="COPYING", dst=os.path.join(self.package_folder, "licenses"), src=self.source_folder)
|
||||
cmake = CMake(self)
|
||||
cmake.install()
|
||||
rmdir(self, os.path.join(self.package_folder, "lib", "cmake"))
|
||||
rmdir(self, os.path.join(self.package_folder, "lib", "pkgconfig"))
|
||||
|
||||
def package_info(self):
|
||||
self.cpp_info.libs = ["ssh"]
|
||||
if not self.options.shared:
|
||||
self.cpp_info.defines.append("LIBSSH_STATIC=ON")
|
||||
if self.settings.os == "Windows":
|
||||
self.cpp_info.system_libs.extend(["ws2_32", "iphlpapi"])
|
||||
|
||||
self.cpp_info.set_property("cmake_file_name", "libssh")
|
||||
# target and alias names defined at:
|
||||
# ssh https://git.libssh.org/projects/libssh.git/tree/src/CMakeLists.txt?h=libssh-0.10.6#n351
|
||||
# ssh::ssh https://git.libssh.org/projects/libssh.git/tree/src/CMakeLists.txt?h=libssh-0.10.6#n371
|
||||
# ssh-static https://git.libssh.org/projects/libssh.git/tree/src/CMakeLists.txt?h=libssh-0.10.6#n413
|
||||
# ssh::static https://git.libssh.org/projects/libssh.git/tree/src/CMakeLists.txt?h=libssh-0.10.6#n428
|
||||
self.cpp_info.set_property("cmake_target_name", "ssh::ssh")
|
||||
self.cpp_info.set_property(
|
||||
"cmake_target_aliases",
|
||||
["ssh"] if self.options.shared else ["ssh", "ssh-static", "ssh::static"],
|
||||
)
|
||||
# pkg-config defined at https://git.libssh.org/projects/libssh.git/tree/CMakeLists.txt?h=libssh-0.10.6#n124
|
||||
self.cpp_info.set_property("pkg_config_name", "libssh")
|
||||
67
recipes/openvpn-pt-android/conanfile.py
Normal file
67
recipes/openvpn-pt-android/conanfile.py
Normal file
@@ -0,0 +1,67 @@
|
||||
from conan import ConanFile
|
||||
from conan.tools.cmake import cmake_layout, CMake, CMakeToolchain
|
||||
from conan.tools.env import VirtualBuildEnv
|
||||
from conan.tools.files import copy
|
||||
from conan.tools.scm import Git
|
||||
from conan.errors import ConanInvalidConfiguration
|
||||
|
||||
import os
|
||||
|
||||
class OpenvpnPtAndroid(ConanFile):
|
||||
name = "openvpn-pt-android"
|
||||
version = "1.0.0"
|
||||
package_type = "shared-library"
|
||||
settings = "os", "arch", "build_type", "compiler"
|
||||
options = {"page_16k": [True, False]}
|
||||
default_options = {"page_16k": True}
|
||||
|
||||
def layout(self):
|
||||
cmake_layout(self)
|
||||
|
||||
def build_requirements(self):
|
||||
self.tool_requires("swig/4.1.1")
|
||||
self.tool_requires("go/1.26.0")
|
||||
self.tool_requires("cmake/[>=3.4.1 <4]")
|
||||
|
||||
def validate(self):
|
||||
if self.settings.os != "Android":
|
||||
raise ConanInvalidConfiguration(f"{self.name} only supports Android, got {self.settings.os}")
|
||||
|
||||
def source(self):
|
||||
git = Git(self)
|
||||
git.clone(
|
||||
url="https://github.com/amnezia-vpn/openvpn-pt-android.git",
|
||||
target=".",
|
||||
args=["--recurse-submodules", "--branch", "update-ovpn3"]
|
||||
)
|
||||
|
||||
def generate(self):
|
||||
tc = CMakeToolchain(self)
|
||||
tc.variables["ANDROID_PACKAGE_NAME"] = "org.amnezia.vpn"
|
||||
tc.variables["ANDROID_PLATFORM"] = 24
|
||||
if self.options.page_16k:
|
||||
page_flag = "-Wl,-z,max-page-size=16384"
|
||||
tc.cache_variables["CMAKE_SHARED_LINKER_FLAGS"] = page_flag
|
||||
tc.cache_variables["CMAKE_EXE_LINKER_FLAGS"] = page_flag
|
||||
tc.generate()
|
||||
|
||||
vbe = VirtualBuildEnv(self)
|
||||
if self.options.page_16k:
|
||||
vbe.environment().define("CGO_LDFLAGS", "-Wl,-z,max-page-size=16384")
|
||||
vbe.generate()
|
||||
|
||||
def build(self):
|
||||
cmake = CMake(self)
|
||||
cmake.configure()
|
||||
cmake.build(target=["ck_ovpn_plugin_go", "ovpn3", "ovpnutil", "rsapss"])
|
||||
|
||||
def package(self):
|
||||
copy(self, "*.h", src=self.build_folder, dst=os.path.join(self.package_folder, "include"))
|
||||
copy(self, "*.so", src=self.build_folder, dst=os.path.join(self.package_folder, "lib"))
|
||||
|
||||
def package_info(self):
|
||||
self.cpp_info.set_property("cmake_target_name", "amnezia::openvpn-pt-android")
|
||||
self.cpp_info.libs = ["ovpn3", "ovpnutil", "rsapss"]
|
||||
self.cpp_info.set_property("cmake_extra_variables", {
|
||||
"OPENVPN_PT_ANDROID_LIBCK_OVPN_PLUGIN_PATH": os.path.join(self.package_folder, "lib", "libck-ovpn-plugin.so"),
|
||||
})
|
||||
48
recipes/openvpn/conanfile.py
Normal file
48
recipes/openvpn/conanfile.py
Normal file
@@ -0,0 +1,48 @@
|
||||
from conan import ConanFile
|
||||
from conan.tools.files import get, copy
|
||||
from conan.tools.gnu import Autotools, AutotoolsToolchain, AutotoolsDeps
|
||||
from conan.tools.layout import basic_layout
|
||||
|
||||
import os
|
||||
|
||||
class Openvpn(ConanFile):
|
||||
name = "openvpn"
|
||||
version = "2.7.0"
|
||||
package_type = "application"
|
||||
settings = "os", "build_type", "arch"
|
||||
|
||||
def layout(self):
|
||||
basic_layout(self)
|
||||
|
||||
def build_requirements(self):
|
||||
self.tool_requires("libtool/2.4.7")
|
||||
self.tool_requires("automake/1.16.5")
|
||||
|
||||
def requirements(self):
|
||||
self.requires("openssl/[>=1.1.0]", visible=False)
|
||||
self.requires("lz4/[>=1.7.1]", visible=False)
|
||||
self.requires("lzo/2.10", visible=False)
|
||||
|
||||
def source(self):
|
||||
get(self, f"https://github.com/OpenVPN/openvpn/archive/refs/tags/v{self.version}.zip",
|
||||
sha256="1a65d8587f932c13d55b1f175ff2e1d61d795d9092788662e888054854d4ee3d", strip_root=True
|
||||
)
|
||||
|
||||
def generate(self):
|
||||
tc = AutotoolsToolchain(self)
|
||||
tc.generate()
|
||||
deps = AutotoolsDeps(self)
|
||||
deps.generate()
|
||||
|
||||
def build(self):
|
||||
at = Autotools(self)
|
||||
at.autoreconf()
|
||||
at.configure()
|
||||
at.make()
|
||||
|
||||
def package(self):
|
||||
copy(self, "openvpn", src=os.path.join(self.build_folder, "src", "openvpn"), dst=self.package_folder)
|
||||
|
||||
def package_info(self):
|
||||
self.cpp_info.exe = True
|
||||
self.cpp_info.location = os.path.join(self.package_folder, "openvpn")
|
||||
72
recipes/openvpnadapter/conanfile.py
Normal file
72
recipes/openvpnadapter/conanfile.py
Normal file
@@ -0,0 +1,72 @@
|
||||
from conan import ConanFile
|
||||
from conan.tools.layout import basic_layout
|
||||
from conan.tools.apple import is_apple_os
|
||||
from conan.errors import ConanInvalidConfiguration
|
||||
from conan.tools.scm import Git
|
||||
from conan.internal.model.pkg_type import PackageType
|
||||
from conan.tools.files import chdir
|
||||
|
||||
import os
|
||||
import shutil
|
||||
|
||||
class OpenVPNAdapter(ConanFile):
|
||||
name = "openvpnadapter"
|
||||
version = "1.0.0"
|
||||
settings = "os", "build_type"
|
||||
|
||||
@property
|
||||
def _sdk(self):
|
||||
return str(self.settings.get_safe("os.sdk", "macosx"))
|
||||
|
||||
@property
|
||||
def _platform(self):
|
||||
return {
|
||||
"macosx": "macOS",
|
||||
"iphoneos": "iOS",
|
||||
"iphonesimulator": "iOS Simulator"
|
||||
}.get(self._sdk)
|
||||
|
||||
@property
|
||||
def _configuration(self):
|
||||
return "Debug" if self.settings.get_safe("build_type") == "Debug" else "Release"
|
||||
|
||||
def layout(self):
|
||||
basic_layout(self)
|
||||
|
||||
def validate(self):
|
||||
if not is_apple_os(self):
|
||||
raise ConanInvalidConfiguration(
|
||||
f"There is absolutely no point building Apple framework for {self.settings.os}"
|
||||
)
|
||||
|
||||
def source(self):
|
||||
git = Git(self)
|
||||
git.clone(
|
||||
url="https://github.com/amnezia-vpn/OpenVPNAdapter.git",
|
||||
target=".",
|
||||
args=["--recurse-submodules", "--branch", "master-amnezia"]
|
||||
)
|
||||
|
||||
def build(self):
|
||||
with chdir(self, self.source_folder):
|
||||
self.run("xcrun xcodebuild"
|
||||
" -project OpenVPNAdapter.xcodeproj"
|
||||
" -scheme OpenVPNAdapter"
|
||||
" -configuration Release"
|
||||
f" -destination 'generic/platform={self._platform}'"
|
||||
f" -sdk {self._sdk}"
|
||||
f' "CONFIGURATION_BUILD_DIR={self.build_folder}"'
|
||||
f' "BUILT_PRODUCTS_DIR={self.build_folder}"'
|
||||
" BUILD_LIBRARY_FOR_DISTRIBUTION=YES"
|
||||
" CODE_SIGNING_ALLOWED=NO"
|
||||
)
|
||||
|
||||
def package(self):
|
||||
shutil.copytree(os.path.join(self.build_folder, "OpenVPNAdapter.framework"),
|
||||
os.path.join(self.package_folder, "OpenVPNAdapter.framework"))
|
||||
|
||||
def package_info(self):
|
||||
self.cpp_info.set_property("cmake_target_name", "amnezia::openvpnadapter")
|
||||
self.cpp_info.type = PackageType.STATIC
|
||||
self.cpp_info.package_framework = True
|
||||
self.cpp_info.location = os.path.join(self.package_folder, "OpenVPNAdapter.framework")
|
||||
80
recipes/tun2socks/conanfile.py
Normal file
80
recipes/tun2socks/conanfile.py
Normal file
@@ -0,0 +1,80 @@
|
||||
from conan import ConanFile
|
||||
from conan.tools.layout import basic_layout
|
||||
from conan.tools.files import get, copy, chdir
|
||||
from conan.errors import ConanInvalidConfiguration
|
||||
from conan.tools.gnu import Autotools, AutotoolsToolchain
|
||||
from conan.tools.env import Environment
|
||||
|
||||
import os
|
||||
|
||||
class Tun2Socks(ConanFile):
|
||||
name = "tun2socks"
|
||||
version = "2.6.0"
|
||||
package_type = "application"
|
||||
settings = "os", "arch"
|
||||
|
||||
@property
|
||||
def _goos(self):
|
||||
return {
|
||||
"Linux": "linux",
|
||||
"Macos": "darwin",
|
||||
"Windows": "windows"
|
||||
}.get(str(self.settings.os))
|
||||
|
||||
@property
|
||||
def _goarch(self):
|
||||
return {
|
||||
"x86": "386",
|
||||
"x86_64": "amd64",
|
||||
"armv8": "arm64"
|
||||
}.get(str(self.settings.arch))
|
||||
|
||||
@property
|
||||
def _is_windows(self):
|
||||
return str(self.settings.get_safe("os")).startswith("Windows")
|
||||
|
||||
@property
|
||||
def _ext(self):
|
||||
return ".exe" if self._is_windows else ""
|
||||
|
||||
def layout(self):
|
||||
basic_layout(self)
|
||||
|
||||
def validate(self):
|
||||
if not self._goos or not self._goarch:
|
||||
raise ConanInvalidConfiguration(
|
||||
f"{self.name} v{self.version} does not support {self.settings.os} {self.settings.arch}"
|
||||
)
|
||||
|
||||
def build_requirements(self):
|
||||
self.tool_requires("go/1.26.0")
|
||||
|
||||
def source(self):
|
||||
get(self, f"https://github.com/xjasonlyu/tun2socks/archive/refs/tags/v{self.version}.zip",
|
||||
sha256="a7ef9cec1c30dfe9971af89a8aac767fd3d2a4df833e92b635642c2f0204c701", strip_root=True
|
||||
)
|
||||
|
||||
def generate(self):
|
||||
tc = AutotoolsToolchain(self)
|
||||
env = Environment()
|
||||
env.define("CGO_LDFLAGS", tc.ldflags)
|
||||
env.define("CGO_CFLAGS", tc.cflags)
|
||||
env.define("GOOS", self._goos)
|
||||
env.define("GOARCH", self._goarch)
|
||||
tc.generate(env)
|
||||
|
||||
def build(self):
|
||||
with chdir(self, self.source_folder):
|
||||
at = Autotools(self)
|
||||
at.make("tun2socks")
|
||||
|
||||
def package(self):
|
||||
copy(self, "tun2socks", src=self.build_folder, dst=self.package_folder)
|
||||
if self._is_windows:
|
||||
with chdir(self, self.package_folder):
|
||||
os.rename(src="tun2socks", dst="tun2socks.exe")
|
||||
|
||||
def package_info(self):
|
||||
self.cpp_info.exe = True
|
||||
self.cpp_info.location = os.path.join(self.package_folder, f"tun2socks{self._ext}")
|
||||
self.cpp_info.set_property("cmake_target_name", "xjasonlyu::tun2socks")
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user