diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index bc106ba0..8f50cae2 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -38,16 +38,23 @@ jobs: - name: 'Build project' run: | + sudo apt-get install libxkbcommon-x11-0 export QT_BIN_DIR=${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/gcc_64/bin export QIF_BIN_DIR=${{ runner.temp }}/Qt/Tools/QtInstallerFramework/${{ env.QIF_VERSION }}/bin bash deploy/build_linux.sh - - name: 'Upload artifact' + - name: 'Upload installer artifact' uses: actions/upload-artifact@v3 with: - name: AmneziaVPN_Linux + name: AmneziaVPN_Linux_installer path: deploy/AmneziaVPN_Linux_Installer - retention-days: 3 + retention-days: 7 + - name: 'Upload unpacked artifact' + uses: actions/upload-artifact@v3 + with: + name: AmneziaVPN_Linux_unpacked + path: deploy/AppDir + retention-days: 7 # ------------------------------------------------------ @@ -97,12 +104,18 @@ jobs: set QIF_BIN_DIR="${{ runner.temp }}\\Qt\\Tools\\QtInstallerFramework\\${{ env.QIF_VERSION }}\\bin" call deploy\\build_windows.bat - - name: 'Upload artifact' + - name: 'Upload installer artifact' uses: actions/upload-artifact@v3 with: - name: AmneziaVPN_Windows + name: AmneziaVPN_Windows_installer path: AmneziaVPN_x${{ env.BUILD_ARCH }}.exe - retention-days: 3 + retention-days: 7 + - name: 'Upload unpacked artifact' + uses: actions/upload-artifact@v3 + with: + name: AmneziaVPN_Windows_unpacked + path: deploy\\build_${{ env.BUILD_ARCH }}\\client\\Release + retention-days: 7 # ------------------------------------------------------ @@ -225,12 +238,18 @@ jobs: export QIF_BIN_DIR="${{ runner.temp }}/Qt/Tools/QtInstallerFramework/${{ env.QIF_VERSION }}/bin" bash deploy/build_macos.sh - - name: 'Upload artifact' + - name: 'Upload installer artifact' uses: actions/upload-artifact@v3 with: - name: AmneziaVPN_MacOS + name: AmneziaVPN_MacOS_installer path: AmneziaVPN.dmg - retention-days: 3 + retention-days: 7 + - name: 'Upload unpacked artifact' + uses: actions/upload-artifact@v3 + with: + name: AmneziaVPN_MacOS_unpacked + path: deploy/build/client/AmneziaVPN.app + retention-days: 7 # ------------------------------------------------------ diff --git a/CMakeLists.txt b/CMakeLists.txt index fa841819..e8dbd7c7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.23.0 FATAL_ERROR) +cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR) set(PROJECT AmneziaVPN) project(${PROJECT}) diff --git a/client/3rd/QtSsh/src/botan/botan.cmake b/client/3rd/QtSsh/src/botan/botan.cmake index 24a35d1c..983b76db 100644 --- a/client/3rd/QtSsh/src/botan/botan.cmake +++ b/client/3rd/QtSsh/src/botan/botan.cmake @@ -27,7 +27,7 @@ if(APPLE AND NOT IOS) set(SOURCES ${SOURCES} ${CMAKE_CURRENT_LIST_DIR}/macos/botan_all.cpp) endif() -if(LINUX) +if(LINUX AND NOT ANDROID) include_directories(${CMAKE_CURRENT_LIST_DIR}/linux) set(HEADERS ${HEADERS} ${CMAKE_CURRENT_LIST_DIR}/linux/botan_all.h) set(SOURCES ${SOURCES} ${CMAKE_CURRENT_LIST_DIR}/linux/botan_all.cpp) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index e1d0e7d5..993314ac 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.23.0 FATAL_ERROR) +cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR) set(PROJECT AmneziaVPN) project(${PROJECT} VERSION 2.1.2) @@ -10,6 +10,10 @@ if(${CMAKE_VERSION} VERSION_GREATER_EQUAL 3.17) cmake_policy(SET CMP0099 OLD) endif() +if(CMAKE_XCODE_BUILD_SYSTEM VERSION_GREATER_EQUAL 12) + cmake_policy(SET CMP0114 NEW) +endif() + set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) set(CMAKE_AUTOUIC ON) @@ -188,6 +192,7 @@ if(APPLE) set(CMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM ${BUILD_VPN_DEVELOPMENT_TEAM}) set(CMAKE_XCODE_ATTRIBUTE_GROUP_ID_IOS ${BUILD_IOS_GROUP_IDENTIFIER}) + #set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../../build) if(NOT IOS) set(CMAKE_OSX_ARCHITECTURES "x86_64" CACHE INTERNAL "" FORCE) @@ -353,6 +358,8 @@ if(IOS) enable_language(OBJC) enable_language(OBJCXX) enable_language(Swift) + + #disbale in cicd include(cmake/osxtools.cmake) # set(CMAKE_XCODE_GENERATE_TOP_LEVEL_PROJECT_ONLY TRUE) @@ -364,12 +371,21 @@ if(IOS) set(CMAKE_XCODE_ATTRIBUTE_FRAMEWORK_SEARCH_PATHS ${CMAKE_CURRENT_LIST_DIR}/3rd/OpenVPNAdapter/build/Release-iphoneos) + #need to change for debug and relase set_target_properties(${PROJECT} PROPERTIES XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "org.amnezia.${PROJECT}" XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1" XCODE_ATTRIBUTE_DEVELOPMENT_TEAM "X7UJ388FXK" - XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Development" + XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution" ) + + set_target_properties(${PROJECT} + PROPERTIES XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "org.amnezia.${PROJECT}" + XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1" + XCODE_ATTRIBUTE_DEVELOPMENT_TEAM "X7UJ388FXK" + XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] "Apple Development" + ) + set(LIBS ${LIBS} ${CMAKE_CURRENT_LIST_DIR}/3rd/OpenSSL/lib/ios/iphone/libcrypto.a ${CMAKE_CURRENT_LIST_DIR}/3rd/OpenSSL/lib/ios/iphone/libssl.a @@ -463,6 +479,36 @@ set_source_files_properties( ) set_target_properties(${PROJECT} PROPERTIES XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY ON) set_target_properties(${PROJECT} PROPERTIES XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION) + + + set_target_properties("networkextension" + PROPERTIES XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "org.amnezia.${PROJECT}.network-extension" + XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1" + XCODE_ATTRIBUTE_DEVELOPMENT_TEAM "X7UJ388FXK" + XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution" + ) + + set_target_properties("networkextension" + PROPERTIES XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "org.amnezia.${PROJECT}.network-extension" + XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1" + XCODE_ATTRIBUTE_DEVELOPMENT_TEAM "X7UJ388FXK" + XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] "Apple Development" + ) + + set_target_properties (${PROJECT} PROPERTIES XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual) + + set_target_properties(${PROJECT} PROPERTIES XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "match AppStore org.amnezia.AmneziaVPN") + + set_target_properties(${PROJECT} PROPERTIES XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "match Development org.amnezia.AmneziaVPN") + + + set_target_properties ("networkextension" PROPERTIES XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual) + + set_target_properties("networkextension" PROPERTIES XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "match AppStore org.amnezia.AmneziaVPN.network-extension") + + set_target_properties("networkextension" PROPERTIES XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "match Development org.amnezia.AmneziaVPN.network-extension") + + endif() if(ANDROID) @@ -544,11 +590,24 @@ elseif(APPLE AND NOT IOS) endif() if(NOT IOS) -add_custom_command( - TARGET ${PROJECT} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E $,copy_directory,true> - ${CMAKE_SOURCE_DIR}/deploy/data/${DEPLOY_ARTIFACT_PATH} - $ - COMMAND_EXPAND_LISTS -) + add_custom_command( + TARGET ${PROJECT} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E $,copy_directory,true> + ${CMAKE_SOURCE_DIR}/deploy/data/${DEPLOY_ARTIFACT_PATH} + $ + COMMAND_EXPAND_LISTS + ) +endif() + +if(WIN32) + add_custom_command( + TARGET ${PROJECT} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E $,copy,true> + $/../service/wireguard-service/wireguard-service.exe + $/wireguard/wireguard-service.exe + COMMAND_EXPAND_LISTS + ) +endif() +if(IOS) + #include(cmake/ios-arch-fixup.cmake) endif() diff --git a/client/android/AndroidManifest.xml b/client/android/AndroidManifest.xml index 09a8775b..22c828fb 100644 --- a/client/android/AndroidManifest.xml +++ b/client/android/AndroidManifest.xml @@ -34,6 +34,7 @@ android:extractNativeLibs="true" android:requestLegacyExternalStorage="true" android:allowNativeHeapPointerTagging="false" + android:theme="@style/Theme.AppCompat.NoActionBar" android:icon="@drawable/icon"> + + + + + + + \ No newline at end of file diff --git a/client/android/settings.gradle b/client/android/settings.gradle index ba31e000..9256d846 100644 --- a/client/android/settings.gradle +++ b/client/android/settings.gradle @@ -1 +1,19 @@ +pluginManagement { + repositories { + google() + mavenCentral() + jcenter() + gradlePluginPortal() + } +} + +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + jcenter() + } +} + include ':shadowsocks' diff --git a/client/android/shadowsocks/build.gradle b/client/android/shadowsocks/build.gradle index d35daa11..d87139fe 100644 --- a/client/android/shadowsocks/build.gradle +++ b/client/android/shadowsocks/build.gradle @@ -1,21 +1,9 @@ - -allprojects { - repositories { - google() - jcenter() - mavenCentral() - } -} - - apply plugin: 'com.android.library' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-kapt' //apply plugin: 'com.novoda.bintray-release' - - android { compileSdkVersion 30 defaultConfig { diff --git a/client/android/src/org/amnezia/vpn/qt/CameraActivity.kt b/client/android/src/org/amnezia/vpn/qt/CameraActivity.kt new file mode 100644 index 00000000..8416d97c --- /dev/null +++ b/client/android/src/org/amnezia/vpn/qt/CameraActivity.kt @@ -0,0 +1,158 @@ +package org.amnezia.vpn.qt + +import android.Manifest +import android.annotation.SuppressLint +import android.app.Activity +import android.content.Intent +import android.content.pm.PackageManager +import android.os.Bundle +import android.util.Log +import android.widget.Toast +import androidx.appcompat.app.AppCompatActivity +import androidx.camera.core.* +import androidx.camera.lifecycle.ProcessCameraProvider +import androidx.camera.view.PreviewView +import androidx.core.content.ContextCompat +import com.google.mlkit.vision.barcode.BarcodeScannerOptions +import com.google.mlkit.vision.barcode.BarcodeScanning +import com.google.mlkit.vision.barcode.common.Barcode +import com.google.mlkit.vision.common.InputImage +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors +import org.amnezia.vpn.R + + +class CameraActivity : AppCompatActivity() { + + private val CAMERA_REQUEST = 100 + + private lateinit var cameraExecutor: ExecutorService + private lateinit var analyzerExecutor: ExecutorService + + private lateinit var viewFinder: PreviewView + + companion object { + private lateinit var instance: CameraActivity + + @JvmStatic fun getInstance(): CameraActivity { + return instance + } + + @JvmStatic fun stopQrCodeReader() { + CameraActivity.getInstance().finish() + } + } + + external fun passDataToDecoder(data: String) + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_camera) + + viewFinder = findViewById(R.id.viewFinder) + + cameraExecutor = Executors.newSingleThreadExecutor() + analyzerExecutor = Executors.newSingleThreadExecutor() + + instance = this + + checkPermissions() + + configureVideoPreview() + } + + private fun checkPermissions() { + if (checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { + requestPermissions(arrayOf(Manifest.permission.CAMERA), CAMERA_REQUEST) + } + } + + override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + if (requestCode == CAMERA_REQUEST) { + if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { + Toast.makeText(this, "CameraX permission granted", Toast.LENGTH_SHORT).show(); + } else { + Toast.makeText(this, "CameraX permission denied", Toast.LENGTH_SHORT).show(); + } + } + } + + @SuppressLint("UnsafeOptInUsageError") + private fun configureVideoPreview() { + val cameraProviderFuture = ProcessCameraProvider.getInstance(this) + val imageCapture = ImageCapture.Builder().build() + + cameraProviderFuture.addListener({ + val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get() + + val preview = Preview.Builder().build() + + val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA + + val imageAnalyzer = BarCodeAnalyzer() + + val analysisUseCase = ImageAnalysis.Builder() + .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) + .build() + + analysisUseCase.setAnalyzer(analyzerExecutor, imageAnalyzer) + + try { + preview.setSurfaceProvider(viewFinder.surfaceProvider) + cameraProvider.unbindAll() + cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture, analysisUseCase) + } catch(exc: Exception) { + Log.e("WUTT", "Use case binding failed", exc) + } + }, ContextCompat.getMainExecutor(this)) + } + + override fun onDestroy() { + cameraExecutor.shutdown() + analyzerExecutor.shutdown() + + super.onDestroy() + } + + val barcodesSet = mutableSetOf() + + private inner class BarCodeAnalyzer(): ImageAnalysis.Analyzer { + + private val options = BarcodeScannerOptions.Builder() + .setBarcodeFormats(Barcode.FORMAT_QR_CODE) + .build() + + private val scanner = BarcodeScanning.getClient(options) + + @SuppressLint("UnsafeOptInUsageError") + override fun analyze(imageProxy: ImageProxy) { + val mediaImage = imageProxy.image + + if (mediaImage != null) { + val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees) + + scanner.process(image) + .addOnSuccessListener { barcodes -> + if (barcodes.isNotEmpty()) { + val barcode = barcodes[0] + if (barcode != null) { + val str = barcode?.displayValue ?: "" + if (str.isNotEmpty()) { + val isAdded = barcodesSet.add(str) + if (isAdded) { + passDataToDecoder(str) + } + } + } + } + + imageProxy.close() + } + .addOnFailureListener { + imageProxy.close() + } + } + } + } +} \ No newline at end of file diff --git a/client/android/src/org/amnezia/vpn/qt/VPNActivity.kt b/client/android/src/org/amnezia/vpn/qt/VPNActivity.kt index 673c4b59..b5e8d5fb 100644 --- a/client/android/src/org/amnezia/vpn/qt/VPNActivity.kt +++ b/client/android/src/org/amnezia/vpn/qt/VPNActivity.kt @@ -36,6 +36,8 @@ class VPNActivity : org.qtproject.qt.android.bindings.QtActivity() { private val TAG = "VPNActivity" private val STORAGE_PERMISSION_CODE = 42 + private val CAMERA_ACTION_CODE = 101 + companion object { private lateinit var instance: VPNActivity @@ -47,6 +49,10 @@ class VPNActivity : org.qtproject.qt.android.bindings.QtActivity() { VPNActivity.getInstance().initServiceConnection() } + @JvmStatic fun startQrCodeReader() { + VPNActivity.getInstance().startQrCodeActivity() + } + @JvmStatic fun sendToService(actionCode: Int, body: String) { VPNActivity.getInstance().dispatchParcel(actionCode, body) } @@ -62,7 +68,12 @@ class VPNActivity : org.qtproject.qt.android.bindings.QtActivity() { super.onCreate(savedInstanceState) - instance = this; + instance = this + } + + private fun startQrCodeActivity() { + val intent = Intent(this, CameraActivity::class.java) + startActivityForResult(intent, CAMERA_ACTION_CODE) } override fun getSystemService(name: String): Any? { @@ -82,6 +93,7 @@ class VPNActivity : org.qtproject.qt.android.bindings.QtActivity() { external fun onServiceMessage(actionCode: Int, body: String?) external fun qtOnServiceConnected() external fun qtOnServiceDisconnected() + external fun onActivityMessage(actionCode: Int, body: String?) private fun dispatchParcel(actionCode: Int, body: String) { if (!isBound) { @@ -286,6 +298,7 @@ class VPNActivity : org.qtproject.qt.android.bindings.QtActivity() { private val EVENT_PERMISSION_REQURED = 6 private val EVENT_DISCONNECTED = 2 + private val UI_EVENT_QR_CODE_RECEIVED = 0 fun onPermissionRequest(code: Int, data: Parcel?) { if (code != EVENT_PERMISSION_REQURED) { @@ -310,7 +323,13 @@ class VPNActivity : org.qtproject.qt.android.bindings.QtActivity() { } return } + super.onActivityResult(requestCode, resultCode, data) + + if (requestCode == CAMERA_ACTION_CODE && resultCode == RESULT_OK) { + val extra = data?.getStringExtra("result") ?: "" + onActivityMessage(UI_EVENT_QR_CODE_RECEIVED, extra) + } } override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean { diff --git a/client/cmake/golang.cmake b/client/cmake/golang.cmake index 2e16db70..21cc2725 100644 --- a/client/cmake/golang.cmake +++ b/client/cmake/golang.cmake @@ -5,6 +5,58 @@ ## Find the absolute path to the go build tool. find_program(GOLANG_BUILD_TOOL NAMES go REQUIRED) +## Build a library file from a golang project. +function(build_go_archive OUTPUT_NAME MODULE_FILE) + cmake_parse_arguments(GOBUILD + "" + "GOOS;GOARCH" + "CGO_CFLAGS;CGO_LDFLAGS;SOURCES" + ${ARGN}) + + string(REGEX REPLACE "\\.[^/]*$" ".h" GOBUILD_HEADER_FILE ${OUTPUT_NAME}) + get_filename_component(GOBUILD_MODULE_ABS ${MODULE_FILE} ABSOLUTE) + get_filename_component(GOBUILD_MODULE_DIR ${GOBUILD_MODULE_ABS} DIRECTORY) + set(GOBUILD_ARGS -buildmode=c-archive -trimpath -v) + if(IS_DIRECTORY ${GOBUILD_MODULE_DIR}/vendor) + list(APPEND GOBUILD_ARGS -mod vendor) + endif() + + ## Collect arguments, or find their defaults. + if(NOT GOBUILD_CGO_CFLAGS) + execute_process(OUTPUT_VARIABLE GOBUILD_CGO_CFLAGS OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND ${GOLANG_BUILD_TOOL} env CGO_CFLAGS) + separate_arguments(GOBUILD_CGO_CFLAGS NATIVE_COMMAND ${GOBUILD_CGO_CFLAGS}) + endif() + if(NOT GOBUILD_CGO_LDFLAGS) + execute_process(OUTPUT_VARIABLE GOBUILD_CGO_LDFLAGS OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND ${GOLANG_BUILD_TOOL} env CGO_LDFLAGS) + separate_arguments(GOBUILD_CGO_LDFLAGS NATIVE_COMMAND ${GOBUILD_CGO_LDFLAGS}) + endif() + if(NOT GOBUILD_GOOS) + execute_process(OUTPUT_VARIABLE GOBUILD_GOOS OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND ${GOLANG_BUILD_TOOL} env GOOS) + endif() + if(NOT GOBUILD_GOARCH) + execute_process(OUTPUT_VARIABLE GOBUILD_GOARCH OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND ${GOLANG_BUILD_TOOL} env GOARCH) + endif() + + ## Use a go-cache isolated to our project + set(GOCACHE ${CMAKE_BINARY_DIR}/go-cache) + set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES ${CMAKE_BINARY_DIR}/go-cache) + + ## The command that does the building + get_filename_component(ABS_OUTPUT_NAME ${OUTPUT_NAME} ABSOLUTE) + add_custom_command( + OUTPUT ${OUTPUT_NAME} ${GOBUILD_HEADER_FILE} + DEPENDS ${MODULE_FILE} ${GOBUILD_SOURCES} + WORKING_DIRECTORY ${GOBUILD_MODULE_DIR} + COMMAND ${CMAKE_COMMAND} -E env GOCACHE=${GOCACHE} + CGO_ENABLED=1 + CGO_CFLAGS="${GOBUILD_CGO_CFLAGS}" + CGO_LDFLAGS="${GOBUILD_CGO_LDFLAGS}" + GOOS=${GOBUILD_GOOS} + GOARCH=${GOBUILD_GOARCH} + ${GOLANG_BUILD_TOOL} build ${GOBUILD_ARGS} -o ${ABS_OUTPUT_NAME} + ) +endfunction(build_go_archive) + ## Create a library target built from a golang c-archive. function(add_go_library GOTARGET SOURCE) cmake_parse_arguments(GOLANG diff --git a/client/cmake/ios-arch-fixup.cmake b/client/cmake/ios-arch-fixup.cmake new file mode 100644 index 00000000..0f349f8d --- /dev/null +++ b/client/cmake/ios-arch-fixup.cmake @@ -0,0 +1,43 @@ +if(NOT XCODE) + return() +endif() + +## Enumerate all the targets in the project +get_directory_property(IOS_SUBDIRS SUBDIRECTORIES) +get_directory_property(IOS_TARGETS BUILDSYSTEM_TARGETS) +while(IOS_SUBDIRS) + list(POP_FRONT IOS_SUBDIRS SUBDIR) + + get_directory_property(SUBDIR_TARGETS DIRECTORY ${SUBDIR} BUILDSYSTEM_TARGETS) + list(APPEND IOS_TARGETS ${SUBDIR_TARGETS}) + + get_directory_property(SUBDIR_NESTED DIRECTORY ${SUBDIR} SUBDIRECTORIES) + list(APPEND IOS_SUBDIRS ${SUBDIR_NESTED}) +endwhile() + +## The set of target types that we want to modify. +set(IOS_TARGET_COMPILED_TYPES + STATIC_LIBRARY + MODULE_LIBRARY + SHARED_LIBRARY + OBJECT_LIBRARY + EXECUTABLE +) + +## Inspect all the targets, and add extra properties if necessary. +while(IOS_TARGETS) + list(POP_FRONT IOS_TARGETS TARGET_NAME) + + get_target_property(TARGET_TYPE ${TARGET_NAME} TYPE) + list(FIND IOS_TARGET_COMPILED_TYPES ${TARGET_TYPE} IOS_TARGET_TYPE_INDEX) + if(IOS_TARGET_TYPE_INDEX LESS 0) + continue() + endif() + + ## I just want to say it's amazing this doesn't explode with syntax errors. + message("Patching architectures for ${TARGET_NAME}") + set_target_properties(${TARGET_NAME} PROPERTIES + XCODE_ATTRIBUTE_ARCHS[sdk=iphoneos*] "arm64" + XCODE_ATTRIBUTE_ARCHS[sdk=iphonesimulator*] "x86_64" + ) +endwhile() \ No newline at end of file diff --git a/client/configurators/cloak_configurator.cpp b/client/configurators/cloak_configurator.cpp index 1f1726d6..fdf9b9e1 100644 --- a/client/configurators/cloak_configurator.cpp +++ b/client/configurators/cloak_configurator.cpp @@ -40,7 +40,7 @@ QString CloakConfigurator::genCloakConfig(const ServerCredentials &credentials, config.insert("StreamTimeout", 300); // transfer params to protocol runner - config.insert(config_key::transport_proto, "$OPENVPN_TRANSPORT_PROTO"); + config.insert(config_key::transport_proto, "tcp"); config.insert(config_key::remote, credentials.hostName); config.insert(config_key::port, "$CLOAK_SERVER_PORT"); diff --git a/client/containers/containers_defs.cpp b/client/containers/containers_defs.cpp index 3313a772..cca77e7d 100644 --- a/client/containers/containers_defs.cpp +++ b/client/containers/containers_defs.cpp @@ -27,6 +27,16 @@ QString ContainerProps::containerToString(amnezia::DockerContainer c){ return "amnezia-" + containerKey.toLower(); } +QString ContainerProps::containerTypeToString(amnezia::DockerContainer c){ + if (c == DockerContainer::None) return "none"; + if (c == DockerContainer::Ipsec) return "ikev2"; + + QMetaEnum metaEnum = QMetaEnum::fromType(); + QString containerKey = metaEnum.valueToKey(static_cast(c)); + + return containerKey.toLower(); +} + QVector ContainerProps::protocolsForContainer(amnezia::DockerContainer container) { switch (container) { @@ -70,7 +80,7 @@ QList ContainerProps::allContainers() QMap ContainerProps::containerHumanNames() { return { - {DockerContainer::None, "Unknown (Old version)"}, + {DockerContainer::None, "Not installed"}, {DockerContainer::OpenVpn, "OpenVPN"}, {DockerContainer::ShadowSocks, "OpenVpn over ShadowSocks"}, {DockerContainer::Cloak, "OpenVpn over Cloak"}, @@ -171,3 +181,10 @@ return false; #endif } +QStringList ContainerProps::fixedPortsForContainer(DockerContainer c) +{ + switch (c) { + case DockerContainer::Ipsec : return QStringList{"500", "4500"}; + default: return {}; + } +} diff --git a/client/containers/containers_defs.h b/client/containers/containers_defs.h index f1891864..33e9a4ae 100644 --- a/client/containers/containers_defs.h +++ b/client/containers/containers_defs.h @@ -37,24 +37,26 @@ class ContainerProps : public QObject Q_OBJECT public: - Q_INVOKABLE static DockerContainer containerFromString(const QString &container); - Q_INVOKABLE static QString containerToString(DockerContainer container); + Q_INVOKABLE static amnezia::DockerContainer containerFromString(const QString &container); + Q_INVOKABLE static QString containerToString(amnezia::DockerContainer container); + Q_INVOKABLE static QString containerTypeToString(amnezia::DockerContainer c); - Q_INVOKABLE static QList allContainers(); + Q_INVOKABLE static QList allContainers(); - Q_INVOKABLE static QMap containerHumanNames(); - Q_INVOKABLE static QMap containerDescriptions(); + Q_INVOKABLE static QMap containerHumanNames(); + Q_INVOKABLE static QMap containerDescriptions(); // these protocols will be displayed in container settings - Q_INVOKABLE static QVector protocolsForContainer(DockerContainer container); + Q_INVOKABLE static QVector protocolsForContainer(amnezia::DockerContainer container); - Q_INVOKABLE static ServiceType containerService(DockerContainer c); + Q_INVOKABLE static amnezia::ServiceType containerService(amnezia::DockerContainer c); // binding between Docker container and main protocol of given container // it may be changed fot future containers :) - Q_INVOKABLE static Proto defaultProtocol(DockerContainer c); + Q_INVOKABLE static amnezia::Proto defaultProtocol(amnezia::DockerContainer c); - Q_INVOKABLE static bool isSupportedByCurrentPlatform(DockerContainer c); + Q_INVOKABLE static bool isSupportedByCurrentPlatform(amnezia::DockerContainer c); + Q_INVOKABLE static QStringList fixedPortsForContainer(amnezia::DockerContainer c); }; diff --git a/client/core/servercontroller.cpp b/client/core/servercontroller.cpp index e596c32f..04c7a05c 100644 --- a/client/core/servercontroller.cpp +++ b/client/core/servercontroller.cpp @@ -409,12 +409,18 @@ ErrorCode ServerController::removeContainer(const ServerCredentials &credentials genVarsForScript(credentials, container))); } -ErrorCode ServerController::setupContainer(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config) +ErrorCode ServerController::setupContainer(const ServerCredentials &credentials, DockerContainer container, + QJsonObject &config, bool isUpdate) { qDebug().noquote() << "ServerController::setupContainer" << ContainerProps::containerToString(container); //qDebug().noquote() << QJsonDocument(config).toJson(); ErrorCode e = ErrorCode::NoError; + if (!isUpdate) { + e = isServerPortBusy(credentials, container, config); + if (e) return e; + } + e = installDockerWorker(credentials, container); if (e) return e; qDebug().noquote() << "ServerController::setupContainer installDockerWorker finished"; @@ -451,7 +457,7 @@ ErrorCode ServerController::updateContainer(const ServerCredentials &credentials qDebug() << "ServerController::updateContainer for container" << container << "reinstall required is" << reinstallRequred; if (reinstallRequred) { - return setupContainer(credentials, container, newConfig); + return setupContainer(credentials, container, newConfig, true); } else { ErrorCode e = configureContainerWorker(credentials, container, newConfig); @@ -853,3 +859,35 @@ QString ServerController::replaceVars(const QString &script, const Vars &vars) //qDebug().noquote() << script; return s; } + +ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config) +{ + QString stdOut; + auto cbReadStdOut = [&](const QString &data, QSharedPointer proc) { + stdOut += data + "\n"; + }; + auto cbReadStdErr = [&](const QString &data, QSharedPointer ) { + stdOut += data + "\n"; + }; + + const QString containerString = ProtocolProps::protoToString(ContainerProps::defaultProtocol(container)); + const QJsonObject containerConfig = config.value(containerString).toObject(); + + QStringList fixedPorts = ContainerProps::fixedPortsForContainer(container); + + QString port = containerConfig.value(config_key::port).toString(); + QString transportProto = containerConfig.value(config_key::transport_proto).toString(); + + QString script = QString("sudo lsof -i -P -n | grep -E ':%1").arg(port); + for (auto &port : fixedPorts) { + script = script.append("|:%1").arg(port); + } + script = script.append("' | grep -i %1").arg(transportProto); + runScript(credentials, + replaceVars(script, genVarsForScript(credentials, container)), cbReadStdOut, cbReadStdErr); + + if (!stdOut.isEmpty()) { + return ErrorCode::ServerPortAlreadyAllocatedError; + } + return ErrorCode::NoError; +} diff --git a/client/core/servercontroller.h b/client/core/servercontroller.h index b803ee0f..f3c84117 100644 --- a/client/core/servercontroller.h +++ b/client/core/servercontroller.h @@ -35,9 +35,10 @@ public: ErrorCode removeAllContainers(const ServerCredentials &credentials); ErrorCode removeContainer(const ServerCredentials &credentials, DockerContainer container); - ErrorCode setupContainer(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config); + ErrorCode setupContainer(const ServerCredentials &credentials, DockerContainer container, + QJsonObject &config, bool isUpdate = false); ErrorCode updateContainer(const ServerCredentials &credentials, DockerContainer container, - const QJsonObject &oldConfig, QJsonObject &newConfig); + const QJsonObject &oldConfig, QJsonObject &newConfig); // create initial config - generate passwords, etc QJsonObject createContainerInitialConfig(DockerContainer container, int port, TransportProto tp); @@ -82,6 +83,7 @@ private: ErrorCode runContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config); ErrorCode configureContainerWorker(const ServerCredentials &credentials, DockerContainer container, QJsonObject &config); ErrorCode startupContainerWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config = QJsonObject()); + ErrorCode isServerPortBusy(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config); std::shared_ptr m_settings; std::shared_ptr m_configurator; diff --git a/client/ios/networkextension/CMakeLists.txt b/client/ios/networkextension/CMakeLists.txt index ec59cad4..bbe591c8 100644 --- a/client/ios/networkextension/CMakeLists.txt +++ b/client/ios/networkextension/CMakeLists.txt @@ -79,29 +79,36 @@ target_sources(networkextension PRIVATE target_include_directories(networkextension PRIVATE ${CLIENT_ROOT_DIR}) target_include_directories(networkextension PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) -## HACK: Build only the first architecture, this will break universal builds -## for now, but they are already broken for mobile, which uses the arch to -## determine iOS vs. simulator builds :) -if(NOT CMAKE_OSX_ARCHITECTURES) - set(OSXARCH arm64) -else() - list(GET CMAKE_OSX_ARCHITECTURES 0 OSXARCH) -endif() - -## Build the wireguard go library -## TODO: The upstream makefile also makes an attempt to patch the golang runtime -## to provide the boot-time clock instead of an uptime clock. We should probably -## make an attempt to do the same, somehow? include(${CLIENT_ROOT_DIR}/cmake/golang.cmake) -if(OSXARCH STREQUAL "x86_64") - set(GOARCH amd64) -else() - set(GOARCH ${FIRST_OSX_ARCHITECTURE}) -endif() -add_go_library(libwg-go ${CLIENT_ROOT_DIR}/3rd/wireguard-apple/Sources/WireGuardKitGo/api-apple.go +## Build the wireguard go library for iOS simulation. +## TODO: Some special handling around GOARCH for +execute_process(OUTPUT_VARIABLE SIM_SDKROOT OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND xcrun --sdk iphonesimulator --show-sdk-path) +build_go_archive(${CMAKE_CURRENT_BINARY_DIR}/libwg-sim.a ${CMAKE_SOURCE_DIR}/3rd/wireguard-apple/Sources/WireGuardKitGo/go.mod GOOS ios - GOARCH ${GOARCH} - CGO_CFLAGS -arch ${OSXARCH} - CGO_LDFLAGS -arch ${OSXARCH} + GOARCH amd64 + CGO_CFLAGS -arch x86_64 -isysroot ${SIM_SDKROOT} + CGO_LDFLAGS -arch x86_64 -isysroot ${SIM_SDKROOT} ) -target_link_libraries(networkextension PRIVATE libwg-go) \ No newline at end of file + +## Build the wireguard go library for iOS devices. +execute_process(OUTPUT_VARIABLE IOS_SDKROOT OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND xcrun --sdk ${CMAKE_OSX_SYSROOT} --show-sdk-path) +build_go_archive(${CMAKE_CURRENT_BINARY_DIR}/libwg-dev.a ${CMAKE_SOURCE_DIR}/3rd/wireguard-apple/Sources/WireGuardKitGo/go.mod + GOOS ios + GOARCH arm64 + CGO_CFLAGS -arch arm64 -isysroot ${IOS_SDKROOT} + CGO_LDFLAGS -arch arm64 -isysroot ${IOS_SDKROOT} +) + +## Unify the wireguard go libraries. +add_custom_target(libwg_builder + DEPENDS + ${CMAKE_CURRENT_BINARY_DIR}/libwg-dev.a + ${CMAKE_CURRENT_BINARY_DIR}/libwg-sim.a + BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/libwg-unified.a + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND lipo -create -output libwg-unified.a libwg-dev.a libwg-sim.a +) + +## Link and depend on the wireguard library. +add_dependencies(networkextension libwg_builder) +target_link_libraries(networkextension PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/libwg-unified.a) \ No newline at end of file diff --git a/client/platforms/android/android_controller.cpp b/client/platforms/android/android_controller.cpp index b25fce78..a86ab7ca 100644 --- a/client/platforms/android/android_controller.cpp +++ b/client/platforms/android/android_controller.cpp @@ -15,7 +15,6 @@ #include "private/qandroidextras_p.h" #include "ui/pages_logic/StartPageLogic.h" -#include "androidvpnactivity.h" #include "androidutils.h" namespace { @@ -262,6 +261,11 @@ void AndroidController::setVpnConfig(const QJsonObject &newVpnConfig) m_vpnConfig = newVpnConfig; } +void AndroidController::startQrReaderActivity() +{ + AndroidVPNActivity::instance()->startQrCodeReader(); +} + void AndroidController::scheduleStatusCheckSlot() { QTimer::singleShot(1000, [this]() { diff --git a/client/platforms/android/android_controller.h b/client/platforms/android/android_controller.h index ddaa0d79..59ecd9b3 100644 --- a/client/platforms/android/android_controller.h +++ b/client/platforms/android/android_controller.h @@ -11,6 +11,7 @@ #include "ui/pages_logic/StartPageLogic.h" #include "protocols/vpnprotocol.h" +#include "androidvpnactivity.h" using namespace amnezia; @@ -42,6 +43,8 @@ public: const QJsonObject &vpnConfig() const; void setVpnConfig(const QJsonObject &newVpnConfig); + void startQrReaderActivity(); + signals: void connectionStateChanged(VpnProtocol::VpnConnectionState state); @@ -50,8 +53,7 @@ signals: // to true and the "connectionDate" should be set to the activation date if // known. // If "status" is set to false, the backend service is considered unavailable. - void initialized(bool status, bool connected, - const QDateTime& connectionDate); + void initialized(bool status, bool connected, const QDateTime& connectionDate); void statusUpdated(QString totalRx, QString totalTx, QString endpoint, QString deviceIPv4); void scheduleStatusCheckSignal(); @@ -59,9 +61,6 @@ signals: protected slots: void scheduleStatusCheckSlot(); -protected: - - private: bool m_init = false; diff --git a/client/platforms/android/androidvpnactivity.cpp b/client/platforms/android/androidvpnactivity.cpp index 0e8b9574..17639023 100644 --- a/client/platforms/android/androidvpnactivity.cpp +++ b/client/platforms/android/androidvpnactivity.cpp @@ -22,12 +22,10 @@ AndroidVPNActivity::AndroidVPNActivity() { AndroidUtils::runOnAndroidThreadAsync([]() { JNINativeMethod methods[]{ {"handleBackButton", "()Z", reinterpret_cast(handleBackButton)}, - {"onServiceMessage", "(ILjava/lang/String;)V", - reinterpret_cast(onServiceMessage)}, - {"qtOnServiceConnected", "()V", - reinterpret_cast(onServiceConnected)}, - {"qtOnServiceDisconnected", "()V", - reinterpret_cast(onServiceDisconnected)}, + {"onServiceMessage", "(ILjava/lang/String;)V", reinterpret_cast(onServiceMessage)}, + {"qtOnServiceConnected", "()V", reinterpret_cast(onServiceConnected)}, + {"qtOnServiceDisconnected", "()V", reinterpret_cast(onServiceDisconnected)}, + {"onActivityMessage", "(ILjava/lang/String;)V", reinterpret_cast(onAndroidVpnActivityMessage)} }; QJniObject javaClass(CLASSNAME); @@ -54,6 +52,11 @@ void AndroidVPNActivity::connectService() { QJniObject::callStaticMethod(CLASSNAME, "connectService", "()V"); } +void AndroidVPNActivity::startQrCodeReader() +{ + QJniObject::callStaticMethod(CLASSNAME, "startQrCodeReader", "()V"); +} + // static AndroidVPNActivity* AndroidVPNActivity::instance() { if (s_instance == nullptr) { @@ -121,6 +124,19 @@ void AndroidVPNActivity::handleServiceMessage(int code, const QString& data) { } } +void AndroidVPNActivity::handleActivityMessage(int code, const QString &data) +{ + auto mode = (UIEvents)code; + + switch (mode) { + case UIEvents::QR_CODED_DECODED: + emit eventQrCodeReceived(data); + break; + default: + Q_ASSERT(false); + } +} + void AndroidVPNActivity::onServiceConnected(JNIEnv* env, jobject thiz) { Q_UNUSED(env); Q_UNUSED(thiz); @@ -134,3 +150,19 @@ void AndroidVPNActivity::onServiceDisconnected(JNIEnv* env, jobject thiz) { emit AndroidVPNActivity::instance()->serviceDisconnected(); } + +void AndroidVPNActivity::onAndroidVpnActivityMessage(JNIEnv *env, jobject thiz, jint messageType, jstring message) +{ + Q_UNUSED(thiz); + const char* buffer = env->GetStringUTFChars(message, nullptr); + if (!buffer) { + return; + } + + QString parcelBody(buffer); + env->ReleaseStringUTFChars(message, buffer); + + AndroidUtils::dispatchToMainThread([messageType, parcelBody] { + AndroidVPNActivity::instance()->handleActivityMessage(messageType, parcelBody); + }); +} diff --git a/client/platforms/android/androidvpnactivity.h b/client/platforms/android/androidvpnactivity.h index 49d1aae5..1bc1a522 100644 --- a/client/platforms/android/androidvpnactivity.h +++ b/client/platforms/android/androidvpnactivity.h @@ -59,6 +59,11 @@ enum ServiceEvents { }; typedef enum ServiceEvents ServiceEvents; +enum UIEvents { + QR_CODED_DECODED = 0, +}; +typedef enum UIEvents UIEvents; + class AndroidVPNActivity : public QObject { Q_OBJECT @@ -69,6 +74,7 @@ public: static bool handleBackButton(JNIEnv* env, jobject thiz); static void sendToService(ServiceAction type, const QString& data); static void connectService(); + static void startQrCodeReader(); signals: void serviceConnected(); @@ -80,6 +86,7 @@ signals: void eventBackendLogs(const QString& data); void eventActivationError(const QString& data); void eventConfigImport(const QString& data); + void eventQrCodeReceived(const QString& data); private: AndroidVPNActivity(); @@ -87,7 +94,9 @@ private: static void onServiceMessage(JNIEnv* env, jobject thiz, jint messageType, jstring body); static void onServiceConnected(JNIEnv* env, jobject thiz); static void onServiceDisconnected(JNIEnv* env, jobject thiz); + static void onAndroidVpnActivityMessage(JNIEnv* env, jobject thiz, jint messageType, jstring message); void handleServiceMessage(int code, const QString& data); + void handleActivityMessage(int code, const QString& data); }; #endif // ANDROIDVPNACTIVITY_H diff --git a/client/protocols/openvpnovercloakprotocol.cpp b/client/protocols/openvpnovercloakprotocol.cpp index 5ba2c587..55939895 100644 --- a/client/protocols/openvpnovercloakprotocol.cpp +++ b/client/protocols/openvpnovercloakprotocol.cpp @@ -109,8 +109,6 @@ QString OpenVpnOverCloakProtocol::cloakExecPath() { #ifdef Q_OS_WIN return Utils::executable(QString("cloak/ck-client"), true); -#elif defined Q_OS_LINUX - return Utils::usrExecutable("ck-client"); #else return Utils::executable(QString("/ck-client"), true); #endif diff --git a/client/protocols/shadowsocksvpnprotocol.cpp b/client/protocols/shadowsocksvpnprotocol.cpp index 7e55b6f3..82ae08b8 100644 --- a/client/protocols/shadowsocksvpnprotocol.cpp +++ b/client/protocols/shadowsocksvpnprotocol.cpp @@ -109,8 +109,6 @@ QString ShadowSocksVpnProtocol::shadowSocksExecPath() { #ifdef Q_OS_WIN return Utils::executable(QString("ss/ss-local"), true); -#elif defined Q_OS_LINUX - return Utils::usrExecutable(QString("ss-local")); #else return Utils::executable(QString("/ss-local"), true); #endif @@ -118,5 +116,17 @@ QString ShadowSocksVpnProtocol::shadowSocksExecPath() void ShadowSocksVpnProtocol::readShadowSocksConfiguration(const QJsonObject &configuration) { - m_shadowSocksConfig = configuration.value(ProtocolProps::key_proto_config_data(Proto::ShadowSocks)).toObject(); + QJsonObject shadowSocksConfig = configuration.value(ProtocolProps::key_proto_config_data(Proto::ShadowSocks)).toObject(); + bool isLocalPortConvertOk = false; + bool isServerPortConvertOk = false; + int localPort = shadowSocksConfig.value("local_port").toString().toInt(&isLocalPortConvertOk); + int serverPort = shadowSocksConfig.value("server_port").toString().toInt(&isServerPortConvertOk); + if (!isLocalPortConvertOk) { + qDebug() << "Error when converting local_port field in ShadowSocks config"; + } else if (!isServerPortConvertOk) { + qDebug() << "Error when converting server_port field in ShadowSocks config"; + } + shadowSocksConfig["local_port"] = localPort; + shadowSocksConfig["server_port"] = serverPort; + m_shadowSocksConfig = shadowSocksConfig; } diff --git a/client/resources.qrc b/client/resources.qrc index 105c06b8..fd26f5ad 100644 --- a/client/resources.qrc +++ b/client/resources.qrc @@ -81,7 +81,6 @@ ui/qml/Pages/PageSites.qml ui/qml/Pages/PageStart.qml ui/qml/Pages/PageVPN.qml - ui/qml/Pages/PageQrDecoder.qml ui/qml/Pages/PageAbout.qml ui/qml/Pages/PageQrDecoderIos.qml ui/qml/Pages/PageViewConfig.qml @@ -163,5 +162,6 @@ images/svg/control_point_black_24dp.svg images/svg/settings_suggest_black_24dp.svg server_scripts/website_tor/Dockerfile + ui/qml/Controls/PopupWithQuestion.qml diff --git a/client/server_scripts/openvpn_cloak/Dockerfile b/client/server_scripts/openvpn_cloak/Dockerfile index b3a6810d..3cb14e82 100644 --- a/client/server_scripts/openvpn_cloak/Dockerfile +++ b/client/server_scripts/openvpn_cloak/Dockerfile @@ -1,5 +1,4 @@ FROM alpine:3.15 - LABEL maintainer="AmneziaVPN" ARG SS_RELEASE="v1.13.1" diff --git a/client/server_scripts/openvpn_cloak/configure_container.sh b/client/server_scripts/openvpn_cloak/configure_container.sh index 26b514a4..5e2deb45 100644 --- a/client/server_scripts/openvpn_cloak/configure_container.sh +++ b/client/server_scripts/openvpn_cloak/configure_container.sh @@ -1,6 +1,6 @@ cat > /opt/amnezia/openvpn/server.conf < /opt/amnezia/cloak/ck-config.json < +#include +#include "../../platforms/android/androidutils.h" +#endif + using namespace amnezia; using namespace PageEnumNS; +namespace { + QrDecoderLogic* mInstance = nullptr; + constexpr auto CLASSNAME = "org.amnezia.vpn.qt.CameraActivity"; +} + QrDecoderLogic::QrDecoderLogic(UiLogic *logic, QObject *parent): PageLogicBase(logic, parent) { + mInstance = this; + #if (defined(Q_OS_ANDROID)) + AndroidUtils::runOnAndroidThreadAsync([]() { + JNINativeMethod methods[]{ + {"passDataToDecoder", "(Ljava/lang/String;)V", reinterpret_cast(onNewDataChunk)}, + }; + + QJniObject javaClass(CLASSNAME); + QJniEnvironment env; + jclass objectClass = env->GetObjectClass(javaClass.object()); + env->RegisterNatives(objectClass, methods, sizeof(methods) / sizeof(methods[0])); + env->DeleteLocalRef(objectClass); + }); + #endif } +void QrDecoderLogic::stopDecodingQr() +{ + #if (defined(Q_OS_ANDROID)) + QJniObject::callStaticMethod(CLASSNAME, "stopQrCodeReader", "()V"); + #endif + + emit stopDecode(); +} + +#ifdef Q_OS_ANDROID +void QrDecoderLogic::onNewDataChunk(JNIEnv *env, jobject thiz, jstring data) +{ + Q_UNUSED(thiz); + const char* buffer = env->GetStringUTFChars(data, nullptr); + if (!buffer) { + return; + } + + QString parcelBody(buffer); + env->ReleaseStringUTFChars(data, buffer); + + if (mInstance != nullptr) { + if (!mInstance->m_detectingEnabled) { + mInstance->onUpdatePage(); + } + mInstance->onDetectedQrCode(parcelBody); + } +} +#endif + void QrDecoderLogic::onUpdatePage() { m_chunks.clear(); @@ -24,7 +79,6 @@ void QrDecoderLogic::onUpdatePage() void QrDecoderLogic::onDetectedQrCode(const QString &code) { //qDebug() << code; - if (!detectingEnabled()) return; // check if chunk received @@ -32,12 +86,12 @@ void QrDecoderLogic::onDetectedQrCode(const QString &code) QDataStream s(&ba, QIODevice::ReadOnly); qint16 magic; s >> magic; - if (magic == amnezia::qrMagicCode) { quint8 chunksCount; s >> chunksCount; if (totalChunksCount() != chunksCount) { m_chunks.clear(); } + set_totalChunksCount(chunksCount); quint8 chunkId; s >> chunkId; @@ -46,6 +100,7 @@ void QrDecoderLogic::onDetectedQrCode(const QString &code) if (m_chunks.size() == totalChunksCount()) { QByteArray data; + for (int i = 0; i < totalChunksCount(); ++i) { data.append(m_chunks.value(i)); } @@ -53,21 +108,18 @@ void QrDecoderLogic::onDetectedQrCode(const QString &code) bool ok = uiLogic()->pageLogic()->importConnectionFromQr(data); if (ok) { set_detectingEnabled(false); - emit stopDecode(); - } - else { + stopDecodingQr(); + } else { m_chunks.clear(); set_totalChunksCount(0); set_receivedChunksCount(0); } } - } - else { + } else { bool ok = uiLogic()->pageLogic()->importConnectionFromQr(ba); if (ok) { set_detectingEnabled(false); - emit stopDecode(); + stopDecodingQr(); } } } - diff --git a/client/ui/pages_logic/QrDecoderLogic.h b/client/ui/pages_logic/QrDecoderLogic.h index 243da95b..2b24bf27 100644 --- a/client/ui/pages_logic/QrDecoderLogic.h +++ b/client/ui/pages_logic/QrDecoderLogic.h @@ -3,6 +3,10 @@ #include "PageLogicBase.h" +#ifdef Q_OS_ANDROID +#include "jni.h" +#endif + class UiLogic; class QrDecoderLogic : public PageLogicBase @@ -16,10 +20,17 @@ public: Q_INVOKABLE void onUpdatePage() override; Q_INVOKABLE void onDetectedQrCode(const QString &code); +#ifdef Q_OS_ANDROID + static void onNewDataChunk(JNIEnv *env, jobject thiz, jstring data); +#endif + public: explicit QrDecoderLogic(UiLogic *uiLogic, QObject *parent = nullptr); ~QrDecoderLogic() = default; +private: + void stopDecodingQr(); + signals: void startDecode(); void stopDecode(); diff --git a/client/ui/pages_logic/StartPageLogic.cpp b/client/ui/pages_logic/StartPageLogic.cpp index 3c15199d..06febc05 100644 --- a/client/ui/pages_logic/StartPageLogic.cpp +++ b/client/ui/pages_logic/StartPageLogic.cpp @@ -13,6 +13,7 @@ #ifdef Q_OS_ANDROID #include #include "../../platforms/android/androidutils.h" +#include "../../platforms/android/android_controller.h" #endif namespace { @@ -184,6 +185,13 @@ void StartPageLogic::onPushButtonImportOpenFile() } } +#ifdef Q_OS_ANDROID +void StartPageLogic::startQrDecoder() +{ + AndroidController::instance()->startQrReaderActivity(); +} +#endif + bool StartPageLogic::importConnection(const QJsonObject &profile) { ServerCredentials credentials; diff --git a/client/ui/pages_logic/StartPageLogic.h b/client/ui/pages_logic/StartPageLogic.h index 183d0bd3..b3dea002 100644 --- a/client/ui/pages_logic/StartPageLogic.h +++ b/client/ui/pages_logic/StartPageLogic.h @@ -31,6 +31,10 @@ public: Q_INVOKABLE void onPushButtonImport(); Q_INVOKABLE void onPushButtonImportOpenFile(); +#ifdef Q_OS_ANDROID + Q_INVOKABLE void startQrDecoder(); +#endif + bool importConnection(const QJsonObject &profile); bool importConnectionFromCode(QString code); bool importConnectionFromQr(const QByteArray &data); diff --git a/client/ui/pages_logic/protocols/OpenVpnLogic.cpp b/client/ui/pages_logic/protocols/OpenVpnLogic.cpp index d069bf87..7ed71962 100644 --- a/client/ui/pages_logic/protocols/OpenVpnLogic.cpp +++ b/client/ui/pages_logic/protocols/OpenVpnLogic.cpp @@ -51,9 +51,15 @@ void OpenVpnLogic::updateProtocolPage(const QJsonObject &openvpnConfig, DockerCo set_lineEditSubnetText(openvpnConfig.value(config_key::subnet_address). toString(protocols::openvpn::defaultSubnetAddress)); - QString trasnsport = openvpnConfig.value(config_key::transport_proto). - toString(protocols::openvpn::defaultTransportProto); - + QString trasnsport; + if (container == DockerContainer::ShadowSocks || container == DockerContainer::Cloak) { + trasnsport = "tcp"; + set_radioButtonUdpEnabled(false); + set_radioButtonTcpEnabled(false); + } else { + trasnsport = openvpnConfig.value(config_key::transport_proto). + toString(protocols::openvpn::defaultTransportProto); + } set_radioButtonUdpChecked(trasnsport == protocols::openvpn::defaultTransportProto); set_radioButtonTcpChecked(trasnsport != protocols::openvpn::defaultTransportProto); @@ -80,12 +86,6 @@ void OpenVpnLogic::updateProtocolPage(const QJsonObject &openvpnConfig, DockerCo toString(protocols::openvpn::defaultAdditionalServerConfig); set_textAreaAdditionalServerConfig(additionalServerConfig); - if (container == DockerContainer::ShadowSocks) { - set_radioButtonUdpEnabled(false); - set_radioButtonTcpEnabled(false); - set_radioButtonTcpChecked(true); - } - set_lineEditPortText(openvpnConfig.value(config_key::port). toString(protocols::openvpn::defaultPort)); diff --git a/client/ui/qml/Controls/PopupWithQuestion.qml b/client/ui/qml/Controls/PopupWithQuestion.qml new file mode 100644 index 00000000..4eafd359 --- /dev/null +++ b/client/ui/qml/Controls/PopupWithQuestion.qml @@ -0,0 +1,57 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +Popup { + id: root + + property string questionText + property string yesText: "yes" + property string noText: "no" + property var yesFunc + property var noFunc + + anchors.centerIn: Overlay.overlay + modal: true + closePolicy: Popup.NoAutoClose + + width: parent.width - 20 + + ColumnLayout { + width: parent.width + Text { + horizontalAlignment: Text.AlignHCenter + Layout.fillWidth: true + wrapMode: Text.WordWrap + font.pixelSize: 16 + text: questionText + } + + RowLayout { + BlueButtonType { + id: yesButton + Layout.preferredWidth: parent.width / 2 + Layout.fillWidth: true + text: yesText + onClicked: { + root.enabled = false + if (yesFunc && typeof yesFunc === "function") { + yesFunc() + } + root.enabled = true + } + } + BlueButtonType { + id: noButton + Layout.preferredWidth: parent.width / 2 + Layout.fillWidth: true + text: noText + onClicked: { + if (noFunc && typeof noFunc === "function") { + noFunc() + } + } + } + } + } +} diff --git a/client/ui/qml/Pages/PageNewServerProtocols.qml b/client/ui/qml/Pages/PageNewServerProtocols.qml index aa7c3c23..0ce2090f 100644 --- a/client/ui/qml/Pages/PageNewServerProtocols.qml +++ b/client/ui/qml/Pages/PageNewServerProtocols.qml @@ -63,7 +63,7 @@ PageBase { pageLoader.focus = true } - onContainerSelected: { + onContainerSelected: function(c_index){ var containerProto = ContainerProps.defaultProtocol(c_index) tf_port_num.text = ProtocolProps.defaultPort(containerProto) diff --git a/client/ui/qml/Pages/PageQrDecoder.qml b/client/ui/qml/Pages/PageQrDecoder.qml deleted file mode 100644 index 9a4b26f5..00000000 --- a/client/ui/qml/Pages/PageQrDecoder.qml +++ /dev/null @@ -1,164 +0,0 @@ -import QtQuick -import QtQuick.Controls -import QtMultimedia -import PageEnum 1.0 -import QZXing 3.2 - -import "./" -import "../Controls" -import "../Config" - -PageBase { - id: root - page: PageEnum.QrDecoder - logic: QrDecoderLogic - - onDeactivated: { - console.debug("Stopping QR decoder") - loader.sourceComponent = undefined - } - - BackButton { - } - Caption { - id: caption - text: qsTr("Import configuration") - } - - Connections { - target: Qt.platform.os != "ios" ? QrDecoderLogic : null - function onStartDecode() { - console.debug("Starting QR decoder") - loader.sourceComponent = component - } - function onStopDecode() { - console.debug("Stopping QR decoder") - loader.sourceComponent = undefined - } - } - - Loader { - id: loader - - anchors.top: caption.bottom - anchors.bottom: parent.bottom - anchors.left: parent.left - anchors.right: parent.right - } - - Component { - id: component - - Item { - anchors.fill: parent - - Camera - { - id:camera - focus { - focusMode: CameraFocus.FocusContinuous - focusPointMode: CameraFocus.FocusPointAuto - } - } - - VideoOutput - { - id: videoOutput - source: camera - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.left: parent.left - anchors.right: parent.right - autoOrientation: true - fillMode: VideoOutput.PreserveAspectFit -// filters: [ zxingFilter ] - - - Rectangle { - color: "black" - opacity: 0.5 - width: videoOutput.contentRect.width * 0.15 - height: videoOutput.contentRect.height - x: (videoOutput.width - videoOutput.contentRect.width)/2 - anchors.verticalCenter: videoOutput.verticalCenter - } - - Rectangle { - color: "black" - opacity: 0.5 - width: videoOutput.contentRect.width * 0.15 - height: videoOutput.contentRect.height - x: videoOutput.width/2 + videoOutput.contentRect.width/2 - videoOutput.contentRect.width * 0.15 - anchors.verticalCenter: videoOutput.verticalCenter - } - - Rectangle { - color: "black" - opacity: 0.5 - width: videoOutput.contentRect.width * 0.7 - height: videoOutput.contentRect.height * 0.15 - x: (videoOutput.width - videoOutput.contentRect.width)/2 + videoOutput.contentRect.width * 0.15 - y: (videoOutput.height - videoOutput.contentRect.height)/2 - } - - Rectangle { - color: "black" - opacity: 0.5 - width: videoOutput.contentRect.width * 0.7 - height: videoOutput.contentRect.height * 0.15 - x: (videoOutput.width - videoOutput.contentRect.width)/2 + videoOutput.contentRect.width * 0.15 - y: videoOutput.height/2 + videoOutput.contentRect.height/2 - videoOutput.contentRect.height * 0.15 - } - - LabelType { - width: parent.width - text: qsTr("Decoded QR chunks " + QrDecoderLogic.receivedChunksCount + "/" + QrDecoderLogic.totalChunksCount) - horizontalAlignment: Text.AlignLeft - visible: QrDecoderLogic.totalChunksCount > 0 - anchors.horizontalCenter: videoOutput.horizontalCenter - y: videoOutput.height/2 + videoOutput.contentRect.height/2 - } - } - - QZXingFilter - { - id: zxingFilter - orientation: videoOutput.orientation - captureRect: { - // setup bindings - videoOutput.contentRect; - videoOutput.sourceRect; - return videoOutput.mapRectToSource(videoOutput.mapNormalizedRectToItem(Qt.rect( - 0.15, 0.15, 0.7, 0.7 //0, 0, 1.0, 1.0 - ))); - } - - decoder { - enabledDecoders: QZXing.DecoderFormat_QR_CODE - - onTagFound: { - QrDecoderLogic.onDetectedQrCode(tag) - } - - tryHarder: true - } - - property int framesDecoded: 0 - property real timePerFrameDecode: 0 - - onDecodingFinished: - { - timePerFrameDecode = (decodeTime + framesDecoded * timePerFrameDecode) / (framesDecoded + 1); - framesDecoded++; - if(succeeded) - console.log("frame finished: " + succeeded, decodeTime, timePerFrameDecode, framesDecoded); - } - } - - - } - - } - - -} diff --git a/client/ui/qml/Pages/PageServerContainers.qml b/client/ui/qml/Pages/PageServerContainers.qml index 4b8383e6..ddd607b0 100644 --- a/client/ui/qml/Pages/PageServerContainers.qml +++ b/client/ui/qml/Pages/PageServerContainers.qml @@ -55,7 +55,6 @@ PageBase { tf_port_num.text = qsTr("Default") } else tf_port_num.text = ProtocolProps.defaultPort(containerProto) - cb_port_proto.currentIndex = ProtocolProps.defaultTransportProto(containerProto) tf_port_num.enabled = ProtocolProps.defaultPortChangeable(containerProto) @@ -297,23 +296,24 @@ PageBase { implicitHeight: 30 checked: default_role - - MessageDialog { - id: dialogRemove - buttons: StandardButton.Yes | StandardButton.Cancel - title: "AmneziaVPN" - text: qsTr("Remove container") + " " + name_role + "?" + "\n" + qsTr("This action will erase all data of this container on the server.") - onAccepted: { - tb_c.currentIndex = -1 - ServerContainersLogic.onPushButtonRemoveClicked(proxyContainersModel.mapToSource(index)) - } - } - - onClicked: dialogRemove.open() + onClicked: popupRemove.open() VisibleBehavior on visible { } } + PopupWithQuestion { + id: popupRemove + questionText: qsTr("Remove container") + " " + name_role + "?" + "\n" + qsTr("This action will erase all data of this container on the server.") + yesFunc: function() { + tb_c.currentIndex = -1 + ServerContainersLogic.onPushButtonRemoveClicked(proxyContainersModel.mapToSource(index)) + close() + } + noFunc: function() { + close() + } + } + ImageButtonType { id: button_share visible: (index === tb_c.currentIndex) && ServerContainersLogic.isManagedServer @@ -418,7 +418,7 @@ PageBase { BlueButtonType { id: pb_add_container - visible: container_selector.selectedIndex < 0 + visible: container_selector.selectedIndex < 0 && ServerContainersLogic.isManagedServer anchors.horizontalCenter: parent.horizontalCenter anchors.bottom: parent.bottom @@ -430,6 +430,5 @@ PageBase { text: qsTr("Install new service") font.pixelSize: 16 onClicked: container_selector.visible ? container_selector.close() : container_selector.open() - } } diff --git a/client/ui/qml/Pages/PageServerSettings.qml b/client/ui/qml/Pages/PageServerSettings.qml index 921cc8d9..85bc07e3 100644 --- a/client/ui/qml/Pages/PageServerSettings.qml +++ b/client/ui/qml/Pages/PageServerSettings.qml @@ -93,24 +93,49 @@ PageBase { ServerSettingsLogic.onPushButtonClearClientCacheClicked() } } + BlueButtonType { Layout.fillWidth: true Layout.topMargin: 10 text: ServerSettingsLogic.pushButtonClearText visible: ServerSettingsLogic.pushButtonClearVisible - onClicked: { - ServerSettingsLogic.onPushButtonClearServer() + onClicked: { + popupClearServer.open() } } + + PopupWithQuestion { + id: popupClearServer + questionText: "Attention! All containers will be deleted on the server. This means that configuration files, keys and certificates will be deleted. Continue?" + yesFunc: function() { + ServerSettingsLogic.onPushButtonClearServer() + close() + } + noFunc: function() { + close() + } + } + BlueButtonType { Layout.fillWidth: true Layout.topMargin: 10 text: qsTr("Forget this server") onClicked: { - ServerSettingsLogic.onPushButtonForgetServer() + popupForgetServer.open() } } + PopupWithQuestion { + id: popupForgetServer + questionText: "Attention! This action will not remove the container on the server, it will only remove the container information from the application. Continue?" + yesFunc: function() { + ServerSettingsLogic.onPushButtonForgetServer() + close() + } + noFunc: function() { + close() + } + } } } diff --git a/client/ui/qml/Pages/PageStart.qml b/client/ui/qml/Pages/PageStart.qml index d5e67e48..eacb607d 100644 --- a/client/ui/qml/Pages/PageStart.qml +++ b/client/ui/qml/Pages/PageStart.qml @@ -154,7 +154,7 @@ PageBase { if (Qt.platform.os === "ios") { UiLogic.goToPage(PageEnum.QrDecoderIos) } else { - UiLogic.goToPage(PageEnum.QrDecoder) + StartPageLogic.startQrDecoder() } } enabled: StartPageLogic.pushButtonConnectEnabled diff --git a/client/ui/qml/Pages/Protocols/PageProtoCloak.qml b/client/ui/qml/Pages/Protocols/PageProtoCloak.qml index 86a2584a..bbccbad3 100644 --- a/client/ui/qml/Pages/Protocols/PageProtoCloak.qml +++ b/client/ui/qml/Pages/Protocols/PageProtoCloak.qml @@ -11,10 +11,9 @@ PageProtocolBase { protocol: ProtocolEnum.Cloak logic: UiLogic.protocolLogic(protocol) - enabled: logic.pageEnabled BackButton { id: back - enabled: logic.pageEnabled + enabled: !logic.pushButtonCancelVisible } Caption { diff --git a/client/ui/qml/Pages/Protocols/PageProtoOpenVPN.qml b/client/ui/qml/Pages/Protocols/PageProtoOpenVPN.qml index a0779d1c..590501d5 100644 --- a/client/ui/qml/Pages/Protocols/PageProtoOpenVPN.qml +++ b/client/ui/qml/Pages/Protocols/PageProtoOpenVPN.qml @@ -13,7 +13,7 @@ PageProtocolBase { BackButton { id: back - enabled: logic.pageEnabled + enabled: !logic.pushButtonCancelVisible } Caption { @@ -285,8 +285,6 @@ PageProtocolBase { } } } - - } BasicButtonType { @@ -338,8 +336,6 @@ PageProtocolBase { } } } - - } LabelType { diff --git a/client/ui/qml/Pages/Protocols/PageProtoShadowSocks.qml b/client/ui/qml/Pages/Protocols/PageProtoShadowSocks.qml index 4d627fe2..160c3ef6 100644 --- a/client/ui/qml/Pages/Protocols/PageProtoShadowSocks.qml +++ b/client/ui/qml/Pages/Protocols/PageProtoShadowSocks.qml @@ -13,7 +13,7 @@ PageProtocolBase { BackButton { id: back - enabled: logic.pageEnabled + enabled: !logic.pushButtonCancelVisible } Caption { diff --git a/client/ui/qml/main.qml b/client/ui/qml/main.qml index 7f631f96..10552c11 100644 --- a/client/ui/qml/main.qml +++ b/client/ui/qml/main.qml @@ -232,18 +232,6 @@ Window { } } - MessageDialog { - id: closePrompt -// x: (root.width - width) / 2 -// y: (root.height - height) / 2 - title: qsTr("Exit") - text: qsTr("Do you really want to quit?") -// standardButtons: StandardButton.Yes | StandardButton.No -// onYesClicked: { -// Qt.quit() -// } - visible: false - } MessageDialog { id: publicKeyWarning title: "AmneziaVPN" diff --git a/deploy/build_android.sh b/deploy/build_android.sh index 4357034f..d9d8f5c7 100644 --- a/deploy/build_android.sh +++ b/deploy/build_android.sh @@ -51,7 +51,7 @@ $QT_HOST_PATH/bin/androiddeployqt \ --gradle \ --release \ --input android-AmneziaVPN-deployment-settings.json \ - --android-platform android-31 + --android-platform android-33 echo "............Copy apk.................." cp $OUT_APP_DIR/android-build/build/outputs/apk/release/android-build-release-unsigned.apk \ diff --git a/deploy/data/linux/client/bin/ck-client b/deploy/data/linux/client/bin/ck-client new file mode 100755 index 00000000..a6ef310c Binary files /dev/null and b/deploy/data/linux/client/bin/ck-client differ diff --git a/deploy/data/linux/client/bin/ss-local b/deploy/data/linux/client/bin/ss-local new file mode 100755 index 00000000..292abf6f Binary files /dev/null and b/deploy/data/linux/client/bin/ss-local differ diff --git a/deploy/installer/config/controlscript.js b/deploy/installer/config/controlscript.js index 9be76c15..bf22f8f6 100644 --- a/deploy/installer/config/controlscript.js +++ b/deploy/installer/config/controlscript.js @@ -26,7 +26,7 @@ function appInstalled() } else if (runningOnMacOS()){ appInstalledUninstallerPath = "/Applications/" + appName() + ".app/maintenancetool.app/Contents/MacOS/maintenancetool"; } else if (runningOnLinux()){ - allInstalledUninstallerPath = "/opt/" + appName(); + appInstalledUninstallerPath = "/opt/" + appName() + "/maintenancetool"; } return installer.fileExists(appInstalledUninstallerPath) || installer.fileExists(appInstalledUninstallerPath_x86); @@ -49,7 +49,7 @@ function runningOnMacOS() function runningOnLinux() { - return (installer.value("os") === "linux"); + return ((installer.value("os") === "linux") || (installer.value("os") === "x11")); } function sleep(miliseconds) { diff --git a/deploy/installer/packages/org.amneziavpn.package/meta/componentscript.js b/deploy/installer/packages/org.amneziavpn.package/meta/componentscript.js index d752d5a3..aae48338 100644 --- a/deploy/installer/packages/org.amneziavpn.package/meta/componentscript.js +++ b/deploy/installer/packages/org.amneziavpn.package/meta/componentscript.js @@ -93,7 +93,7 @@ Component.prototype.createOperations = function() } else if (runningOnMacOS()) { component.addElevatedOperation("Execute", "@TargetDir@/post_install.sh", "UNDOEXECUTE", "@TargetDir@/post_uninstall.sh"); } else if (runningOnLinux()) { - component.addElevatedOperation("Execute", "bash", "@TargetDir@/post_install.sh", "UNDOEXECUTE", "bash", "@TargetDir@/post_uninstall.sh"); + component.addElevatedOperation("Execute", "bash", "@TargetDir@/post_install.sh", "UNDOEXECUTE", "bash", "@TargetDir@/post_uninstall.sh"); } } diff --git a/service/CMakeLists.txt b/service/CMakeLists.txt index bfad3a3b..cfb3beb2 100644 --- a/service/CMakeLists.txt +++ b/service/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.23.0 FATAL_ERROR) +cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR) set(PROJECT service) project(${PROJECT}) @@ -12,4 +12,4 @@ endif() if(WIN32) add_subdirectory(wireguard-service) -endif() \ No newline at end of file +endif() diff --git a/service/server/CMakeLists.txt b/service/server/CMakeLists.txt index bedc1039..950544b7 100644 --- a/service/server/CMakeLists.txt +++ b/service/server/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.23.0 FATAL_ERROR) +cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR) set(PROJECT AmneziaVPN-service) project(${PROJECT}) diff --git a/service/wireguard-service/CMakeLists.txt b/service/wireguard-service/CMakeLists.txt index 6e064f71..33a3d584 100644 --- a/service/wireguard-service/CMakeLists.txt +++ b/service/wireguard-service/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.23.0 FATAL_ERROR) +cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR) set(PROJECT wireguard-service) project(${PROJECT} LANGUAGES CXX)