From a56fbeb6117a0eaa0226c241a94e14005c6288a2 Mon Sep 17 00:00:00 2001 From: Steve Tchatchouang <38782190+karolsteve@users.noreply.github.com> Date: Tue, 30 Aug 2022 13:58:03 +0100 Subject: [PATCH 01/43] Fix android build script Using correct qt binaries folder (gcc64 -> Android) --- deploy/build_android.sh | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/deploy/build_android.sh b/deploy/build_android.sh index 9afe49d3..2b46b5fb 100644 --- a/deploy/build_android.sh +++ b/deploy/build_android.sh @@ -3,7 +3,6 @@ echo "Build script started ..." set -o errexit -o nounset - # Hold on to current directory PROJECT_DIR=$(pwd) DEPLOY_DIR=$PROJECT_DIR/deploy @@ -30,7 +29,8 @@ QMAKE_STASH_FILE=$PROJECT_DIR/.qmake_stash # Seacrh Qt if [ -z "${QT_VERSION+x}" ]; then QT_VERSION=5.15.2; -QT_BIN_DIR=$HOME/Qt/$QT_VERSION/gcc_64/bin +QT_BIN_DIR=$HOME/Qt/$QT_VERSION/android/bin +#QT_BIN_DIR=$HOME/Qt/$QT_VERSION/gcc_64/bin fi echo "Using Qt in $QT_BIN_DIR" @@ -46,22 +46,23 @@ echo "Building App..." cd $BUILD_DIR $QT_BIN_DIR/qmake -r -spec android-clang CONFIG+=qtquickcompiler ANDROID_ABIS="armeabi-v7a arm64-v8a x86 x86_64" $PROJECT_DIR/AmneziaVPN.pro -$ANDROID_NDK_HOME/prebuilt/linux-x86_64/bin/make -j2 -$ANDROID_NDK_HOME/prebuilt/linux-x86_64/bin/make install INSTALL_ROOT=android - - - +echo "Executing make... may take long time" +$ANDROID_NDK_HOME/prebuilt/linux-x86_64/bin/make -j2 > /dev/null 2>/dev/null +echo "Make install..." +$ANDROID_NDK_HOME/prebuilt/linux-x86_64/bin/make install INSTALL_ROOT=android > /dev/null 2>/dev/null +echo "Build OK" # Build and run tests here -#echo "............Deploy.................." - -# TODO possible solution: https://github.com/mhoeher/opentodolist/blob/b8981852e500589851132a02c5a62af9b0ed592c/ci/android-cmake-build.sh -#$QT_BIN_DIR/androiddeployqt \ -# --output $OUT_APP_DIR \ -# --gradle \ -# --release \ -# --deployment bundled - -#cp $OUT_APP_DIR/build/outputs/apk/release/android-build-release-unsigned.apk \ -# OpenTodoList-${ANDROID_ABIS}-${OTL_VERSION}.apk +echo "............Deploy.................." +cd $OUT_APP_DIR +#--verbose removed +$QT_BIN_DIR/androiddeployqt \ + --output $OUT_APP_DIR/android \ + --gradle \ + --release \ + --input android-AmneziaVPN-deployment-settings.json + +echo "............Copy apk.................." +cp $OUT_APP_DIR/android/build/outputs/apk/release/android-release-unsigned.apk \ + $PROJECT_DIR/AmneziaVPN-release-unsigned.apk From f6d329ac48ac3db6504eeab0c60ced6084cce240 Mon Sep 17 00:00:00 2001 From: Steve Tchatchouang <38782190+karolsteve@users.noreply.github.com> Date: Tue, 30 Aug 2022 14:00:40 +0100 Subject: [PATCH 02/43] Fix Android job CI --- .travis.yml | 75 +++++++++++++++++++++++------------------------------ 1 file changed, 32 insertions(+), 43 deletions(-) diff --git a/.travis.yml b/.travis.yml index c5022b12..7113efad 100644 --- a/.travis.yml +++ b/.travis.yml @@ -209,65 +209,54 @@ jobs: # ------------------------------------------------------ - name: Android os: linux - language: android - dist: xenial - - addons: - apt: - packages: - - p7zip - - python3 - - python3-pip - - android: - components: - # Uncomment the lines below if you want to - # use the latest revision of Android SDK Tools - # - tools - # - platform-tools - - # The BuildTools version used by your project - - build-tools-30.0.2 - - # The SDK version used to compile your project - - android-30 - - # Additional components - - extra - - extra-google-google_play_services - - extra-google-m2repository - - extra-android-m2repository - + language: cpp + dist: focal env: - QT_VERSION=5.15.2 - QT_BIN_DIR=$HOME/Qt/$QT_VERSION/android/bin - USE_ANDROID_NDK_VERSION=21d - ANDROID_NDK_HOME=$HOME/NDK + - PROJ_ROOT=$(pwd) + - JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64 install: - - | - if [ ! -f $QT_BIN_DIR/qmake ]; then \ - export PATH=$HOME/.local/bin:$PATH && \ - python3 -m pip install -U aqtinstall requests py7zr && \ - python3 -m pip show aqtinstall && \ - python3 -m aqt install-qt linux android $QT_VERSION android_armv7 -m all -O $HOME/Qt && \ - python3 -m aqt install-qt linux android $QT_VERSION android_arm64_v8a -m all -O $HOME/Qt && \ - python3 -m aqt install-qt linux android $QT_VERSION android_x86_64 -m all -O $HOME/Qt && \ - python3 -m aqt install-qt linux android $QT_VERSION android_x86 -m all -O $HOME/Qt ; \ + - echo "Installing Java 8" + - sudo apt-get install openjdk-8-jdk > /dev/null + - echo "QT bin install" && pwd + - if [ ! -f $QT_BIN_DIR/qmake ]; then + wget https://ondjoss.com/qt15_2_android_linux_x86_64.zip > /dev/null && + mkdir -p $HOME/Qt/$QT_VERSION && \ + unzip ./qt15_2_android_linux_x86_64.zip -d $HOME/Qt/$QT_VERSION > /dev/null ; fi - - | - export TERM=dumb && - curl -L https://dl.google.com/android/repository/android-ndk-r${USE_ANDROID_NDK_VERSION}-linux-x86_64.zip -O && + - echo "Download SDK" && pwd + - export ANDROID_HOME=`pwd`/sdk + - export LOCAL_ANDROID_HOME=`pwd`/sdk + - export ANDROID_SDK_ROOT=`pwd`/sdk + - export LOCAL_ANDROID_SDK_ROOT=`pwd`/sdk + - export ANDROID_API_VERSION=android-21 + - mkdir -p sdk && cd sdk && + wget https://dl.google.com/android/repository/sdk-tools-linux-4333796.zip -qO sdk.zip > /dev/null || exit 1 + - ls -ll && unzip -q sdk.zip || exit 1 + - echo "Download all needed tools" && pwd && ls -ll && + yes | ./tools/bin/sdkmanager --licenses > /dev/null 2>/dev/null + - ./tools/bin/sdkmanager --install "cmdline-tools;latest" "platform-tools" "platforms;android-30" "build-tools;30.0.2" > /dev/null 2>/dev/null + - cd $PROJ_ROOT && echo "Download NDK" && pwd + - export TERM=dumb && + curl -L https://dl.google.com/android/repository/android-ndk-r${USE_ANDROID_NDK_VERSION}-linux-x86_64.zip -O && unzip ./android-ndk-r${USE_ANDROID_NDK_VERSION}-linux-x86_64.zip > /dev/null && - rm android-ndk-r${USE_ANDROID_NDK_VERSION}-linux-x86_64.zip && + rm android-ndk-r${USE_ANDROID_NDK_VERSION}-linux-x86_64.zip && export ANDROID_NDK_HOME=`pwd`/android-ndk-r${USE_ANDROID_NDK_VERSION} && + export ANDROID_NDK_ROOT=`pwd`/android-ndk-r${USE_ANDROID_NDK_VERSION} && export LOCAL_ANDROID_NDK_HOME="$ANDROID_NDK_HOME" && export LOCAL_ANDROID_NDK_HOST_PLATFORM="linux-x86_64" && export PATH=$PATH:${ANDROID_NDK_HOME} && + export ANDROID_NDK_HOST=linux-x86_64 && + export ANDROID_NDK_PLATFORM=android-21 && env + - echo "Check env vars" && echo $ANDROID_SDK_ROOT && echo $ANDROID_NDK_ROOT && echo $QT_BIN_DIR script: - - bash deploy/build_android.sh + - travis_wait 60 bash deploy/build_android.sh after_script: - ccache --show-stats From cdb1a4c288e86c5cef31ce08f326ad1435dfd201 Mon Sep 17 00:00:00 2001 From: pokamest Date: Fri, 2 Sep 2022 12:39:46 -0700 Subject: [PATCH 03/43] Fix for AVP-01-006 - inscure app config permissions --- client/amnezia_application.cpp | 19 +++++++++++++++++++ client/secure_qsettings.cpp | 5 ++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/client/amnezia_application.cpp b/client/amnezia_application.cpp index 43f352a2..4420f7dc 100644 --- a/client/amnezia_application.cpp +++ b/client/amnezia_application.cpp @@ -1,6 +1,7 @@ #include "amnezia_application.h" #include +#include #include #include @@ -49,6 +50,24 @@ #endif { setQuitOnLastWindowClosed(false); + + // Fix config file permissions +#ifdef Q_OS_LINUX + { + QSettings s(ORGANIZATION_NAME, APPLICATION_NAME); + s.setValue("permFixed", true); + } + + QString configLoc1 = QStandardPaths::standardLocations(QStandardPaths::ConfigLocation).first() + "/" + + ORGANIZATION_NAME + "/" + APPLICATION_NAME + ".conf"; + QFile::setPermissions(configLoc1, QFileDevice::ReadOwner | QFileDevice::WriteOwner); + + QString configLoc2 = QStandardPaths::standardLocations(QStandardPaths::ConfigLocation).first() + "/" + + ORGANIZATION_NAME + "/" + APPLICATION_NAME + "/" + APPLICATION_NAME + ".conf"; + QFile::setPermissions(configLoc2, QFileDevice::ReadOwner | QFileDevice::WriteOwner); + +#endif + m_settings = std::shared_ptr(new Settings); m_serverController = std::shared_ptr(new ServerController(m_settings, this)); m_configurator = std::shared_ptr(new VpnConfigurator(m_settings, m_serverController, this)); diff --git a/client/secure_qsettings.cpp b/client/secure_qsettings.cpp index b78863ba..a58ae7c4 100644 --- a/client/secure_qsettings.cpp +++ b/client/secure_qsettings.cpp @@ -162,7 +162,10 @@ QByteArray SecureQSettings::decryptText(const QByteArray& ba) const bool SecureQSettings::encryptionRequired() const { - // TODO: review on linux +#ifdef Q_OS_LINUX + // QtKeyChain failing on Linux + return false; +#endif return true; } From 5cbe7600a66cbe621f57e4ba779a2a19c6c62c3b Mon Sep 17 00:00:00 2001 From: leetthewire Date: Sat, 3 Sep 2022 06:19:48 +0400 Subject: [PATCH 04/43] updated gitignore --- .gitignore | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitignore b/.gitignore index 26ce16e4..1b4e16b2 100644 --- a/.gitignore +++ b/.gitignore @@ -66,6 +66,12 @@ client/3rd/ShadowSocks/build/ # QtCtreator CMake CMakeLists.txt.user* +# Linux files +*.7z +deploy/AppDir +deploy/Tools +deploy/AmneziaVPN*Installer* + # MACOS files .DS_Store client/.DS_Store From 279f866bf569c461e549f67b94ccbfd1f55eba9f Mon Sep 17 00:00:00 2001 From: leetthewire Date: Sat, 3 Sep 2022 06:19:25 +0400 Subject: [PATCH 05/43] completed build linux script --- deploy/build_linux.sh | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) mode change 100644 => 100755 deploy/build_linux.sh diff --git a/deploy/build_linux.sh b/deploy/build_linux.sh old mode 100644 new mode 100755 index 996c758a..94506625 --- a/deploy/build_linux.sh +++ b/deploy/build_linux.sh @@ -11,6 +11,15 @@ DEPLOY_DIR=$PROJECT_DIR/deploy mkdir -p $DEPLOY_DIR/build BUILD_DIR=$DEPLOY_DIR/build +APP_DIR=$DEPLOY_DIR/AppDir +mkdir -p $APP_DIR + +TOOLS_DIR=$DEPLOY_DIR/Tools +mkdir -p $TOOLS_DIR + +CQTDEPLOYER_DIR=$TOOLS_DIR/cqtdeployer +mkdir -p $CQTDEPLOYER_DIR + echo "Project dir: ${PROJECT_DIR}" echo "Build dir: ${BUILD_DIR}" @@ -18,12 +27,8 @@ APP_NAME=AmneziaVPN APP_FILENAME=$APP_NAME.app APP_DOMAIN=org.amneziavpn.package -OUT_APP_DIR=$BUILD_DIR/client -BUNDLE_DIR=$OUT_APP_DIR/$APP_FILENAME - DEPLOY_DATA_DIR=$PROJECT_DIR/deploy/data/linux -INSTALLER_DATA_DIR=$BUILD_DIR/installer/packages/$APP_DOMAIN/data -INSTALLER_BUNDLE_DIR=$BUILD_DIR/installer/$APP_FILENAME +INSTALLER_DATA_DIR=$PROJECT_DIR/deploy/installer/packages/$APP_DOMAIN/data PRO_FILE_PATH=$PROJECT_DIR/$APP_NAME.pro QMAKE_STASH_FILE=$PROJECT_DIR/.qmake_stash @@ -31,13 +36,10 @@ QMAKE_STASH_FILE=$PROJECT_DIR/.qmake_stash # Seacrh Qt if [ -z "${QT_VERSION+x}" ]; then QT_VERSION=5.15.2; -QIF_VERSION=4.1 -QT_BIN_DIR=$HOME/Qt/$QT_VERSION/gcc_64/bin -QIF_BIN_DIR=$QT_BIN_DIR/../../../Tools/QtInstallerFramework/$QIF_VERSION/bin +QT_BIN_DIR=/opt/Qt/$QT_VERSION/gcc_64/bin fi echo "Using Qt in $QT_BIN_DIR" -echo "Using QIF in $QIF_BIN_DIR" # Checking env @@ -56,4 +58,23 @@ make #echo "............Deploy.................." +cp -r $DEPLOY_DATA_DIR/* $APP_DIR + +wget -O $TOOLS_DIR/CQtDeployer.zip https://github.com/QuasarApp/CQtDeployer/releases/download/v1.5.4.17/CQtDeployer_1.5.4.17_Linux_x86_64.zip + +unzip -o $TOOLS_DIR/CQtDeployer.zip -d $CQTDEPLOYER_DIR/ + +chmod +x -R $CQTDEPLOYER_DIR +#chmod +x $CQTDEPLOYER_DIR/binarycreator.sh + +$CQTDEPLOYER_DIR/cqtdeployer.sh -bin $BUILD_DIR/client/AmneziaVPN -qmake $QT_BIN_DIR/qmake -qmlDir $PROJECT_DIR/client/ui/qml/ -targetDir $APP_DIR/client/ + +$CQTDEPLOYER_DIR/cqtdeployer.sh -bin $BUILD_DIR/service/server/AmneziaVPN-service -qmake $QT_BIN_DIR/qmake -targetDir $APP_DIR/service/ + +rm -f $INSTALLER_DATA_DIR/data.7z + +7z a $INSTALLER_DATA_DIR/data.7z $APP_DIR/* + +$CQTDEPLOYER_DIR/binarycreator.sh --offline-only -v -c $PROJECT_DIR/deploy/installer/config/linux.xml -p $PROJECT_DIR/deploy/installer/packages/ -f $PROJECT_DIR/deploy/AmneziaVPN_Linux_Installer + From 3cb14ad3bcc57921143cfb4dcd2ebac530e48dad Mon Sep 17 00:00:00 2001 From: pokamest Date: Sat, 3 Sep 2022 06:31:59 -0700 Subject: [PATCH 06/43] Tiny Linux build scripts fixes --- .travis.yml | 1 + deploy/build_linux.sh | 19 +++++++++++-------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index c5022b12..0430d529 100644 --- a/.travis.yml +++ b/.travis.yml @@ -162,6 +162,7 @@ jobs: apt: packages: - p7zip + - p7zip-full - python3 - python3-pip - libgl-dev diff --git a/deploy/build_linux.sh b/deploy/build_linux.sh index 94506625..754b5e6d 100755 --- a/deploy/build_linux.sh +++ b/deploy/build_linux.sh @@ -35,8 +35,12 @@ QMAKE_STASH_FILE=$PROJECT_DIR/.qmake_stash # Seacrh Qt if [ -z "${QT_VERSION+x}" ]; then -QT_VERSION=5.15.2; -QT_BIN_DIR=/opt/Qt/$QT_VERSION/gcc_64/bin + QT_VERSION=5.15.2 + if [ -f /opt/Qt/$QT_VERSION/gcc_64/bin/qmake ]; then + QT_BIN_DIR=/opt/Qt/$QT_VERSION/gcc_64/bin + elif [ -f $HOME/Qt/$QT_VERSION/gcc_64/bin/qmake ]; then + QT_BIN_DIR=$HOME/Qt/$QT_VERSION/gcc_64/bin + fi fi echo "Using Qt in $QT_BIN_DIR" @@ -60,15 +64,14 @@ make cp -r $DEPLOY_DATA_DIR/* $APP_DIR -wget -O $TOOLS_DIR/CQtDeployer.zip https://github.com/QuasarApp/CQtDeployer/releases/download/v1.5.4.17/CQtDeployer_1.5.4.17_Linux_x86_64.zip +if [ ! -f $CQTDEPLOYER_DIR/cqtdeployer.sh ]; then + wget -O $TOOLS_DIR/CQtDeployer.zip https://github.com/QuasarApp/CQtDeployer/releases/download/v1.5.4.17/CQtDeployer_1.5.4.17_Linux_x86_64.zip + unzip -o $TOOLS_DIR/CQtDeployer.zip -d $CQTDEPLOYER_DIR/ + chmod +x -R $CQTDEPLOYER_DIR +fi -unzip -o $TOOLS_DIR/CQtDeployer.zip -d $CQTDEPLOYER_DIR/ - -chmod +x -R $CQTDEPLOYER_DIR -#chmod +x $CQTDEPLOYER_DIR/binarycreator.sh $CQTDEPLOYER_DIR/cqtdeployer.sh -bin $BUILD_DIR/client/AmneziaVPN -qmake $QT_BIN_DIR/qmake -qmlDir $PROJECT_DIR/client/ui/qml/ -targetDir $APP_DIR/client/ - $CQTDEPLOYER_DIR/cqtdeployer.sh -bin $BUILD_DIR/service/server/AmneziaVPN-service -qmake $QT_BIN_DIR/qmake -targetDir $APP_DIR/service/ rm -f $INSTALLER_DATA_DIR/data.7z From 448c01ca99e26f3512c4dec4a911d0cd6abbc755 Mon Sep 17 00:00:00 2001 From: pokamest Date: Sun, 4 Sep 2022 14:49:33 +0300 Subject: [PATCH 07/43] Travis build fix --- .gitlab-ci.yml | 27 --------------------------- .travis.yml | 22 ++++++++++++++-------- deploy/build_android.sh | 12 +++++------- 3 files changed, 19 insertions(+), 42 deletions(-) delete mode 100644 .gitlab-ci.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index 6c8fa28b..00000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,27 +0,0 @@ -variables: - GIT_STRATEGY: clone - -stages: - - build - -build-windows: - stage: build - tags: - - windows - script: - - cmd.exe /k "deploy\windows-env.bat && cd deploy && windows.bat" - artifacts: - name: artifacts-windows - paths: - - AmneziaVPN.exe - -build-macos: - stage: build - tags: - - macos - script: - - cd deploy && ./macos.sh - artifacts: - name: artifacts-macos - paths: - - AmneziaVPN.dmg diff --git a/.travis.yml b/.travis.yml index 70d9b6ec..a3d7eacd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -220,14 +220,20 @@ jobs: - PROJ_ROOT=$(pwd) - JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64 + addons: + apt: + packages: + - p7zip + - python3 + - python3-pip + - openjdk-8-jdk + install: - - echo "Installing Java 8" - - sudo apt-get install openjdk-8-jdk > /dev/null - - echo "QT bin install" && pwd - - if [ ! -f $QT_BIN_DIR/qmake ]; then - wget https://ondjoss.com/qt15_2_android_linux_x86_64.zip > /dev/null && - mkdir -p $HOME/Qt/$QT_VERSION && \ - unzip ./qt15_2_android_linux_x86_64.zip -d $HOME/Qt/$QT_VERSION > /dev/null ; + - | + if [ ! -f $QT_BIN_DIR/qmake ]; then \ + python3 -m pip install -U aqtinstall requests py7zr && \ + python3 -m pip show aqtinstall && \ + python3 -m aqt install-qt linux android $QT_VERSION -m all -O $HOME/Qt ; \ fi - echo "Download SDK" && pwd - export ANDROID_HOME=`pwd`/sdk @@ -257,7 +263,7 @@ jobs: - echo "Check env vars" && echo $ANDROID_SDK_ROOT && echo $ANDROID_NDK_ROOT && echo $QT_BIN_DIR script: - - travis_wait 60 bash deploy/build_android.sh + - deploy/build_android.sh after_script: - ccache --show-stats diff --git a/deploy/build_android.sh b/deploy/build_android.sh index 2b46b5fb..75e8c46c 100644 --- a/deploy/build_android.sh +++ b/deploy/build_android.sh @@ -30,16 +30,16 @@ QMAKE_STASH_FILE=$PROJECT_DIR/.qmake_stash if [ -z "${QT_VERSION+x}" ]; then QT_VERSION=5.15.2; QT_BIN_DIR=$HOME/Qt/$QT_VERSION/android/bin -#QT_BIN_DIR=$HOME/Qt/$QT_VERSION/gcc_64/bin fi echo "Using Qt in $QT_BIN_DIR" +echo "Using Android SDK in $ANDROID_SDK_ROOT" +echo "Using Android NDK in $ANDROID_NDK_ROOT" # Checking env $QT_BIN_DIR/qmake -v -make -v -gcc -v +$ANDROID_NDK_HOME/prebuilt/linux-x86_64/bin/make -v # Build App echo "Building App..." @@ -47,16 +47,14 @@ cd $BUILD_DIR $QT_BIN_DIR/qmake -r -spec android-clang CONFIG+=qtquickcompiler ANDROID_ABIS="armeabi-v7a arm64-v8a x86 x86_64" $PROJECT_DIR/AmneziaVPN.pro echo "Executing make... may take long time" -$ANDROID_NDK_HOME/prebuilt/linux-x86_64/bin/make -j2 > /dev/null 2>/dev/null +$ANDROID_NDK_HOME/prebuilt/linux-x86_64/bin/make -j2 echo "Make install..." -$ANDROID_NDK_HOME/prebuilt/linux-x86_64/bin/make install INSTALL_ROOT=android > /dev/null 2>/dev/null +$ANDROID_NDK_HOME/prebuilt/linux-x86_64/bin/make install INSTALL_ROOT=android echo "Build OK" -# Build and run tests here echo "............Deploy.................." cd $OUT_APP_DIR -#--verbose removed $QT_BIN_DIR/androiddeployqt \ --output $OUT_APP_DIR/android \ --gradle \ From df26f492a787eebfea5a604d754d8faa0c98d920 Mon Sep 17 00:00:00 2001 From: pokamest Date: Sun, 4 Sep 2022 23:27:00 +0300 Subject: [PATCH 08/43] Travis build fix --- .travis.yml | 61 +++++++++++++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/.travis.yml b/.travis.yml index a3d7eacd..72795dd6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -168,6 +168,7 @@ jobs: - libgl-dev - mesa-common-dev - libpulse-dev + - libxcb-icccm4 env: - QT_VERSION=5.15.2 @@ -215,10 +216,20 @@ jobs: env: - QT_VERSION=5.15.2 - QT_BIN_DIR=$HOME/Qt/$QT_VERSION/android/bin - - USE_ANDROID_NDK_VERSION=21d - - ANDROID_NDK_HOME=$HOME/NDK - - PROJ_ROOT=$(pwd) + - ANDROID_API_VERSION=android-21 + - ANDROID_HOME=$HOME/sdk + - ANDROID_SDK_ROOT=$ANDROID_HOME + - LOCAL_ANDROID_HOME=$ANDROID_HOME + - LOCAL_ANDROID_SDK_ROOT=$ANDROID_HOME + - NDK_VERSION=21d + - ANDROID_NDK_PLATFORM=android-21 + - ANDROID_NDK_HOME=$HOME/android-ndk-r${NDK_VERSION} + - ANDROID_NDK_ROOT=$ANDROID_NDK_HOME + - ANDROID_NDK_HOST=linux-x86_64 + - LOCAL_ANDROID_NDK_HOME=$ANDROID_NDK_HOME + - LOCAL_ANDROID_NDK_HOST_PLATFORM=$ANDROID_NDK_HOST - JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64 + - TERM=dumb addons: apt: @@ -235,35 +246,24 @@ jobs: python3 -m pip show aqtinstall && \ python3 -m aqt install-qt linux android $QT_VERSION -m all -O $HOME/Qt ; \ fi - - echo "Download SDK" && pwd - - export ANDROID_HOME=`pwd`/sdk - - export LOCAL_ANDROID_HOME=`pwd`/sdk - - export ANDROID_SDK_ROOT=`pwd`/sdk - - export LOCAL_ANDROID_SDK_ROOT=`pwd`/sdk - - export ANDROID_API_VERSION=android-21 - - mkdir -p sdk && cd sdk && - wget https://dl.google.com/android/repository/sdk-tools-linux-4333796.zip -qO sdk.zip > /dev/null || exit 1 - - ls -ll && unzip -q sdk.zip || exit 1 - - echo "Download all needed tools" && pwd && ls -ll && - yes | ./tools/bin/sdkmanager --licenses > /dev/null 2>/dev/null - - ./tools/bin/sdkmanager --install "cmdline-tools;latest" "platform-tools" "platforms;android-30" "build-tools;30.0.2" > /dev/null 2>/dev/null - - cd $PROJ_ROOT && echo "Download NDK" && pwd - - export TERM=dumb && - curl -L https://dl.google.com/android/repository/android-ndk-r${USE_ANDROID_NDK_VERSION}-linux-x86_64.zip -O && - unzip ./android-ndk-r${USE_ANDROID_NDK_VERSION}-linux-x86_64.zip > /dev/null && - rm android-ndk-r${USE_ANDROID_NDK_VERSION}-linux-x86_64.zip && - export ANDROID_NDK_HOME=`pwd`/android-ndk-r${USE_ANDROID_NDK_VERSION} && - export ANDROID_NDK_ROOT=`pwd`/android-ndk-r${USE_ANDROID_NDK_VERSION} && - export LOCAL_ANDROID_NDK_HOME="$ANDROID_NDK_HOME" && - export LOCAL_ANDROID_NDK_HOST_PLATFORM="linux-x86_64" && - export PATH=$PATH:${ANDROID_NDK_HOME} && - export ANDROID_NDK_HOST=linux-x86_64 && - export ANDROID_NDK_PLATFORM=android-21 && - env - - echo "Check env vars" && echo $ANDROID_SDK_ROOT && echo $ANDROID_NDK_ROOT && echo $QT_BIN_DIR + + - | + if [ ! -f $ANDROID_SDK_ROOT/tools/bin/sdkmanager ]; then \ + echo "Download Android SDK" && \ + wget https://dl.google.com/android/repository/sdk-tools-linux-4333796.zip -qO $HOME/sdk.zip > /dev/null && \ + unzip -q -d $ANDROID_SDK_ROOT $HOME/sdk.zip && \ + echo "Download tools" && \ + yes | ./tools/bin/sdkmanager --licenses && \ + $ANDROID_SDK_ROOT/tools/bin/sdkmanager --install "cmdline-tools;latest" "platform-tools" "platforms;android-30" "build-tools;30.0.2" || exit 1 ; \ + fi + - | + if [ ! -f $ANDROID_NDK_ROOT/ndk-build ]; then \ + wget https://dl.google.com/android/repository/android-ndk-r${NDK_VERSION}-linux-x86_64.zip -qO $HOME/ndk.zip && + unzip -q -d $HOME $HOME/ndk.zip ; \ + fi script: - - deploy/build_android.sh + - bash deploy/build_android.sh after_script: - ccache --show-stats @@ -286,6 +286,7 @@ jobs: - $HOME/.gradle/wrapper/ - $HOME/.android/build-cache - $ANDROID_NDK_HOME + - $ANDROID_SDK_ROOT # ------------------------------------------------------ - name: iOS From ac7d2246454601c10b909da44715ebb292c0493e Mon Sep 17 00:00:00 2001 From: pokamest Date: Mon, 5 Sep 2022 00:11:55 +0300 Subject: [PATCH 09/43] Travis build fix --- .travis.yml | 18 +++++++++++++++++- deploy/build_linux.sh | 2 ++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 72795dd6..9f0a9a8a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -168,7 +168,23 @@ jobs: - libgl-dev - mesa-common-dev - libpulse-dev + - libxcb-* - libxcb-icccm4 + - libxcb-image0 + - libxcb-shm0 + - libxcb-util1 + - libxcb-keysyms1 + - libxcb-randr0 + - libxcb-render-util0 + - libxcb-render0 + - libxcb-shape0 + - libxcb-sync1 + - libxcb-xfixes0 + - libxcb-randr0-dev + - libxcb-xtest0-dev + - libxcb-xinerama0-dev + - libxcb-shape0-dev + - libxcb-xkb-dev env: - QT_VERSION=5.15.2 @@ -253,7 +269,7 @@ jobs: wget https://dl.google.com/android/repository/sdk-tools-linux-4333796.zip -qO $HOME/sdk.zip > /dev/null && \ unzip -q -d $ANDROID_SDK_ROOT $HOME/sdk.zip && \ echo "Download tools" && \ - yes | ./tools/bin/sdkmanager --licenses && \ + yes | $ANDROID_SDK_ROOT/tools/bin/sdkmanager --licenses && \ $ANDROID_SDK_ROOT/tools/bin/sdkmanager --install "cmdline-tools;latest" "platform-tools" "platforms;android-30" "build-tools;30.0.2" || exit 1 ; \ fi - | diff --git a/deploy/build_linux.sh b/deploy/build_linux.sh index 754b5e6d..f72f1a0c 100755 --- a/deploy/build_linux.sh +++ b/deploy/build_linux.sh @@ -78,6 +78,8 @@ rm -f $INSTALLER_DATA_DIR/data.7z 7z a $INSTALLER_DATA_DIR/data.7z $APP_DIR/* +ldd $CQTDEPLOYER_DIR/bin/binarycreator + $CQTDEPLOYER_DIR/binarycreator.sh --offline-only -v -c $PROJECT_DIR/deploy/installer/config/linux.xml -p $PROJECT_DIR/deploy/installer/packages/ -f $PROJECT_DIR/deploy/AmneziaVPN_Linux_Installer From ce27af6083abc6229b7d10d6e0c43c83c516dae2 Mon Sep 17 00:00:00 2001 From: pokamest Date: Mon, 5 Sep 2022 00:33:27 +0300 Subject: [PATCH 10/43] Travis build fix --- .travis.yml | 21 +++------------------ client/client.pro | 2 -- 2 files changed, 3 insertions(+), 20 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9f0a9a8a..0edc541e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -169,22 +169,7 @@ jobs: - mesa-common-dev - libpulse-dev - libxcb-* - - libxcb-icccm4 - - libxcb-image0 - - libxcb-shm0 - - libxcb-util1 - - libxcb-keysyms1 - - libxcb-randr0 - - libxcb-render-util0 - - libxcb-render0 - - libxcb-shape0 - - libxcb-sync1 - - libxcb-xfixes0 - - libxcb-randr0-dev - - libxcb-xtest0-dev - - libxcb-xinerama0-dev - - libxcb-shape0-dev - - libxcb-xkb-dev + - libxkbcommon-x11-0 env: - QT_VERSION=5.15.2 @@ -269,8 +254,8 @@ jobs: wget https://dl.google.com/android/repository/sdk-tools-linux-4333796.zip -qO $HOME/sdk.zip > /dev/null && \ unzip -q -d $ANDROID_SDK_ROOT $HOME/sdk.zip && \ echo "Download tools" && \ - yes | $ANDROID_SDK_ROOT/tools/bin/sdkmanager --licenses && \ - $ANDROID_SDK_ROOT/tools/bin/sdkmanager --install "cmdline-tools;latest" "platform-tools" "platforms;android-30" "build-tools;30.0.2" || exit 1 ; \ + yes | $ANDROID_SDK_ROOT/tools/bin/sdkmanager --licenses > /dev/null 2>&1 && \ + $ANDROID_SDK_ROOT/tools/bin/sdkmanager --install "cmdline-tools;latest" "platform-tools" "platforms;android-30" "build-tools;30.0.2" > /dev/null 2>&1 || exit 1 ; \ fi - | if [ ! -f $ANDROID_NDK_ROOT/ndk-build ]; then \ diff --git a/client/client.pro b/client/client.pro index 5b6d3f8f..fc05dd0f 100644 --- a/client/client.pro +++ b/client/client.pro @@ -8,8 +8,6 @@ CONFIG += qzxing_multimedia \ enable_decoder_qr_code \ enable_encoder_qr_code -DEFINES += QT_DEPRECATED_WARNINGS - include("3rd/QtSsh/src/ssh/qssh.pri") include("3rd/QtSsh/src/botan/botan.pri") !android:!ios:include("3rd/SingleApplication/singleapplication.pri") From e1c529ab9164833e455a3f0805f9030606542629 Mon Sep 17 00:00:00 2001 From: pokamest Date: Mon, 5 Sep 2022 01:38:29 +0300 Subject: [PATCH 11/43] Travis ccache --- .travis.yml | 10 +++++++++- client/client.pro | 6 ++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 0edc541e..60c954c7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -77,6 +77,7 @@ jobs: aqt install-qt windows desktop $QT_VERSION win64_msvc2019_64 -m all -O /C/Qt && \ aqt install-tool windows desktop tools_ifw -O /C/Qt ; \ fi + - choco install ccache script: - echo set BUILD_ARCH=$BUILD_ARCH > winbuild.bat @@ -86,7 +87,10 @@ jobs: - echo call \""%MSVC_PATH_WIN%\\Common7\\Tools\\VsDevCmd.bat\" -arch=amd64" >> winbuild.bat - echo call deploy\\build_windows.bat >> winbuild.bat - cmd //c winbuild.bat - + + after_script: + - ccache --show-stats + deploy: provider: releases token: $GH_TOKEN @@ -128,6 +132,7 @@ jobs: aqt install-qt windows desktop $QT_VERSION win32_msvc2019 -m all -O /C/Qt && \ aqt install-tool windows desktop tools_ifw -O /C/Qt ; \ fi + - choco install ccache script: - echo set BUILD_ARCH=$BUILD_ARCH > winbuild.bat @@ -138,6 +143,9 @@ jobs: - echo call deploy\\build_windows.bat >> winbuild.bat - cmd //c winbuild.bat + after_script: + - ccache --show-stats + deploy: provider: releases token: $GH_TOKEN diff --git a/client/client.pro b/client/client.pro index fc05dd0f..42132edc 100644 --- a/client/client.pro +++ b/client/client.pro @@ -3,6 +3,12 @@ QT += widgets core gui network xml remoteobjects quick svg TARGET = AmneziaVPN TEMPLATE = app +# silent builds on CI env +contains(DEFINES,CI){ + message("Detected CI env") + CONFIG += silent ccache +} + CONFIG += qtquickcompiler CONFIG += qzxing_multimedia \ enable_decoder_qr_code \ From 9ceadd44c9bcdc69ba1a3f5df1262a7498dbd8ec Mon Sep 17 00:00:00 2001 From: pokamest Date: Mon, 5 Sep 2022 01:56:39 +0300 Subject: [PATCH 12/43] Travis fix --- client/client.pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/client.pro b/client/client.pro index 42132edc..803c5daf 100644 --- a/client/client.pro +++ b/client/client.pro @@ -4,7 +4,7 @@ TARGET = AmneziaVPN TEMPLATE = app # silent builds on CI env -contains(DEFINES,CI){ +contains(DEFINES,$$(CI)){ message("Detected CI env") CONFIG += silent ccache } From fb07adf7c15aa22d89d512eaa23ca6ed93b1c59c Mon Sep 17 00:00:00 2001 From: pokamest Date: Mon, 5 Sep 2022 02:06:58 +0300 Subject: [PATCH 13/43] Travis fix --- client/client.pro | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/client.pro b/client/client.pro index 803c5daf..9a89842d 100644 --- a/client/client.pro +++ b/client/client.pro @@ -4,7 +4,8 @@ TARGET = AmneziaVPN TEMPLATE = app # silent builds on CI env -contains(DEFINES,$$(CI)){ +IS_CI=$$(CI) +!isEmpty(IS_CI){ message("Detected CI env") CONFIG += silent ccache } From 7a6c1de5d5c0fd21a18a2b242667e6be3bbd163f Mon Sep 17 00:00:00 2001 From: pokamest Date: Wed, 7 Sep 2022 09:51:03 +0300 Subject: [PATCH 14/43] QrCoreGen --- .gitmodules | 3 - client/3rd/qrcodegen/qrcodegen.cpp | 856 ++++++++++++++++++ client/3rd/qrcodegen/qrcodegen.hpp | 551 +++++++++++ client/3rd/qrcodegen/qrcodegen.pri | 5 + client/3rd/qzxing | 1 - client/amnezia_application.cpp | 4 - client/client.pro | 2 +- .../ui/pages_logic/ShareConnectionLogic.cpp | 33 +- client/ui/pages_logic/ShareConnectionLogic.h | 3 +- 9 files changed, 1430 insertions(+), 28 deletions(-) create mode 100644 client/3rd/qrcodegen/qrcodegen.cpp create mode 100644 client/3rd/qrcodegen/qrcodegen.hpp create mode 100644 client/3rd/qrcodegen/qrcodegen.pri delete mode 160000 client/3rd/qzxing diff --git a/.gitmodules b/.gitmodules index 328bfe76..a8295ac7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -16,9 +16,6 @@ [submodule "client/3rd/outline-go-tun2socks"] path = client/3rd/outline-go-tun2socks url = https://github.com/Jigsaw-Code/outline-go-tun2socks.git -[submodule "client/3rd/qzxing"] - path = client/3rd/qzxing - url = https://github.com/ftylitak/qzxing.git [submodule "client/3rd/CocoaAsyncSocket"] path = client/3rd/CocoaAsyncSocket url = https://github.com/robbiehanson/CocoaAsyncSocket.git diff --git a/client/3rd/qrcodegen/qrcodegen.cpp b/client/3rd/qrcodegen/qrcodegen.cpp new file mode 100644 index 00000000..21fffba9 --- /dev/null +++ b/client/3rd/qrcodegen/qrcodegen.cpp @@ -0,0 +1,856 @@ +/* + * QR Code generator library (C++) + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "qrcodegen.hpp" + +using std::int8_t; +using std::uint8_t; +using std::size_t; +using std::vector; + + +namespace qrcodegen { + +/*---- Class QrSegment ----*/ + +QrSegment::Mode::Mode(int mode, int cc0, int cc1, int cc2) : + modeBits(mode) { + numBitsCharCount[0] = cc0; + numBitsCharCount[1] = cc1; + numBitsCharCount[2] = cc2; +} + + +int QrSegment::Mode::getModeBits() const { + return modeBits; +} + + +int QrSegment::Mode::numCharCountBits(int ver) const { + return numBitsCharCount[(ver + 7) / 17]; +} + + +const QrSegment::Mode QrSegment::Mode::NUMERIC (0x1, 10, 12, 14); +const QrSegment::Mode QrSegment::Mode::ALPHANUMERIC(0x2, 9, 11, 13); +const QrSegment::Mode QrSegment::Mode::BYTE (0x4, 8, 16, 16); +const QrSegment::Mode QrSegment::Mode::KANJI (0x8, 8, 10, 12); +const QrSegment::Mode QrSegment::Mode::ECI (0x7, 0, 0, 0); + + +QrSegment QrSegment::makeBytes(const vector &data) { + if (data.size() > static_cast(INT_MAX)) + throw std::length_error("Data too long"); + BitBuffer bb; + for (uint8_t b : data) + bb.appendBits(b, 8); + return QrSegment(Mode::BYTE, static_cast(data.size()), std::move(bb)); +} + + +QrSegment QrSegment::makeNumeric(const char *digits) { + BitBuffer bb; + int accumData = 0; + int accumCount = 0; + int charCount = 0; + for (; *digits != '\0'; digits++, charCount++) { + char c = *digits; + if (c < '0' || c > '9') + throw std::domain_error("String contains non-numeric characters"); + accumData = accumData * 10 + (c - '0'); + accumCount++; + if (accumCount == 3) { + bb.appendBits(static_cast(accumData), 10); + accumData = 0; + accumCount = 0; + } + } + if (accumCount > 0) // 1 or 2 digits remaining + bb.appendBits(static_cast(accumData), accumCount * 3 + 1); + return QrSegment(Mode::NUMERIC, charCount, std::move(bb)); +} + + +QrSegment QrSegment::makeAlphanumeric(const char *text) { + BitBuffer bb; + int accumData = 0; + int accumCount = 0; + int charCount = 0; + for (; *text != '\0'; text++, charCount++) { + const char *temp = std::strchr(ALPHANUMERIC_CHARSET, *text); + if (temp == nullptr) + throw std::domain_error("String contains unencodable characters in alphanumeric mode"); + accumData = accumData * 45 + static_cast(temp - ALPHANUMERIC_CHARSET); + accumCount++; + if (accumCount == 2) { + bb.appendBits(static_cast(accumData), 11); + accumData = 0; + accumCount = 0; + } + } + if (accumCount > 0) // 1 character remaining + bb.appendBits(static_cast(accumData), 6); + return QrSegment(Mode::ALPHANUMERIC, charCount, std::move(bb)); +} + + +vector QrSegment::makeSegments(const char *text) { + // Select the most efficient segment encoding automatically + vector result; + if (*text == '\0'); // Leave result empty + else if (isNumeric(text)) + result.push_back(makeNumeric(text)); + else if (isAlphanumeric(text)) + result.push_back(makeAlphanumeric(text)); + else { + vector bytes; + for (; *text != '\0'; text++) + bytes.push_back(static_cast(*text)); + result.push_back(makeBytes(bytes)); + } + return result; +} + + +QrSegment QrSegment::makeEci(long assignVal) { + BitBuffer bb; + if (assignVal < 0) + throw std::domain_error("ECI assignment value out of range"); + else if (assignVal < (1 << 7)) + bb.appendBits(static_cast(assignVal), 8); + else if (assignVal < (1 << 14)) { + bb.appendBits(2, 2); + bb.appendBits(static_cast(assignVal), 14); + } else if (assignVal < 1000000L) { + bb.appendBits(6, 3); + bb.appendBits(static_cast(assignVal), 21); + } else + throw std::domain_error("ECI assignment value out of range"); + return QrSegment(Mode::ECI, 0, std::move(bb)); +} + + +QrSegment::QrSegment(const Mode &md, int numCh, const std::vector &dt) : + mode(&md), + numChars(numCh), + data(dt) { + if (numCh < 0) + throw std::domain_error("Invalid value"); +} + + +QrSegment::QrSegment(const Mode &md, int numCh, std::vector &&dt) : + mode(&md), + numChars(numCh), + data(std::move(dt)) { + if (numCh < 0) + throw std::domain_error("Invalid value"); +} + + +int QrSegment::getTotalBits(const vector &segs, int version) { + int result = 0; + for (const QrSegment &seg : segs) { + int ccbits = seg.mode->numCharCountBits(version); + if (seg.numChars >= (1L << ccbits)) + return -1; // The segment's length doesn't fit the field's bit width + if (4 + ccbits > INT_MAX - result) + return -1; // The sum will overflow an int type + result += 4 + ccbits; + if (seg.data.size() > static_cast(INT_MAX - result)) + return -1; // The sum will overflow an int type + result += static_cast(seg.data.size()); + } + return result; +} + + +bool QrSegment::isNumeric(const char *text) { + for (; *text != '\0'; text++) { + char c = *text; + if (c < '0' || c > '9') + return false; + } + return true; +} + + +bool QrSegment::isAlphanumeric(const char *text) { + for (; *text != '\0'; text++) { + if (std::strchr(ALPHANUMERIC_CHARSET, *text) == nullptr) + return false; + } + return true; +} + + +const QrSegment::Mode &QrSegment::getMode() const { + return *mode; +} + + +int QrSegment::getNumChars() const { + return numChars; +} + + +const std::vector &QrSegment::getData() const { + return data; +} + + +const char *QrSegment::ALPHANUMERIC_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"; + + + +/*---- Class QrCode ----*/ + +int QrCode::getFormatBits(Ecc ecl) { + switch (ecl) { + case Ecc::LOW : return 1; + case Ecc::MEDIUM : return 0; + case Ecc::QUARTILE: return 3; + case Ecc::HIGH : return 2; + default: throw std::logic_error("Unreachable"); + } +} + + +QrCode QrCode::encodeText(const char *text, Ecc ecl) { + vector segs = QrSegment::makeSegments(text); + return encodeSegments(segs, ecl); +} + + +QrCode QrCode::encodeBinary(const vector &data, Ecc ecl) { + vector segs{QrSegment::makeBytes(data)}; + return encodeSegments(segs, ecl); +} + + +QrCode QrCode::encodeSegments(const vector &segs, Ecc ecl, + int minVersion, int maxVersion, int mask, bool boostEcl) { + if (!(MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= MAX_VERSION) || mask < -1 || mask > 7) + throw std::invalid_argument("Invalid value"); + + // Find the minimal version number to use + int version, dataUsedBits; + for (version = minVersion; ; version++) { + int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; // Number of data bits available + dataUsedBits = QrSegment::getTotalBits(segs, version); + if (dataUsedBits != -1 && dataUsedBits <= dataCapacityBits) + break; // This version number is found to be suitable + if (version >= maxVersion) { // All versions in the range could not fit the given data + std::ostringstream sb; + if (dataUsedBits == -1) + sb << "Segment too long"; + else { + sb << "Data length = " << dataUsedBits << " bits, "; + sb << "Max capacity = " << dataCapacityBits << " bits"; + } + throw data_too_long(sb.str()); + } + } + assert(dataUsedBits != -1); + + // Increase the error correction level while the data still fits in the current version number + for (Ecc newEcl : {Ecc::MEDIUM, Ecc::QUARTILE, Ecc::HIGH}) { // From low to high + if (boostEcl && dataUsedBits <= getNumDataCodewords(version, newEcl) * 8) + ecl = newEcl; + } + + // Concatenate all segments to create the data bit string + BitBuffer bb; + for (const QrSegment &seg : segs) { + bb.appendBits(static_cast(seg.getMode().getModeBits()), 4); + bb.appendBits(static_cast(seg.getNumChars()), seg.getMode().numCharCountBits(version)); + bb.insert(bb.end(), seg.getData().begin(), seg.getData().end()); + } + assert(bb.size() == static_cast(dataUsedBits)); + + // Add terminator and pad up to a byte if applicable + size_t dataCapacityBits = static_cast(getNumDataCodewords(version, ecl)) * 8; + assert(bb.size() <= dataCapacityBits); + bb.appendBits(0, std::min(4, static_cast(dataCapacityBits - bb.size()))); + bb.appendBits(0, (8 - static_cast(bb.size() % 8)) % 8); + assert(bb.size() % 8 == 0); + + // Pad with alternating bytes until data capacity is reached + for (uint8_t padByte = 0xEC; bb.size() < dataCapacityBits; padByte ^= 0xEC ^ 0x11) + bb.appendBits(padByte, 8); + + // Pack bits into bytes in big endian + vector dataCodewords(bb.size() / 8); + for (size_t i = 0; i < bb.size(); i++) + dataCodewords.at(i >> 3) |= (bb.at(i) ? 1 : 0) << (7 - (i & 7)); + + // Create the QR Code object + return QrCode(version, ecl, dataCodewords, mask); +} + + +QrCode::QrCode(int ver, Ecc ecl, const vector &dataCodewords, int msk) : + // Initialize fields and check arguments + version(ver), + errorCorrectionLevel(ecl) { + if (ver < MIN_VERSION || ver > MAX_VERSION) + throw std::domain_error("Version value out of range"); + if (msk < -1 || msk > 7) + throw std::domain_error("Mask value out of range"); + size = ver * 4 + 17; + size_t sz = static_cast(size); + modules = vector >(sz, vector(sz)); // Initially all light + isFunction = vector >(sz, vector(sz)); + + // Compute ECC, draw modules + drawFunctionPatterns(); + const vector allCodewords = addEccAndInterleave(dataCodewords); + drawCodewords(allCodewords); + + // Do masking + if (msk == -1) { // Automatically choose best mask + long minPenalty = LONG_MAX; + for (int i = 0; i < 8; i++) { + applyMask(i); + drawFormatBits(i); + long penalty = getPenaltyScore(); + if (penalty < minPenalty) { + msk = i; + minPenalty = penalty; + } + applyMask(i); // Undoes the mask due to XOR + } + } + assert(0 <= msk && msk <= 7); + mask = msk; + applyMask(msk); // Apply the final choice of mask + drawFormatBits(msk); // Overwrite old format bits + + isFunction.clear(); + isFunction.shrink_to_fit(); +} + + +int QrCode::getVersion() const { + return version; +} + + +int QrCode::getSize() const { + return size; +} + + +QrCode::Ecc QrCode::getErrorCorrectionLevel() const { + return errorCorrectionLevel; +} + + +int QrCode::getMask() const { + return mask; +} + + +bool QrCode::getModule(int x, int y) const { + return 0 <= x && x < size && 0 <= y && y < size && module(x, y); +} + + +void QrCode::drawFunctionPatterns() { + // Draw horizontal and vertical timing patterns + for (int i = 0; i < size; i++) { + setFunctionModule(6, i, i % 2 == 0); + setFunctionModule(i, 6, i % 2 == 0); + } + + // Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules) + drawFinderPattern(3, 3); + drawFinderPattern(size - 4, 3); + drawFinderPattern(3, size - 4); + + // Draw numerous alignment patterns + const vector alignPatPos = getAlignmentPatternPositions(); + size_t numAlign = alignPatPos.size(); + for (size_t i = 0; i < numAlign; i++) { + for (size_t j = 0; j < numAlign; j++) { + // Don't draw on the three finder corners + if (!((i == 0 && j == 0) || (i == 0 && j == numAlign - 1) || (i == numAlign - 1 && j == 0))) + drawAlignmentPattern(alignPatPos.at(i), alignPatPos.at(j)); + } + } + + // Draw configuration data + drawFormatBits(0); // Dummy mask value; overwritten later in the constructor + drawVersion(); +} + + +void QrCode::drawFormatBits(int msk) { + // Calculate error correction code and pack bits + int data = getFormatBits(errorCorrectionLevel) << 3 | msk; // errCorrLvl is uint2, msk is uint3 + int rem = data; + for (int i = 0; i < 10; i++) + rem = (rem << 1) ^ ((rem >> 9) * 0x537); + int bits = (data << 10 | rem) ^ 0x5412; // uint15 + assert(bits >> 15 == 0); + + // Draw first copy + for (int i = 0; i <= 5; i++) + setFunctionModule(8, i, getBit(bits, i)); + setFunctionModule(8, 7, getBit(bits, 6)); + setFunctionModule(8, 8, getBit(bits, 7)); + setFunctionModule(7, 8, getBit(bits, 8)); + for (int i = 9; i < 15; i++) + setFunctionModule(14 - i, 8, getBit(bits, i)); + + // Draw second copy + for (int i = 0; i < 8; i++) + setFunctionModule(size - 1 - i, 8, getBit(bits, i)); + for (int i = 8; i < 15; i++) + setFunctionModule(8, size - 15 + i, getBit(bits, i)); + setFunctionModule(8, size - 8, true); // Always dark +} + + +void QrCode::drawVersion() { + if (version < 7) + return; + + // Calculate error correction code and pack bits + int rem = version; // version is uint6, in the range [7, 40] + for (int i = 0; i < 12; i++) + rem = (rem << 1) ^ ((rem >> 11) * 0x1F25); + long bits = static_cast(version) << 12 | rem; // uint18 + assert(bits >> 18 == 0); + + // Draw two copies + for (int i = 0; i < 18; i++) { + bool bit = getBit(bits, i); + int a = size - 11 + i % 3; + int b = i / 3; + setFunctionModule(a, b, bit); + setFunctionModule(b, a, bit); + } +} + + +void QrCode::drawFinderPattern(int x, int y) { + for (int dy = -4; dy <= 4; dy++) { + for (int dx = -4; dx <= 4; dx++) { + int dist = std::max(std::abs(dx), std::abs(dy)); // Chebyshev/infinity norm + int xx = x + dx, yy = y + dy; + if (0 <= xx && xx < size && 0 <= yy && yy < size) + setFunctionModule(xx, yy, dist != 2 && dist != 4); + } + } +} + + +void QrCode::drawAlignmentPattern(int x, int y) { + for (int dy = -2; dy <= 2; dy++) { + for (int dx = -2; dx <= 2; dx++) + setFunctionModule(x + dx, y + dy, std::max(std::abs(dx), std::abs(dy)) != 1); + } +} + + +void QrCode::setFunctionModule(int x, int y, bool isDark) { + size_t ux = static_cast(x); + size_t uy = static_cast(y); + modules .at(uy).at(ux) = isDark; + isFunction.at(uy).at(ux) = true; +} + + +bool QrCode::module(int x, int y) const { + return modules.at(static_cast(y)).at(static_cast(x)); +} + + +vector QrCode::addEccAndInterleave(const vector &data) const { + if (data.size() != static_cast(getNumDataCodewords(version, errorCorrectionLevel))) + throw std::invalid_argument("Invalid argument"); + + // Calculate parameter numbers + int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[static_cast(errorCorrectionLevel)][version]; + int blockEccLen = ECC_CODEWORDS_PER_BLOCK [static_cast(errorCorrectionLevel)][version]; + int rawCodewords = getNumRawDataModules(version) / 8; + int numShortBlocks = numBlocks - rawCodewords % numBlocks; + int shortBlockLen = rawCodewords / numBlocks; + + // Split data into blocks and append ECC to each block + vector > blocks; + const vector rsDiv = reedSolomonComputeDivisor(blockEccLen); + for (int i = 0, k = 0; i < numBlocks; i++) { + vector dat(data.cbegin() + k, data.cbegin() + (k + shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1))); + k += static_cast(dat.size()); + const vector ecc = reedSolomonComputeRemainder(dat, rsDiv); + if (i < numShortBlocks) + dat.push_back(0); + dat.insert(dat.end(), ecc.cbegin(), ecc.cend()); + blocks.push_back(std::move(dat)); + } + + // Interleave (not concatenate) the bytes from every block into a single sequence + vector result; + for (size_t i = 0; i < blocks.at(0).size(); i++) { + for (size_t j = 0; j < blocks.size(); j++) { + // Skip the padding byte in short blocks + if (i != static_cast(shortBlockLen - blockEccLen) || j >= static_cast(numShortBlocks)) + result.push_back(blocks.at(j).at(i)); + } + } + assert(result.size() == static_cast(rawCodewords)); + return result; +} + + +void QrCode::drawCodewords(const vector &data) { + if (data.size() != static_cast(getNumRawDataModules(version) / 8)) + throw std::invalid_argument("Invalid argument"); + + size_t i = 0; // Bit index into the data + // Do the funny zigzag scan + for (int right = size - 1; right >= 1; right -= 2) { // Index of right column in each column pair + if (right == 6) + right = 5; + for (int vert = 0; vert < size; vert++) { // Vertical counter + for (int j = 0; j < 2; j++) { + size_t x = static_cast(right - j); // Actual x coordinate + bool upward = ((right + 1) & 2) == 0; + size_t y = static_cast(upward ? size - 1 - vert : vert); // Actual y coordinate + if (!isFunction.at(y).at(x) && i < data.size() * 8) { + modules.at(y).at(x) = getBit(data.at(i >> 3), 7 - static_cast(i & 7)); + i++; + } + // If this QR Code has any remainder bits (0 to 7), they were assigned as + // 0/false/light by the constructor and are left unchanged by this method + } + } + } + assert(i == data.size() * 8); +} + + +void QrCode::applyMask(int msk) { + if (msk < 0 || msk > 7) + throw std::domain_error("Mask value out of range"); + size_t sz = static_cast(size); + for (size_t y = 0; y < sz; y++) { + for (size_t x = 0; x < sz; x++) { + bool invert; + switch (msk) { + case 0: invert = (x + y) % 2 == 0; break; + case 1: invert = y % 2 == 0; break; + case 2: invert = x % 3 == 0; break; + case 3: invert = (x + y) % 3 == 0; break; + case 4: invert = (x / 3 + y / 2) % 2 == 0; break; + case 5: invert = x * y % 2 + x * y % 3 == 0; break; + case 6: invert = (x * y % 2 + x * y % 3) % 2 == 0; break; + case 7: invert = ((x + y) % 2 + x * y % 3) % 2 == 0; break; + default: throw std::logic_error("Unreachable"); + } + modules.at(y).at(x) = modules.at(y).at(x) ^ (invert & !isFunction.at(y).at(x)); + } + } +} + + +long QrCode::getPenaltyScore() const { + long result = 0; + + // Adjacent modules in row having same color, and finder-like patterns + for (int y = 0; y < size; y++) { + bool runColor = false; + int runX = 0; + std::array runHistory = {}; + for (int x = 0; x < size; x++) { + if (module(x, y) == runColor) { + runX++; + if (runX == 5) + result += PENALTY_N1; + else if (runX > 5) + result++; + } else { + finderPenaltyAddHistory(runX, runHistory); + if (!runColor) + result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3; + runColor = module(x, y); + runX = 1; + } + } + result += finderPenaltyTerminateAndCount(runColor, runX, runHistory) * PENALTY_N3; + } + // Adjacent modules in column having same color, and finder-like patterns + for (int x = 0; x < size; x++) { + bool runColor = false; + int runY = 0; + std::array runHistory = {}; + for (int y = 0; y < size; y++) { + if (module(x, y) == runColor) { + runY++; + if (runY == 5) + result += PENALTY_N1; + else if (runY > 5) + result++; + } else { + finderPenaltyAddHistory(runY, runHistory); + if (!runColor) + result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3; + runColor = module(x, y); + runY = 1; + } + } + result += finderPenaltyTerminateAndCount(runColor, runY, runHistory) * PENALTY_N3; + } + + // 2*2 blocks of modules having same color + for (int y = 0; y < size - 1; y++) { + for (int x = 0; x < size - 1; x++) { + bool color = module(x, y); + if ( color == module(x + 1, y) && + color == module(x, y + 1) && + color == module(x + 1, y + 1)) + result += PENALTY_N2; + } + } + + // Balance of dark and light modules + int dark = 0; + for (const vector &row : modules) { + for (bool color : row) { + if (color) + dark++; + } + } + int total = size * size; // Note that size is odd, so dark/total != 1/2 + // Compute the smallest integer k >= 0 such that (45-5k)% <= dark/total <= (55+5k)% + int k = static_cast((std::abs(dark * 20L - total * 10L) + total - 1) / total) - 1; + assert(0 <= k && k <= 9); + result += k * PENALTY_N4; + assert(0 <= result && result <= 2568888L); // Non-tight upper bound based on default values of PENALTY_N1, ..., N4 + return result; +} + + +vector QrCode::getAlignmentPatternPositions() const { + if (version == 1) + return vector(); + else { + int numAlign = version / 7 + 2; + int step = (version == 32) ? 26 : + (version * 4 + numAlign * 2 + 1) / (numAlign * 2 - 2) * 2; + vector result; + for (int i = 0, pos = size - 7; i < numAlign - 1; i++, pos -= step) + result.insert(result.begin(), pos); + result.insert(result.begin(), 6); + return result; + } +} + + +int QrCode::getNumRawDataModules(int ver) { + if (ver < MIN_VERSION || ver > MAX_VERSION) + throw std::domain_error("Version number out of range"); + int result = (16 * ver + 128) * ver + 64; + if (ver >= 2) { + int numAlign = ver / 7 + 2; + result -= (25 * numAlign - 10) * numAlign - 55; + if (ver >= 7) + result -= 36; + } + assert(208 <= result && result <= 29648); + return result; +} + + +int QrCode::getNumDataCodewords(int ver, Ecc ecl) { + return getNumRawDataModules(ver) / 8 + - ECC_CODEWORDS_PER_BLOCK [static_cast(ecl)][ver] + * NUM_ERROR_CORRECTION_BLOCKS[static_cast(ecl)][ver]; +} + + +vector QrCode::reedSolomonComputeDivisor(int degree) { + if (degree < 1 || degree > 255) + throw std::domain_error("Degree out of range"); + // Polynomial coefficients are stored from highest to lowest power, excluding the leading term which is always 1. + // For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}. + vector result(static_cast(degree)); + result.at(result.size() - 1) = 1; // Start off with the monomial x^0 + + // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), + // and drop the highest monomial term which is always 1x^degree. + // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). + uint8_t root = 1; + for (int i = 0; i < degree; i++) { + // Multiply the current product by (x - r^i) + for (size_t j = 0; j < result.size(); j++) { + result.at(j) = reedSolomonMultiply(result.at(j), root); + if (j + 1 < result.size()) + result.at(j) ^= result.at(j + 1); + } + root = reedSolomonMultiply(root, 0x02); + } + return result; +} + + +vector QrCode::reedSolomonComputeRemainder(const vector &data, const vector &divisor) { + vector result(divisor.size()); + for (uint8_t b : data) { // Polynomial division + uint8_t factor = b ^ result.at(0); + result.erase(result.begin()); + result.push_back(0); + for (size_t i = 0; i < result.size(); i++) + result.at(i) ^= reedSolomonMultiply(divisor.at(i), factor); + } + return result; +} + + +uint8_t QrCode::reedSolomonMultiply(uint8_t x, uint8_t y) { + // Russian peasant multiplication + int z = 0; + for (int i = 7; i >= 0; i--) { + z = (z << 1) ^ ((z >> 7) * 0x11D); + z ^= ((y >> i) & 1) * x; + } + assert(z >> 8 == 0); + return static_cast(z); +} + + +int QrCode::finderPenaltyCountPatterns(const std::array &runHistory) const { + int n = runHistory.at(1); + assert(n <= size * 3); + bool core = n > 0 && runHistory.at(2) == n && runHistory.at(3) == n * 3 && runHistory.at(4) == n && runHistory.at(5) == n; + return (core && runHistory.at(0) >= n * 4 && runHistory.at(6) >= n ? 1 : 0) + + (core && runHistory.at(6) >= n * 4 && runHistory.at(0) >= n ? 1 : 0); +} + + +int QrCode::finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLength, std::array &runHistory) const { + if (currentRunColor) { // Terminate dark run + finderPenaltyAddHistory(currentRunLength, runHistory); + currentRunLength = 0; + } + currentRunLength += size; // Add light border to final run + finderPenaltyAddHistory(currentRunLength, runHistory); + return finderPenaltyCountPatterns(runHistory); +} + + +void QrCode::finderPenaltyAddHistory(int currentRunLength, std::array &runHistory) const { + if (runHistory.at(0) == 0) + currentRunLength += size; // Add light border to initial run + std::copy_backward(runHistory.cbegin(), runHistory.cend() - 1, runHistory.end()); + runHistory.at(0) = currentRunLength; +} + + +bool QrCode::getBit(long x, int i) { + return ((x >> i) & 1) != 0; +} + + +/*---- Tables of constants ----*/ + +const int QrCode::PENALTY_N1 = 3; +const int QrCode::PENALTY_N2 = 3; +const int QrCode::PENALTY_N3 = 40; +const int QrCode::PENALTY_N4 = 10; + + +const int8_t QrCode::ECC_CODEWORDS_PER_BLOCK[4][41] = { + // Version: (note that index 0 is for padding, and is set to an illegal value) + //0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level + {-1, 7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30, 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // Low + {-1, 10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 24, 24, 28, 28, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, // Medium + {-1, 13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26, 30, 28, 30, 30, 30, 30, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // Quartile + {-1, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28, 30, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // High +}; + +const int8_t QrCode::NUM_ERROR_CORRECTION_BLOCKS[4][41] = { + // Version: (note that index 0 is for padding, and is set to an illegal value) + //0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level + {-1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25}, // Low + {-1, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49}, // Medium + {-1, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68}, // Quartile + {-1, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81}, // High +}; + + +data_too_long::data_too_long(const std::string &msg) : + std::length_error(msg) {} + + + +/*---- Class BitBuffer ----*/ + +BitBuffer::BitBuffer() + : std::vector() {} + + +void BitBuffer::appendBits(std::uint32_t val, int len) { + if (len < 0 || len > 31 || val >> len != 0) + throw std::domain_error("Value out of range"); + for (int i = len - 1; i >= 0; i--) // Append bit by bit + this->push_back(((val >> i) & 1) != 0); +} + +std::string toSvgString(const QrCode &qr, int border) { + if (border < 0) + throw std::domain_error("Border must be non-negative"); + if (border > INT_MAX / 2 || border * 2 > INT_MAX - qr.getSize()) + throw std::overflow_error("Border too large"); + + std::ostringstream sb; + sb << "\n"; + sb << "\n"; + sb << "\n"; + sb << "\t\n"; + sb << "\t\n"; + sb << "\n"; + return sb.str(); +} +} diff --git a/client/3rd/qrcodegen/qrcodegen.hpp b/client/3rd/qrcodegen/qrcodegen.hpp new file mode 100644 index 00000000..f355edc7 --- /dev/null +++ b/client/3rd/qrcodegen/qrcodegen.hpp @@ -0,0 +1,551 @@ +/* + * QR Code generator library (C++) + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * 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. + */ + +#pragma once + +#include +#include +#include +#include +#include + + +namespace qrcodegen { + +/* + * A segment of character/binary/control data in a QR Code symbol. + * Instances of this class are immutable. + * The mid-level way to create a segment is to take the payload data + * and call a static factory function such as QrSegment::makeNumeric(). + * The low-level way to create a segment is to custom-make the bit buffer + * and call the QrSegment() constructor with appropriate values. + * This segment class imposes no length restrictions, but QR Codes have restrictions. + * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. + * Any segment longer than this is meaningless for the purpose of generating QR Codes. + */ +class QrSegment final { + + /*---- Public helper enumeration ----*/ + + /* + * Describes how a segment's data bits are interpreted. Immutable. + */ + public: class Mode final { + + /*-- Constants --*/ + + public: static const Mode NUMERIC; + public: static const Mode ALPHANUMERIC; + public: static const Mode BYTE; + public: static const Mode KANJI; + public: static const Mode ECI; + + + /*-- Fields --*/ + + // The mode indicator bits, which is a uint4 value (range 0 to 15). + private: int modeBits; + + // Number of character count bits for three different version ranges. + private: int numBitsCharCount[3]; + + + /*-- Constructor --*/ + + private: Mode(int mode, int cc0, int cc1, int cc2); + + + /*-- Methods --*/ + + /* + * (Package-private) Returns the mode indicator bits, which is an unsigned 4-bit value (range 0 to 15). + */ + public: int getModeBits() const; + + /* + * (Package-private) Returns the bit width of the character count field for a segment in + * this mode in a QR Code at the given version number. The result is in the range [0, 16]. + */ + public: int numCharCountBits(int ver) const; + + }; + + + + /*---- Static factory functions (mid level) ----*/ + + /* + * Returns a segment representing the given binary data encoded in + * byte mode. All input byte vectors are acceptable. Any text string + * can be converted to UTF-8 bytes and encoded as a byte mode segment. + */ + public: static QrSegment makeBytes(const std::vector &data); + + + /* + * Returns a segment representing the given string of decimal digits encoded in numeric mode. + */ + public: static QrSegment makeNumeric(const char *digits); + + + /* + * Returns a segment representing the given text string encoded in alphanumeric mode. + * The characters allowed are: 0 to 9, A to Z (uppercase only), space, + * dollar, percent, asterisk, plus, hyphen, period, slash, colon. + */ + public: static QrSegment makeAlphanumeric(const char *text); + + + /* + * Returns a list of zero or more segments to represent the given text string. The result + * may use various segment modes and switch modes to optimize the length of the bit stream. + */ + public: static std::vector makeSegments(const char *text); + + + /* + * Returns a segment representing an Extended Channel Interpretation + * (ECI) designator with the given assignment value. + */ + public: static QrSegment makeEci(long assignVal); + + + /*---- Public static helper functions ----*/ + + /* + * Tests whether the given string can be encoded as a segment in numeric mode. + * A string is encodable iff each character is in the range 0 to 9. + */ + public: static bool isNumeric(const char *text); + + + /* + * Tests whether the given string can be encoded as a segment in alphanumeric mode. + * A string is encodable iff each character is in the following set: 0 to 9, A to Z + * (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon. + */ + public: static bool isAlphanumeric(const char *text); + + + + /*---- Instance fields ----*/ + + /* The mode indicator of this segment. Accessed through getMode(). */ + private: const Mode *mode; + + /* The length of this segment's unencoded data. Measured in characters for + * numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode. + * Always zero or positive. Not the same as the data's bit length. + * Accessed through getNumChars(). */ + private: int numChars; + + /* The data bits of this segment. Accessed through getData(). */ + private: std::vector data; + + + /*---- Constructors (low level) ----*/ + + /* + * Creates a new QR Code segment with the given attributes and data. + * The character count (numCh) must agree with the mode and the bit buffer length, + * but the constraint isn't checked. The given bit buffer is copied and stored. + */ + public: QrSegment(const Mode &md, int numCh, const std::vector &dt); + + + /* + * Creates a new QR Code segment with the given parameters and data. + * The character count (numCh) must agree with the mode and the bit buffer length, + * but the constraint isn't checked. The given bit buffer is moved and stored. + */ + public: QrSegment(const Mode &md, int numCh, std::vector &&dt); + + + /*---- Methods ----*/ + + /* + * Returns the mode field of this segment. + */ + public: const Mode &getMode() const; + + + /* + * Returns the character count field of this segment. + */ + public: int getNumChars() const; + + + /* + * Returns the data bits of this segment. + */ + public: const std::vector &getData() const; + + + // (Package-private) Calculates the number of bits needed to encode the given segments at + // the given version. Returns a non-negative number if successful. Otherwise returns -1 if a + // segment has too many characters to fit its length field, or the total bits exceeds INT_MAX. + public: static int getTotalBits(const std::vector &segs, int version); + + + /*---- Private constant ----*/ + + /* The set of all legal characters in alphanumeric mode, where + * each character value maps to the index in the string. */ + private: static const char *ALPHANUMERIC_CHARSET; + +}; + + + +/* + * A QR Code symbol, which is a type of two-dimension barcode. + * Invented by Denso Wave and described in the ISO/IEC 18004 standard. + * Instances of this class represent an immutable square grid of dark and light cells. + * The class provides static factory functions to create a QR Code from text or binary data. + * The class covers the QR Code Model 2 specification, supporting all versions (sizes) + * from 1 to 40, all 4 error correction levels, and 4 character encoding modes. + * + * Ways to create a QR Code object: + * - High level: Take the payload data and call QrCode::encodeText() or QrCode::encodeBinary(). + * - Mid level: Custom-make the list of segments and call QrCode::encodeSegments(). + * - Low level: Custom-make the array of data codeword bytes (including + * segment headers and final padding, excluding error correction codewords), + * supply the appropriate version number, and call the QrCode() constructor. + * (Note that all ways require supplying the desired error correction level.) + */ +class QrCode final { + + /*---- Public helper enumeration ----*/ + + /* + * The error correction level in a QR Code symbol. + */ + public: enum class Ecc { + LOW = 0 , // The QR Code can tolerate about 7% erroneous codewords + MEDIUM , // The QR Code can tolerate about 15% erroneous codewords + QUARTILE, // The QR Code can tolerate about 25% erroneous codewords + HIGH , // The QR Code can tolerate about 30% erroneous codewords + }; + + + // Returns a value in the range 0 to 3 (unsigned 2-bit integer). + private: static int getFormatBits(Ecc ecl); + + + + /*---- Static factory functions (high level) ----*/ + + /* + * Returns a QR Code representing the given Unicode text string at the given error correction level. + * As a conservative upper bound, this function is guaranteed to succeed for strings that have 2953 or fewer + * UTF-8 code units (not Unicode code points) if the low error correction level is used. The smallest possible + * QR Code version is automatically chosen for the output. The ECC level of the result may be higher than + * the ecl argument if it can be done without increasing the version. + */ + public: static QrCode encodeText(const char *text, Ecc ecl); + + + /* + * Returns a QR Code representing the given binary data at the given error correction level. + * This function always encodes using the binary segment mode, not any text mode. The maximum number of + * bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. + * The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. + */ + public: static QrCode encodeBinary(const std::vector &data, Ecc ecl); + + + /*---- Static factory functions (mid level) ----*/ + + /* + * Returns a QR Code representing the given segments with the given encoding parameters. + * The smallest possible QR Code version within the given range is automatically + * chosen for the output. Iff boostEcl is true, then the ECC level of the result + * may be higher than the ecl argument if it can be done without increasing the + * version. The mask number is either between 0 to 7 (inclusive) to force that + * mask, or -1 to automatically choose an appropriate mask (which may be slow). + * This function allows the user to create a custom sequence of segments that switches + * between modes (such as alphanumeric and byte) to encode text in less space. + * This is a mid-level API; the high-level API is encodeText() and encodeBinary(). + */ + public: static QrCode encodeSegments(const std::vector &segs, Ecc ecl, + int minVersion=1, int maxVersion=40, int mask=-1, bool boostEcl=true); // All optional parameters + + + + /*---- Instance fields ----*/ + + // Immutable scalar parameters: + + /* The version number of this QR Code, which is between 1 and 40 (inclusive). + * This determines the size of this barcode. */ + private: int version; + + /* The width and height of this QR Code, measured in modules, between + * 21 and 177 (inclusive). This is equal to version * 4 + 17. */ + private: int size; + + /* The error correction level used in this QR Code. */ + private: Ecc errorCorrectionLevel; + + /* The index of the mask pattern used in this QR Code, which is between 0 and 7 (inclusive). + * Even if a QR Code is created with automatic masking requested (mask = -1), + * the resulting object still has a mask value between 0 and 7. */ + private: int mask; + + // Private grids of modules/pixels, with dimensions of size*size: + + // The modules of this QR Code (false = light, true = dark). + // Immutable after constructor finishes. Accessed through getModule(). + private: std::vector > modules; + + // Indicates function modules that are not subjected to masking. Discarded when constructor finishes. + private: std::vector > isFunction; + + + + /*---- Constructor (low level) ----*/ + + /* + * Creates a new QR Code with the given version number, + * error correction level, data codeword bytes, and mask number. + * This is a low-level API that most users should not use directly. + * A mid-level API is the encodeSegments() function. + */ + public: QrCode(int ver, Ecc ecl, const std::vector &dataCodewords, int msk); + + + + /*---- Public instance methods ----*/ + + /* + * Returns this QR Code's version, in the range [1, 40]. + */ + public: int getVersion() const; + + + /* + * Returns this QR Code's size, in the range [21, 177]. + */ + public: int getSize() const; + + + /* + * Returns this QR Code's error correction level. + */ + public: Ecc getErrorCorrectionLevel() const; + + + /* + * Returns this QR Code's mask, in the range [0, 7]. + */ + public: int getMask() const; + + + /* + * Returns the color of the module (pixel) at the given coordinates, which is false + * for light or true for dark. The top left corner has the coordinates (x=0, y=0). + * If the given coordinates are out of bounds, then false (light) is returned. + */ + public: bool getModule(int x, int y) const; + + + + /*---- Private helper methods for constructor: Drawing function modules ----*/ + + // Reads this object's version field, and draws and marks all function modules. + private: void drawFunctionPatterns(); + + + // Draws two copies of the format bits (with its own error correction code) + // based on the given mask and this object's error correction level field. + private: void drawFormatBits(int msk); + + + // Draws two copies of the version bits (with its own error correction code), + // based on this object's version field, iff 7 <= version <= 40. + private: void drawVersion(); + + + // Draws a 9*9 finder pattern including the border separator, + // with the center module at (x, y). Modules can be out of bounds. + private: void drawFinderPattern(int x, int y); + + + // Draws a 5*5 alignment pattern, with the center module + // at (x, y). All modules must be in bounds. + private: void drawAlignmentPattern(int x, int y); + + + // Sets the color of a module and marks it as a function module. + // Only used by the constructor. Coordinates must be in bounds. + private: void setFunctionModule(int x, int y, bool isDark); + + + // Returns the color of the module at the given coordinates, which must be in range. + private: bool module(int x, int y) const; + + + /*---- Private helper methods for constructor: Codewords and masking ----*/ + + // Returns a new byte string representing the given data with the appropriate error correction + // codewords appended to it, based on this object's version and error correction level. + private: std::vector addEccAndInterleave(const std::vector &data) const; + + + // Draws the given sequence of 8-bit codewords (data and error correction) onto the entire + // data area of this QR Code. Function modules need to be marked off before this is called. + private: void drawCodewords(const std::vector &data); + + + // XORs the codeword modules in this QR Code with the given mask pattern. + // The function modules must be marked and the codeword bits must be drawn + // before masking. Due to the arithmetic of XOR, calling applyMask() with + // the same mask value a second time will undo the mask. A final well-formed + // QR Code needs exactly one (not zero, two, etc.) mask applied. + private: void applyMask(int msk); + + + // Calculates and returns the penalty score based on state of this QR Code's current modules. + // This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score. + private: long getPenaltyScore() const; + + + + /*---- Private helper functions ----*/ + + // Returns an ascending list of positions of alignment patterns for this version number. + // Each position is in the range [0,177), and are used on both the x and y axes. + // This could be implemented as lookup table of 40 variable-length lists of unsigned bytes. + private: std::vector getAlignmentPatternPositions() const; + + + // Returns the number of data bits that can be stored in a QR Code of the given version number, after + // all function modules are excluded. This includes remainder bits, so it might not be a multiple of 8. + // The result is in the range [208, 29648]. This could be implemented as a 40-entry lookup table. + private: static int getNumRawDataModules(int ver); + + + // Returns the number of 8-bit data (i.e. not error correction) codewords contained in any + // QR Code of the given version number and error correction level, with remainder bits discarded. + // This stateless pure function could be implemented as a (40*4)-cell lookup table. + private: static int getNumDataCodewords(int ver, Ecc ecl); + + + // Returns a Reed-Solomon ECC generator polynomial for the given degree. This could be + // implemented as a lookup table over all possible parameter values, instead of as an algorithm. + private: static std::vector reedSolomonComputeDivisor(int degree); + + + // Returns the Reed-Solomon error correction codeword for the given data and divisor polynomials. + private: static std::vector reedSolomonComputeRemainder(const std::vector &data, const std::vector &divisor); + + + // Returns the product of the two given field elements modulo GF(2^8/0x11D). + // All inputs are valid. This could be implemented as a 256*256 lookup table. + private: static std::uint8_t reedSolomonMultiply(std::uint8_t x, std::uint8_t y); + + + // Can only be called immediately after a light run is added, and + // returns either 0, 1, or 2. A helper function for getPenaltyScore(). + private: int finderPenaltyCountPatterns(const std::array &runHistory) const; + + + // Must be called at the end of a line (row or column) of modules. A helper function for getPenaltyScore(). + private: int finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLength, std::array &runHistory) const; + + + // Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore(). + private: void finderPenaltyAddHistory(int currentRunLength, std::array &runHistory) const; + + + // Returns true iff the i'th bit of x is set to 1. + private: static bool getBit(long x, int i); + + + /*---- Constants and tables ----*/ + + // The minimum version number supported in the QR Code Model 2 standard. + public: static constexpr int MIN_VERSION = 1; + + // The maximum version number supported in the QR Code Model 2 standard. + public: static constexpr int MAX_VERSION = 40; + + + // For use in getPenaltyScore(), when evaluating which mask is best. + private: static const int PENALTY_N1; + private: static const int PENALTY_N2; + private: static const int PENALTY_N3; + private: static const int PENALTY_N4; + + + private: static const std::int8_t ECC_CODEWORDS_PER_BLOCK[4][41]; + private: static const std::int8_t NUM_ERROR_CORRECTION_BLOCKS[4][41]; + +}; + + + +/*---- Public exception class ----*/ + +/* + * Thrown when the supplied data does not fit any QR Code version. Ways to handle this exception include: + * - Decrease the error correction level if it was greater than Ecc::LOW. + * - If the encodeSegments() function was called with a maxVersion argument, then increase + * it if it was less than QrCode::MAX_VERSION. (This advice does not apply to the other + * factory functions because they search all versions up to QrCode::MAX_VERSION.) + * - Split the text data into better or optimal segments in order to reduce the number of bits required. + * - Change the text or binary data to be shorter. + * - Change the text to fit the character set of a particular segment mode (e.g. alphanumeric). + * - Propagate the error upward to the caller/user. + */ +class data_too_long : public std::length_error { + + public: explicit data_too_long(const std::string &msg); + +}; + + + +/* + * An appendable sequence of bits (0s and 1s). Mainly used by QrSegment. + */ +class BitBuffer final : public std::vector { + + /*---- Constructor ----*/ + + // Creates an empty bit buffer (length 0). + public: BitBuffer(); + + + + /*---- Method ----*/ + + // Appends the given number of low-order bits of the given value + // to this buffer. Requires 0 <= len <= 31 and val < 2^len. + public: void appendBits(std::uint32_t val, int len); + +}; + +std::string toSvgString(const QrCode &qr, int border); + +} diff --git a/client/3rd/qrcodegen/qrcodegen.pri b/client/3rd/qrcodegen/qrcodegen.pri new file mode 100644 index 00000000..da50cb98 --- /dev/null +++ b/client/3rd/qrcodegen/qrcodegen.pri @@ -0,0 +1,5 @@ +INCLUDEPATH += $$PWD +DEPENDPATH += $$PWD + +HEADERS += $$PWD/qrcodegen.hpp +SOURCES += $$PWD/qrcodegen.cpp diff --git a/client/3rd/qzxing b/client/3rd/qzxing deleted file mode 160000 index 2fd4dd60..00000000 --- a/client/3rd/qzxing +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2fd4dd60c04a29c6d1271fdd9ae25378b8f61ec8 diff --git a/client/amnezia_application.cpp b/client/amnezia_application.cpp index 4420f7dc..334582cb 100644 --- a/client/amnezia_application.cpp +++ b/client/amnezia_application.cpp @@ -6,8 +6,6 @@ #include -#include "QZXing.h" - #include "core/servercontroller.h" #include "debug.h" #include "defines.h" @@ -141,8 +139,6 @@ void AmneziaApplication::init() void AmneziaApplication::registerTypes() { - QZXing::registerQMLTypes(); - qRegisterMetaType("VpnProtocol::VpnConnectionState"); qRegisterMetaType("ServerCredentials"); diff --git a/client/client.pro b/client/client.pro index 9a89842d..14a78e16 100644 --- a/client/client.pro +++ b/client/client.pro @@ -19,7 +19,7 @@ include("3rd/QtSsh/src/ssh/qssh.pri") include("3rd/QtSsh/src/botan/botan.pri") !android:!ios:include("3rd/SingleApplication/singleapplication.pri") include ("3rd/SortFilterProxyModel/SortFilterProxyModel.pri") -include("3rd/qzxing/src/QZXing-components.pri") +include("3rd/qrcodegen/qrcodegen.pri") include("3rd/QSimpleCrypto/QSimpleCrypto.pri") include("3rd/qtkeychain/qtkeychain.pri") diff --git a/client/ui/pages_logic/ShareConnectionLogic.cpp b/client/ui/pages_logic/ShareConnectionLogic.cpp index 6367722a..85849391 100644 --- a/client/ui/pages_logic/ShareConnectionLogic.cpp +++ b/client/ui/pages_logic/ShareConnectionLogic.cpp @@ -1,12 +1,9 @@ #include #include #include -//#include #include -#include "QZXing.h" -#include "QZXingImageProvider.h" -#include "QZXingFilter.h" +#include "qrcodegen.hpp" #include "ShareConnectionLogic.h" @@ -30,6 +27,8 @@ #include #endif +using namespace qrcodegen; + ShareConnectionLogic::ShareConnectionLogic(UiLogic *logic, QObject *parent): PageLogicBase(logic, parent), m_textEditShareOpenVpnCodeText{}, @@ -168,8 +167,10 @@ void ShareConnectionLogic::onPushButtonShareShadowSocksGenerateClicked() ssString = "ss://" + ssString.toUtf8().toBase64(); set_lineEditShareShadowSocksStringText(ssString); - QImage qr = QZXing::encodeData(ssString.toUtf8(), QZXing::EncoderFormat_QR_CODE, QSize(512,512), QZXing::EncodeErrorCorrectionLevel_L); - set_shareShadowSocksQrCodeText(imageToBase64(qr)); + QrCode qr = QrCode::encodeText(ssString.toUtf8(), QrCode::Ecc::LOW); + QString svg = QString::fromStdString(toSvgString(qr, 0)); + + set_shareShadowSocksQrCodeText(svgToBase64(svg)); QString humanString = QString("Server: %3\n" "Port: %4\n" @@ -227,9 +228,10 @@ void ShareConnectionLogic::onPushButtonShareWireGuardGenerateClicked() set_textEditShareWireGuardCodeText(cfg); - QImage qr = QZXing::encodeData(cfg.toUtf8(), QZXing::EncoderFormat_QR_CODE, QSize(512,512), QZXing::EncodeErrorCorrectionLevel_L); + QrCode qr = QrCode::encodeText(cfg.toUtf8(), QrCode::Ecc::LOW); + QString svg = QString::fromStdString(toSvgString(qr, 0)); - set_shareWireGuardQrCodeText(imageToBase64(qr)); + set_shareWireGuardQrCodeText(svgToBase64(svg)); } void ShareConnectionLogic::onPushButtonShareIkev2GenerateClicked() @@ -267,7 +269,7 @@ void ShareConnectionLogic::updateSharingPage(int serverIndex, DockerContainer co QList ShareConnectionLogic::genQrCodeImageSeries(const QByteArray &data) { - double k = 1500; + double k = 850; quint8 chunksCount = std::ceil(data.size() / k); QList chunks; @@ -278,18 +280,15 @@ QList ShareConnectionLogic::genQrCodeImageSeries(const QByteArray &data QByteArray ba = chunk.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals); - QImage qr = QZXing::encodeData(ba, QZXing::EncoderFormat_QR_CODE, QSize(512,512), QZXing::EncodeErrorCorrectionLevel_L); - chunks.append(imageToBase64(qr)); + QrCode qr = QrCode::encodeText(ba, QrCode::Ecc::LOW); + QString svg = QString::fromStdString(toSvgString(qr, 0)); + chunks.append(svgToBase64(svg)); } return chunks; } -QString ShareConnectionLogic::imageToBase64(const QImage &image) +QString ShareConnectionLogic::svgToBase64(const QString &image) { - QByteArray ba; - QBuffer bu(&ba); - bu.open(QIODevice::WriteOnly); - image.save(&bu, "PNG"); - return "data:image/png;base64," + QString::fromLatin1(ba.toBase64().data()); + return "data:image/svg;base64," + QString::fromLatin1(image.toUtf8().toBase64().data()); } diff --git a/client/ui/pages_logic/ShareConnectionLogic.h b/client/ui/pages_logic/ShareConnectionLogic.h index a167b43e..5d46be97 100644 --- a/client/ui/pages_logic/ShareConnectionLogic.h +++ b/client/ui/pages_logic/ShareConnectionLogic.h @@ -49,8 +49,7 @@ public: void updateSharingPage(int serverIndex, DockerContainer container); QList genQrCodeImageSeries(const QByteArray &data); - QString imageToBase64(const QImage &image); - + QString svgToBase64(const QString &image); }; #endif // SHARE_CONNECTION_LOGIC_H From b356522f94c8064be20749cbe30c732cd711ffd2 Mon Sep 17 00:00:00 2001 From: pokamest Date: Fri, 9 Sep 2022 15:01:11 +0300 Subject: [PATCH 15/43] Secure settings crash fix --- client/secure_qsettings.cpp | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/client/secure_qsettings.cpp b/client/secure_qsettings.cpp index a58ae7c4..d038be6b 100644 --- a/client/secure_qsettings.cpp +++ b/client/secure_qsettings.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include "utils.h" #include @@ -222,35 +223,39 @@ QByteArray SecureQSettings::getEncIv() const QByteArray SecureQSettings::getSecTag(const QString &tag) { - ReadPasswordJob job(keyChainName); - job.setAutoDelete(false); - job.setKey(tag); + auto job = QSharedPointer(new ReadPasswordJob(keyChainName), &QObject::deleteLater); + job->setAutoDelete(false); + job->setKey(tag); QEventLoop loop; - job.connect(&job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit())); - job.start(); + job->connect(job.data(), &ReadPasswordJob::finished, job.data(), [&loop](){ + loop.quit(); + }); + job->start(); loop.exec(); - if ( job.error() ) { - qCritical() << "SecureQSettings::getSecTag Error:" << job.errorString(); + if ( job->error() ) { + qCritical() << "SecureQSettings::getSecTag Error:" << job->errorString(); } - return job.binaryData(); + return job->binaryData(); } void SecureQSettings::setSecTag(const QString &tag, const QByteArray &data) { - WritePasswordJob job(keyChainName); - job.setAutoDelete(false); - job.setKey(tag); - job.setBinaryData(data); + auto job = QSharedPointer(new WritePasswordJob(keyChainName), &QObject::deleteLater); + job->setAutoDelete(false); + job->setKey(tag); + job->setBinaryData(data); QEventLoop loop; QTimer::singleShot(1000, &loop, SLOT(quit())); - job.connect(&job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit())); - job.start(); + job->connect(job.data(), &WritePasswordJob::finished, job.data(), [&loop](){ + loop.quit(); + }); + job->start(); loop.exec(); - if (job.error()) { - qCritical() << "SecureQSettings::setSecTag Error:" << job.errorString(); + if (job->error()) { + qCritical() << "SecureQSettings::setSecTag Error:" << job->errorString(); } } From 8cea93de94b20e9b47671e3f7e49825f8e049518 Mon Sep 17 00:00:00 2001 From: pokamest Date: Fri, 9 Sep 2022 15:34:09 +0300 Subject: [PATCH 16/43] Travis build fix --- .travis.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 60c954c7..1f1affd6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,10 +23,10 @@ jobs: if [ ! -f $QT_BIN_DIR/qmake ]; then \ brew install p7zip ccache && \ python3 -m pip install --upgrade pip && \ - pip install -U aqtinstall requests py7zr && \ - pip show aqtinstall && \ - aqt install-qt mac desktop $QT_VERSION clang_64 -m all -O $HOME/Qt && \ - aqt install-tool mac desktop tools_ifw -O $HOME/Qt ; \ + python3 -m pip install -U aqtinstall requests py7zr && \ + python3 -m pip show aqtinstall && \ + python3 -m aqt install-qt mac desktop $QT_VERSION clang_64 -m all -O $HOME/Qt && \ + python3 -m aqt install-tool mac desktop tools_ifw -O $HOME/Qt ; \ fi script: @@ -314,9 +314,9 @@ jobs: if [ ! -f $QT_BIN_DIR/qmake ]; then \ brew install p7zip ccache && \ python3 -m pip install --upgrade pip && \ - pip install -U aqtinstall requests py7zr && \ - pip show aqtinstall && \ - aqt install-qt mac ios $QT_VERSION -m all -O $HOME/Qt ; \ + python3 -m pip install -U aqtinstall requests py7zr && \ + python3 -m pip show aqtinstall && \ + python3 -m aqt install-qt mac ios $QT_VERSION -m all -O $HOME/Qt ; \ fi - brew install golang - go install golang.org/x/mobile/cmd/gomobile@latest From c5ba89b05425154af8b4943a098c825d587df9b9 Mon Sep 17 00:00:00 2001 From: pokamest Date: Sat, 10 Sep 2022 19:57:44 +0300 Subject: [PATCH 17/43] Code cleanup --- .travis.yml | 1 + client/client.pro | 5 -- client/main.cpp | 8 ---- client/platforms/android/native.cpp | 54 ---------------------- client/platforms/android/native.h | 20 -------- client/ui/qml/Pages/PageNetworkSetting.qml | 2 +- client/ui/qml/Pages/PageStart.qml | 1 + 7 files changed, 3 insertions(+), 88 deletions(-) delete mode 100644 client/platforms/android/native.cpp delete mode 100644 client/platforms/android/native.h diff --git a/.travis.yml b/.travis.yml index 1f1affd6..35c94108 100644 --- a/.travis.yml +++ b/.travis.yml @@ -164,6 +164,7 @@ jobs: # ------------------------------------------------------ - name: Linux os: linux + language: cpp dist: focal addons: diff --git a/client/client.pro b/client/client.pro index 14a78e16..bf9db7ed 100644 --- a/client/client.pro +++ b/client/client.pro @@ -11,9 +11,6 @@ IS_CI=$$(CI) } CONFIG += qtquickcompiler -CONFIG += qzxing_multimedia \ - enable_decoder_qr_code \ - enable_encoder_qr_code include("3rd/QtSsh/src/ssh/qssh.pri") include("3rd/QtSsh/src/botan/botan.pri") @@ -247,13 +244,11 @@ android { INCLUDEPATH += platforms/android HEADERS += \ - platforms/android/native.h \ platforms/android/android_controller.h \ platforms/android/android_notificationhandler.h \ protocols/android_vpnprotocol.h SOURCES += \ - platforms/android/native.cpp \ platforms/android/android_controller.cpp \ platforms/android/android_notificationhandler.cpp \ protocols/android_vpnprotocol.cpp diff --git a/client/main.cpp b/client/main.cpp index a5751979..62685953 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -9,10 +9,6 @@ #include "Windows.h" #endif -#if defined(Q_OS_ANDROID) -#include "native.h" -#endif - #if defined(Q_OS_IOS) #include "platforms/ios/QtAppDelegate-C-Interface.h" #endif @@ -46,10 +42,6 @@ int main(int argc, char *argv[]) AllowSetForegroundWindow(0); #endif -#if defined(Q_OS_ANDROID) - NativeHelpers::registerApplicationInstance(&app); -#endif - #if defined(Q_OS_IOS) QtAppDelegateInitialize(); #endif diff --git a/client/platforms/android/native.cpp b/client/platforms/android/native.cpp deleted file mode 100644 index 60ff2d8b..00000000 --- a/client/platforms/android/native.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include "native.h" -#include -#if defined(Q_OS_ANDROID) - #include -#endif // Q_OS_ANDROID - - -QObject *NativeHelpers::application_p_ = 0; - -#if defined(Q_OS_ANDROID) - -// define our native static functions -// these are the functions that Java part will call directly from Android UI thread -static void onPermissionsGranted(JNIEnv * /*env*/, jobject /*obj*/) -{ - QMetaObject::invokeMethod(NativeHelpers::getApplicationInstance(), "onPermissionsGranted" - , Qt::QueuedConnection); -} - -static void onPermissionsDenied(JNIEnv * /*env*/, jobject /*obj*/) -{ - QMetaObject::invokeMethod(NativeHelpers::getApplicationInstance(), "onPermissionsDenied" - , Qt::QueuedConnection); -} - -//create a vector with all our JNINativeMethod(s) -static JNINativeMethod methods[] = { - {"onPermissionsGranted", "()V", (void *)onPermissionsGranted}, - {"onPermissionsDenied", "()V", (void *)onPermissionsDenied}, -}; - -// this method is called automatically by Java after the .so file is loaded -JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* /*reserved*/) -{ - JNIEnv* env; - // get the JNIEnv pointer. - if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) - return JNI_ERR; - - // search for Java class which declares the native methods - jclass javaClass = env->FindClass("org/ftylitak/qzxing/NativeFunctions"); - if (!javaClass) - return JNI_ERR; - - // register our native methods - if (env->RegisterNatives(javaClass, methods, - sizeof(methods) / sizeof(methods[0])) < 0) { - return JNI_ERR; - } - - return JNI_VERSION_1_6; -} - -#endif // Q_OS_ANDROID diff --git a/client/platforms/android/native.h b/client/platforms/android/native.h deleted file mode 100644 index db187a73..00000000 --- a/client/platforms/android/native.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef NATIVE_H -#define NATIVE_H - -#include - -class NativeHelpers { -public: - static void registerApplicationInstance(QObject *app_p) { - application_p_ = app_p; - } - - static QObject* getApplicationInstance() { - return application_p_; - } - -private: - static QObject *application_p_; -}; - -#endif // NATIVE_H diff --git a/client/ui/qml/Pages/PageNetworkSetting.qml b/client/ui/qml/Pages/PageNetworkSetting.qml index 924b32dd..0e890446 100644 --- a/client/ui/qml/Pages/PageNetworkSetting.qml +++ b/client/ui/qml/Pages/PageNetworkSetting.qml @@ -87,7 +87,7 @@ If AmneziaDNS service is not installed on the same server, or this option is unc anchors.topMargin: 20 width: parent.width - 60 height: 21 - text: qsTr("Secondray DNS server") + text: qsTr("Secondary DNS server") } TextFieldType { id: dns2 diff --git a/client/ui/qml/Pages/PageStart.qml b/client/ui/qml/Pages/PageStart.qml index ab53792d..d7f2a49c 100644 --- a/client/ui/qml/Pages/PageStart.qml +++ b/client/ui/qml/Pages/PageStart.qml @@ -127,6 +127,7 @@ PageBase { BlueButtonType { id: qr_code_import + visible: GC.isMobile() anchors.horizontalCenter: parent.horizontalCenter anchors.top: qr_code_import_open.bottom anchors.topMargin: 10 From 9cfc65eeda920b0e467e849501b059129407d1b7 Mon Sep 17 00:00:00 2001 From: pokamest Date: Sun, 11 Sep 2022 00:24:43 +0300 Subject: [PATCH 18/43] Version bump --- client/defines.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/defines.h b/client/defines.h index 4334c68f..c64eb458 100644 --- a/client/defines.h +++ b/client/defines.h @@ -4,7 +4,7 @@ #define APPLICATION_NAME "AmneziaVPN" #define SERVICE_NAME "AmneziaVPN-service" #define ORGANIZATION_NAME "AmneziaVPN.ORG" -#define APP_MAJOR_VERSION "2.1.0" -#define APP_VERSION "2.1.0.0" +#define APP_MAJOR_VERSION "2.1.2" +#define APP_VERSION "2.1.2.0" #endif // DEFINES_H From 53e240add72904c586d02ae706398c72dd233c12 Mon Sep 17 00:00:00 2001 From: pokamest Date: Mon, 12 Sep 2022 14:57:00 +0300 Subject: [PATCH 19/43] Android manifest fix --- client/android/AndroidManifest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/android/AndroidManifest.xml b/client/android/AndroidManifest.xml index 1c112d2f..cb70759d 100644 --- a/client/android/AndroidManifest.xml +++ b/client/android/AndroidManifest.xml @@ -19,7 +19,7 @@ - + From 5fff65db5ae6f90f6e197b77a96f82f8a046de76 Mon Sep 17 00:00:00 2001 From: pokamest Date: Mon, 19 Sep 2022 00:44:00 +0300 Subject: [PATCH 20/43] Tiny fixes --- .travis.yml | 1 + client/configurators/wireguard_configurator.cpp | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 35c94108..9e16ef5f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -217,6 +217,7 @@ jobs: - ccache - directories: - $HOME/Qt + - $HOME/.ccache # ------------------------------------------------------ - name: Android diff --git a/client/configurators/wireguard_configurator.cpp b/client/configurators/wireguard_configurator.cpp index 08e6c40a..913299c7 100644 --- a/client/configurators/wireguard_configurator.cpp +++ b/client/configurators/wireguard_configurator.cpp @@ -80,7 +80,12 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon stdOut += data + "\n"; }; - m_serverController->runContainerScript(credentials, container, script, cbReadStdOut); + e = m_serverController->runContainerScript(credentials, container, script, cbReadStdOut); + if (errorCode && e) { + *errorCode = e; + return connData; + } + stdOut.replace("AllowedIPs = ", ""); stdOut.replace("/32", ""); QStringList ips = stdOut.split("\n", Qt::SkipEmptyParts); From d93be76505b5ae2d7d1aefa90f40829066dc27a3 Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Mon, 19 Sep 2022 12:27:33 +0300 Subject: [PATCH 21/43] Add auth protection for ssh key export We use a builtin keyguard for ssh key export protection on Android. This protection works only if some protection is set on the phone. https://developer.android.com/reference/android/app/KeyguardManager#isDeviceSecure() --- .../src/org/amnezia/vpn/AuthHelper.java | 24 ++++++++++++ client/ui/pages_logic/ServerSettingsLogic.cpp | 39 +++++++++++++++++++ client/ui/pages_logic/ServerSettingsLogic.h | 25 ++++++++++++ 3 files changed, 88 insertions(+) create mode 100644 client/android/src/org/amnezia/vpn/AuthHelper.java diff --git a/client/android/src/org/amnezia/vpn/AuthHelper.java b/client/android/src/org/amnezia/vpn/AuthHelper.java new file mode 100644 index 00000000..b30aa8f5 --- /dev/null +++ b/client/android/src/org/amnezia/vpn/AuthHelper.java @@ -0,0 +1,24 @@ +package org.amnezia.vpn; + +import android.content.Context; +import android.app.KeyguardManager; +import android.content.Intent; +import org.qtproject.qt5.android.bindings.QtActivity; + + +import static android.content.Context.KEYGUARD_SERVICE; + +public class AuthHelper extends QtActivity { + + static final String TAG = "AuthHelper"; + + public static Intent getAuthIntent(Context context) { + KeyguardManager mKeyguardManager = (KeyguardManager)context.getSystemService(KEYGUARD_SERVICE); + if (mKeyguardManager.isDeviceSecure()) { + return mKeyguardManager.createConfirmDeviceCredentialIntent(null, null); + } else { + return null; + } + } + +} diff --git a/client/ui/pages_logic/ServerSettingsLogic.cpp b/client/ui/pages_logic/ServerSettingsLogic.cpp index bd954c88..3b123174 100644 --- a/client/ui/pages_logic/ServerSettingsLogic.cpp +++ b/client/ui/pages_logic/ServerSettingsLogic.cpp @@ -10,6 +10,12 @@ #include #include +#if defined(Q_OS_ANDROID) +#include +#include +#include +#endif + ServerSettingsLogic::ServerSettingsLogic(UiLogic *logic, QObject *parent): PageLogicBase(logic, parent), m_labelWaitInfoVisible{true}, @@ -126,8 +132,41 @@ void ServerSettingsLogic::onLineEditDescriptionEditingFinished() uiLogic()->onUpdateAllPages(); } +#if defined(Q_OS_ANDROID) +/* Auth result handler for Android */ +void authResultReceiver::handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject &data) +{ + qDebug() << "receiverRequestCode" << receiverRequestCode << "resultCode" << resultCode; + + if (resultCode == -1) { //ResultOK + uiLogic()->pageLogic()->updateSharingPage(m_serverIndex, DockerContainer::None); + emit uiLogic()->goToShareProtocolPage(Proto::Any); + } +} +#endif + void ServerSettingsLogic::onPushButtonShareFullClicked() { +#if defined(Q_OS_ANDROID) +/* We use builtin keyguard for ssh key export protection on Android */ + auto appContext = QtAndroid::androidActivity().callObjectMethod( + "getApplicationContext", "()Landroid/content/Context;"); + if (appContext.isValid()) { + QAndroidActivityResultReceiver *receiver = new authResultReceiver(uiLogic(), uiLogic()->selectedServerIndex); + auto intent = QAndroidJniObject::callStaticObjectMethod( + "org/amnezia/vpn/AuthHelper", "getAuthIntent", + "(Landroid/content/Context;)Landroid/content/Intent;", appContext.object()); + if (intent.isValid()) { + if (intent.object() != nullptr) { + QtAndroid::startActivity(intent.object(), 1, receiver); + } + } else { + uiLogic()->pageLogic()->updateSharingPage(uiLogic()->selectedServerIndex, DockerContainer::None); + emit uiLogic()->goToShareProtocolPage(Proto::Any); + } + } +#else uiLogic()->pageLogic()->updateSharingPage(uiLogic()->selectedServerIndex, DockerContainer::None); emit uiLogic()->goToShareProtocolPage(Proto::Any); +#endif } diff --git a/client/ui/pages_logic/ServerSettingsLogic.h b/client/ui/pages_logic/ServerSettingsLogic.h index 282b90ea..e2d57422 100644 --- a/client/ui/pages_logic/ServerSettingsLogic.h +++ b/client/ui/pages_logic/ServerSettingsLogic.h @@ -3,6 +3,10 @@ #include "PageLogicBase.h" +#if defined(Q_OS_ANDROID) +#include +#endif + class UiLogic; class ServerSettingsLogic : public PageLogicBase @@ -34,4 +38,25 @@ public: ~ServerSettingsLogic() = default; }; + +#if defined(Q_OS_ANDROID) +/* Auth result handler for Android */ +class authResultReceiver final : public PageLogicBase, public QAndroidActivityResultReceiver +{ +Q_OBJECT + +public: + authResultReceiver(UiLogic *uiLogic, int serverIndex , QObject *parent = nullptr) : PageLogicBase(uiLogic, parent) { + m_serverIndex = serverIndex; + } + ~authResultReceiver() {} + +public: + void handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject &data) override; + +private: + int m_serverIndex; +}; +#endif + #endif // SERVER_SETTINGS_LOGIC_H From f2d7a45b74e42a69070d08d65052b293af3ea4ef Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Fri, 23 Sep 2022 21:50:25 +0300 Subject: [PATCH 22/43] Set security screen for Android app This will guarantee that even apps running with root privileges are unable to directly capture information displayed by the app on the screen. --- client/ui/pages_logic/StartPageLogic.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/client/ui/pages_logic/StartPageLogic.cpp b/client/ui/pages_logic/StartPageLogic.cpp index de3173b5..1e28d6cd 100644 --- a/client/ui/pages_logic/StartPageLogic.cpp +++ b/client/ui/pages_logic/StartPageLogic.cpp @@ -11,6 +11,7 @@ #include #ifdef Q_OS_ANDROID +#include #include "platforms/android/android_controller.h" #endif @@ -23,7 +24,16 @@ StartPageLogic::StartPageLogic(UiLogic *logic, QObject *parent): m_pushButtonBackFromStartVisible{true}, m_ipAddressPortRegex{Utils::ipAddressPortRegExp()} { - +#ifdef Q_OS_ANDROID + // Set security screen for Android app + QtAndroid::runOnAndroidThread([]() { + QAndroidJniObject window = QtAndroid::androidActivity().callObjectMethod("getWindow", "()Landroid/view/Window;"); + if (window.isValid()){ + const int FLAG_SECURE = 8192; + window.callMethod("addFlags", "(I)V", FLAG_SECURE); + } + }); +#endif } void StartPageLogic::onUpdatePage() From f90ebbbb4eb27b9223d9c2a08558cc474041b849 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Wed, 26 Oct 2022 09:36:58 +0300 Subject: [PATCH 23/43] added inclusion of an additional client/server config when generating configs from templates - fixed name in additional server config field --- client/core/servercontroller.cpp | 5 +++++ client/protocols/protocols_defs.h | 5 +++++ .../server_scripts/openvpn/configure_container.sh | 1 + client/server_scripts/openvpn/template.ovpn | 2 ++ .../openvpn_cloak/configure_container.sh | 1 + client/server_scripts/openvpn_cloak/template.ovpn | 2 ++ .../openvpn_shadowsocks/configure_container.sh | 1 + .../openvpn_shadowsocks/template.ovpn | 2 ++ client/ui/pages_logic/protocols/OpenVpnLogic.cpp | 12 ++++++++++++ client/ui/pages_logic/protocols/OpenVpnLogic.h | 2 ++ client/ui/qml/Pages/Protocols/PageProtoOpenVPN.qml | 14 ++++++++++---- 11 files changed, 43 insertions(+), 4 deletions(-) diff --git a/client/core/servercontroller.cpp b/client/core/servercontroller.cpp index 1ea36011..2828d2b0 100644 --- a/client/core/servercontroller.cpp +++ b/client/core/servercontroller.cpp @@ -662,6 +662,11 @@ ServerController::Vars ServerController::genVarsForScript(const ServerCredential vars.append({{"$OPENVPN_TA_KEY", "" }}); } + vars.append({{"$OPENVPN_ADDITIONAL_CLIENT_CONFIG", openvpnConfig.value(config_key::additional_client_config). + toString(protocols::openvpn::defaultAdditionalClientConfig) }}); + vars.append({{"$OPENVPN_ADDITIONAL_SERVER_CONFIG", openvpnConfig.value(config_key::additional_server_config). + toString(protocols::openvpn::defaultAdditionalServerConfig) }}); + // ShadowSocks vars vars.append({{"$SHADOWSOCKS_SERVER_PORT", ssConfig.value(config_key::port).toString(protocols::shadowsocks::defaultPort) }}); vars.append({{"$SHADOWSOCKS_LOCAL_PORT", ssConfig.value(config_key::local_port).toString(protocols::shadowsocks::defaultLocalProxyPort) }}); diff --git a/client/protocols/protocols_defs.h b/client/protocols/protocols_defs.h index 1994660b..c158822d 100644 --- a/client/protocols/protocols_defs.h +++ b/client/protocols/protocols_defs.h @@ -53,6 +53,9 @@ constexpr char subnet_address[] = "subnet_address"; constexpr char subnet_mask[] = "subnet_mask"; constexpr char subnet_cidr[] = "subnet_cidr"; +constexpr char additional_client_config[] = "additional_client_config"; +constexpr char additional_server_config[] = "additional_server_config"; + // proto config keys constexpr char last_config[] = "last_config"; } @@ -82,6 +85,8 @@ constexpr bool defaultTlsAuth = true; constexpr char ncpDisableString[] = "ncp-disable"; constexpr char tlsAuthString[] = "tls-auth /opt/amnezia/openvpn/ta.key 0"; +constexpr char defaultAdditionalClientConfig[] = ""; +constexpr char defaultAdditionalServerConfig[] = ""; } namespace shadowsocks { diff --git a/client/server_scripts/openvpn/configure_container.sh b/client/server_scripts/openvpn/configure_container.sh index d51d019b..0499b6e8 100644 --- a/client/server_scripts/openvpn/configure_container.sh +++ b/client/server_scripts/openvpn/configure_container.sh @@ -23,4 +23,5 @@ verb 1 tls-server tls-version-min 1.2 $OPENVPN_TLS_AUTH +$OPENVPN_ADDITIONAL_SERVER_CONFIG EOF diff --git a/client/server_scripts/openvpn/template.ovpn b/client/server_scripts/openvpn/template.ovpn index 6ce736c2..c0b4a044 100644 --- a/client/server_scripts/openvpn/template.ovpn +++ b/client/server_scripts/openvpn/template.ovpn @@ -21,6 +21,8 @@ block-outside-dns remote $REMOTE_HOST $OPENVPN_PORT +$OPENVPN_ADDITIONAL_CLIENT_CONFIG + $OPENVPN_CA_CERT diff --git a/client/server_scripts/openvpn_cloak/configure_container.sh b/client/server_scripts/openvpn_cloak/configure_container.sh index 72961810..26b514a4 100644 --- a/client/server_scripts/openvpn_cloak/configure_container.sh +++ b/client/server_scripts/openvpn_cloak/configure_container.sh @@ -23,6 +23,7 @@ verb 1 tls-server tls-version-min 1.2 $OPENVPN_TLS_AUTH +$OPENVPN_ADDITIONAL_SERVER_CONFIG EOF # Cloak config diff --git a/client/server_scripts/openvpn_cloak/template.ovpn b/client/server_scripts/openvpn_cloak/template.ovpn index 98e0e09f..4fc2b2c3 100644 --- a/client/server_scripts/openvpn_cloak/template.ovpn +++ b/client/server_scripts/openvpn_cloak/template.ovpn @@ -22,6 +22,8 @@ block-outside-dns route $REMOTE_HOST 255.255.255.255 net_gateway remote 127.0.0.1 1194 +$OPENVPN_ADDITIONAL_CLIENT_CONFIG + $OPENVPN_CA_CERT diff --git a/client/server_scripts/openvpn_shadowsocks/configure_container.sh b/client/server_scripts/openvpn_shadowsocks/configure_container.sh index 29a5ebf9..db8fe055 100644 --- a/client/server_scripts/openvpn_shadowsocks/configure_container.sh +++ b/client/server_scripts/openvpn_shadowsocks/configure_container.sh @@ -23,6 +23,7 @@ verb 1 tls-server tls-version-min 1.2 $OPENVPN_TLS_AUTH +$OPENVPN_ADDITIONAL_SERVER_CONFIG EOF # ShadowSocks config diff --git a/client/server_scripts/openvpn_shadowsocks/template.ovpn b/client/server_scripts/openvpn_shadowsocks/template.ovpn index 76b755f6..64cbd4be 100644 --- a/client/server_scripts/openvpn_shadowsocks/template.ovpn +++ b/client/server_scripts/openvpn_shadowsocks/template.ovpn @@ -23,6 +23,8 @@ socks-proxy 127.0.0.1 $SHADOWSOCKS_LOCAL_PORT route $REMOTE_HOST 255.255.255.255 net_gateway remote $REMOTE_HOST $OPENVPN_PORT +$OPENVPN_ADDITIONAL_CLIENT_CONFIG + $OPENVPN_CA_CERT diff --git a/client/ui/pages_logic/protocols/OpenVpnLogic.cpp b/client/ui/pages_logic/protocols/OpenVpnLogic.cpp index 3b672747..91f21151 100644 --- a/client/ui/pages_logic/protocols/OpenVpnLogic.cpp +++ b/client/ui/pages_logic/protocols/OpenVpnLogic.cpp @@ -21,6 +21,8 @@ OpenVpnLogic::OpenVpnLogic(UiLogic *logic, QObject *parent): m_checkBoxBlockDnsChecked{false}, m_lineEditPortText{}, m_checkBoxTlsAuthChecked{false}, + m_textAreaAdditionalClientConfig{""}, + m_textAreaAdditionalServerConfig{""}, m_pushButtonSaveVisible{false}, m_progressBarResetVisible{false}, @@ -67,6 +69,14 @@ void OpenVpnLogic::updateProtocolPage(const QJsonObject &openvpnConfig, DockerCo bool isTlsAuth = openvpnConfig.value(config_key::tls_auth).toBool(protocols::openvpn::defaultTlsAuth); set_checkBoxTlsAuthChecked(isTlsAuth); + QString additionalClientConfig = openvpnConfig.value(config_key::additional_client_config). + toString(protocols::openvpn::defaultAdditionalClientConfig); + set_textAreaAdditionalClientConfig(additionalClientConfig); + + QString additionalServerConfig = openvpnConfig.value(config_key::additional_server_config). + toString(protocols::openvpn::defaultAdditionalServerConfig); + set_textAreaAdditionalServerConfig(additionalServerConfig); + if (container == DockerContainer::ShadowSocks) { set_radioButtonUdpEnabled(false); set_radioButtonTcpEnabled(false); @@ -142,5 +152,7 @@ QJsonObject OpenVpnLogic::getProtocolConfigFromPage(QJsonObject oldConfig) oldConfig.insert(config_key::block_outside_dns, checkBoxBlockDnsChecked()); oldConfig.insert(config_key::port, lineEditPortText()); oldConfig.insert(config_key::tls_auth, checkBoxTlsAuthChecked()); + oldConfig.insert(config_key::additional_client_config, textAreaAdditionalClientConfig()); + oldConfig.insert(config_key::additional_server_config, textAreaAdditionalServerConfig()); return oldConfig; } diff --git a/client/ui/pages_logic/protocols/OpenVpnLogic.h b/client/ui/pages_logic/protocols/OpenVpnLogic.h index 45141050..23c2dd4d 100644 --- a/client/ui/pages_logic/protocols/OpenVpnLogic.h +++ b/client/ui/pages_logic/protocols/OpenVpnLogic.h @@ -22,6 +22,8 @@ class OpenVpnLogic : public PageProtocolLogicBase AUTO_PROPERTY(bool, checkBoxBlockDnsChecked) AUTO_PROPERTY(QString, lineEditPortText) AUTO_PROPERTY(bool, checkBoxTlsAuthChecked) + AUTO_PROPERTY(QString, textAreaAdditionalClientConfig) + AUTO_PROPERTY(QString, textAreaAdditionalServerConfig) AUTO_PROPERTY(bool, pushButtonSaveVisible) AUTO_PROPERTY(bool, progressBarResetVisible) diff --git a/client/ui/qml/Pages/Protocols/PageProtoOpenVPN.qml b/client/ui/qml/Pages/Protocols/PageProtoOpenVPN.qml index 42ef1e59..9c00172c 100644 --- a/client/ui/qml/Pages/Protocols/PageProtoOpenVPN.qml +++ b/client/ui/qml/Pages/Protocols/PageProtoOpenVPN.qml @@ -281,7 +281,10 @@ PageProtocolBase { font.styleName: "normal" font.pixelSize: 16 color: "#181922" - + text: logic.textAreaAdditionalClientConfig + onEditingFinished: { + logic.textAreaAdditionalClientConfig = text + } } } @@ -294,7 +297,7 @@ PageProtocolBase { implicitWidth: parent.width height: 21 - text: qsTr("Additional client config commands →") + text: qsTr("Additional server config commands →") background: Item { anchors.fill: parent } @@ -305,7 +308,7 @@ PageProtocolBase { font.styleName: "normal" font.pixelSize: 16 color: "#15CDCB"; - text: pb_client_config.text + text: pb_server_config.text horizontalAlignment: Text.AlignLeft verticalAlignment: Text.AlignVCenter } @@ -331,7 +334,10 @@ PageProtocolBase { font.styleName: "normal" font.pixelSize: 16 color: "#181922" - + text: logic.textAreaAdditionalServerConfig + onEditingFinished: { + logic.textAreaAdditionalServerConfig = text + } } } From 53d7a92a0da13b671f4fbe3a8d58ad9dcd5af235 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Tue, 1 Nov 2022 23:12:42 +0300 Subject: [PATCH 24/43] added import of configs in .ovpn format - on the "OpenVPN Settings" page, such a config will be displayed as raw text --- client/settings.cpp | 7 + client/settings.h | 2 + .../ui/pages_logic/ServerContainersLogic.cpp | 3 +- client/ui/pages_logic/StartPageLogic.cpp | 69 +- client/ui/pages_logic/StartPageLogic.h | 1 + .../ui/pages_logic/protocols/CloakLogic.cpp | 2 +- client/ui/pages_logic/protocols/CloakLogic.h | 2 +- .../ui/pages_logic/protocols/OpenVpnLogic.cpp | 12 +- .../ui/pages_logic/protocols/OpenVpnLogic.h | 5 +- .../protocols/OtherProtocolsLogic.cpp | 2 +- .../protocols/OtherProtocolsLogic.h | 2 +- .../protocols/PageProtocolLogicBase.h | 2 +- .../protocols/ShadowSocksLogic.cpp | 2 +- .../pages_logic/protocols/ShadowSocksLogic.h | 2 +- .../qml/Pages/Protocols/PageProtoOpenVPN.qml | 639 +++++++++--------- 15 files changed, 430 insertions(+), 322 deletions(-) diff --git a/client/settings.cpp b/client/settings.cpp index 6e470a33..ccef9e40 100644 --- a/client/settings.cpp +++ b/client/settings.cpp @@ -312,3 +312,10 @@ ServerCredentials Settings::serverCredentials(int index) const return credentials; } + +bool Settings::isThirdPartyConfig(int serverIndex) const +{ + if (serverIndex < 0) return false; + const QJsonObject &s = server(serverIndex); + return s.value("isThirdPartyConfig").toBool(); +} diff --git a/client/settings.h b/client/settings.h index c78b9a79..56b28c18 100644 --- a/client/settings.h +++ b/client/settings.h @@ -113,6 +113,8 @@ public: QByteArray backupAppConfig() const { return m_settings.backupAppConfig(); } bool restoreAppConfig(const QByteArray &cfg) { return m_settings.restoreAppConfig(cfg); } + bool isThirdPartyConfig(int serverIndex) const; + private: SecureQSettings m_settings; diff --git a/client/ui/pages_logic/ServerContainersLogic.cpp b/client/ui/pages_logic/ServerContainersLogic.cpp index 27f865b5..ba873eb2 100644 --- a/client/ui/pages_logic/ServerContainersLogic.cpp +++ b/client/ui/pages_logic/ServerContainersLogic.cpp @@ -38,7 +38,8 @@ void ServerContainersLogic::onPushButtonProtoSettingsClicked(DockerContainer c, uiLogic()->selectedDockerContainer = c; uiLogic()->protocolLogic(p)->updateProtocolPage(m_settings->protocolConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer, p), uiLogic()->selectedDockerContainer, - m_settings->haveAuthData(uiLogic()->selectedServerIndex)); + m_settings->haveAuthData(uiLogic()->selectedServerIndex), + m_settings->isThirdPartyConfig(uiLogic()->selectedServerIndex)); emit uiLogic()->goToProtocolPage(p); } diff --git a/client/ui/pages_logic/StartPageLogic.cpp b/client/ui/pages_logic/StartPageLogic.cpp index 1e28d6cd..6a6333e9 100644 --- a/client/ui/pages_logic/StartPageLogic.cpp +++ b/client/ui/pages_logic/StartPageLogic.cpp @@ -15,6 +15,25 @@ #include "platforms/android/android_controller.h" #endif +namespace { +QString checkConfigFormat(const QString &config) +{ + const QString openVpnConfigPatternCli = "client"; + const QString openVpnConfigPatternProto1 = "proto tcp"; + const QString openVpnConfigPatternProto2 = "proto udp"; + const QString openVpnConfigPatternDriver1 = "dev tun"; + const QString openVpnConfigPatternDriver2 = "dev tap"; + + if (config.contains(openVpnConfigPatternCli) && + (config.contains(openVpnConfigPatternProto1) || config.contains(openVpnConfigPatternProto2)) && + (config.contains(openVpnConfigPatternDriver1) || config.contains(openVpnConfigPatternDriver2))) { + return "OpenVpn"; + } + return "Amnezia"; +} + +} + StartPageLogic::StartPageLogic(UiLogic *logic, QObject *parent): PageLogicBase(logic, parent), m_pushButtonConnectEnabled{true}, @@ -136,7 +155,7 @@ void StartPageLogic::onPushButtonImport() void StartPageLogic::onPushButtonImportOpenFile() { QString fileName = QFileDialog::getOpenFileName(nullptr, tr("Open profile"), - QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), "*.vpn"); + QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), "*.vpn *.ovpn"); if (fileName.isEmpty()) return; @@ -144,7 +163,12 @@ void StartPageLogic::onPushButtonImportOpenFile() file.open(QIODevice::ReadOnly); QByteArray data = file.readAll(); - importConnectionFromCode(QString(data)); + auto configFormat = checkConfigFormat(QString(data)); + if (configFormat == "OpenVpn") { + importConnectionFromOpenVpnConfig(QString(data)); + } else { + importConnectionFromCode(QString(data)); + } } bool StartPageLogic::importConnection(const QJsonObject &profile) @@ -214,3 +238,44 @@ bool StartPageLogic::importConnectionFromQr(const QByteArray &data) return false; } + +bool StartPageLogic::importConnectionFromOpenVpnConfig(const QString &config) +{ + QJsonObject openVpnConfig; + openVpnConfig[config_key::config] = config; + + QJsonObject lastConfig; + lastConfig[config_key::last_config] = QString(QJsonDocument(openVpnConfig).toJson()); + + QJsonObject containers; + containers.insert(config_key::container, QJsonValue("amnezia-openvpn")); + containers.insert("openvpn", QJsonValue(lastConfig)); + + QJsonArray arr; + arr.push_back(containers); + + QJsonObject o; + o[config_key::containers] = arr; + o[config_key::defaultContainer] = "amnezia-openvpn"; + o[config_key::description] = "OpenVpn server"; + + + const static QRegularExpression dnsRegExp("dhcp-option DNS \\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b"); + QRegularExpressionMatchIterator dnsMatch = dnsRegExp.globalMatch(config); + if (dnsMatch.hasNext()) { + o[config_key::dns1] = dnsMatch.next().captured(0).split(" ").at(2); + } + if (dnsMatch.hasNext()) { + o[config_key::dns2] = dnsMatch.next().captured(0).split(" ").at(2); + } + + const static QRegularExpression hostNameRegExp("remote \\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b"); + QRegularExpressionMatch hostNameMatch = hostNameRegExp.match(config); + if (hostNameMatch.hasMatch()) { + o[config_key::hostName] = hostNameMatch.captured(0).split(" ").at(1); + } + + o["isThirdPartyConfig"] = true; + + return importConnection(o); +} diff --git a/client/ui/pages_logic/StartPageLogic.h b/client/ui/pages_logic/StartPageLogic.h index 24bdc53f..830c9100 100644 --- a/client/ui/pages_logic/StartPageLogic.h +++ b/client/ui/pages_logic/StartPageLogic.h @@ -32,6 +32,7 @@ public: bool importConnection(const QJsonObject &profile); bool importConnectionFromCode(QString code); bool importConnectionFromQr(const QByteArray &data); + bool importConnectionFromOpenVpnConfig(const QString &config); public: explicit StartPageLogic(UiLogic *uiLogic, QObject *parent = nullptr); diff --git a/client/ui/pages_logic/protocols/CloakLogic.cpp b/client/ui/pages_logic/protocols/CloakLogic.cpp index 59019990..9faa5996 100644 --- a/client/ui/pages_logic/protocols/CloakLogic.cpp +++ b/client/ui/pages_logic/protocols/CloakLogic.cpp @@ -23,7 +23,7 @@ CloakLogic::CloakLogic(UiLogic *logic, QObject *parent): } -void CloakLogic::updateProtocolPage(const QJsonObject &ckConfig, DockerContainer container, bool haveAuthData) +void CloakLogic::updateProtocolPage(const QJsonObject &ckConfig, DockerContainer container, bool haveAuthData, bool isThirdPartyConfig) { set_pageEnabled(haveAuthData); set_pushButtonSaveVisible(haveAuthData); diff --git a/client/ui/pages_logic/protocols/CloakLogic.h b/client/ui/pages_logic/protocols/CloakLogic.h index d88252c8..8d4450fb 100644 --- a/client/ui/pages_logic/protocols/CloakLogic.h +++ b/client/ui/pages_logic/protocols/CloakLogic.h @@ -28,7 +28,7 @@ public: explicit CloakLogic(UiLogic *uiLogic, QObject *parent = nullptr); ~CloakLogic() = default; - void updateProtocolPage (const QJsonObject &ckConfig, DockerContainer container, bool haveAuthData) override; + void updateProtocolPage(const QJsonObject &ckConfig, DockerContainer container, bool haveAuthData, bool isThirdPartyConfig) override; QJsonObject getProtocolConfigFromPage(QJsonObject oldConfig) override; private: diff --git a/client/ui/pages_logic/protocols/OpenVpnLogic.cpp b/client/ui/pages_logic/protocols/OpenVpnLogic.cpp index 91f21151..7d726670 100644 --- a/client/ui/pages_logic/protocols/OpenVpnLogic.cpp +++ b/client/ui/pages_logic/protocols/OpenVpnLogic.cpp @@ -35,7 +35,7 @@ OpenVpnLogic::OpenVpnLogic(UiLogic *logic, QObject *parent): } -void OpenVpnLogic::updateProtocolPage(const QJsonObject &openvpnConfig, DockerContainer container, bool haveAuthData) +void OpenVpnLogic::updateProtocolPage(const QJsonObject &openvpnConfig, DockerContainer container, bool haveAuthData, bool isThirdPartyConfig) { qDebug() << "OpenVpnLogic::updateProtocolPage"; set_pageEnabled(haveAuthData); @@ -87,6 +87,16 @@ void OpenVpnLogic::updateProtocolPage(const QJsonObject &openvpnConfig, DockerCo toString(protocols::openvpn::defaultPort)); set_lineEditPortEnabled(container == DockerContainer::OpenVpn); + + auto lastConfig = openvpnConfig.value(config_key::last_config).toString(); + auto lastConfigJson = QJsonDocument::fromJson(lastConfig.toUtf8()).object(); + QStringList lines = lastConfigJson.value(config_key::config).toString().replace("\r", "").split("\n"); + for (const QString &l: lines) { + m_openVpnLastConfigText.append(l + "\n"); + } + + emit openVpnLastConfigTextChanged(m_openVpnLastConfigText); + set_isThirdPartyConfig(isThirdPartyConfig); } void OpenVpnLogic::onPushButtonProtoOpenVpnSaveClicked() diff --git a/client/ui/pages_logic/protocols/OpenVpnLogic.h b/client/ui/pages_logic/protocols/OpenVpnLogic.h index 23c2dd4d..76c481e6 100644 --- a/client/ui/pages_logic/protocols/OpenVpnLogic.h +++ b/client/ui/pages_logic/protocols/OpenVpnLogic.h @@ -35,6 +35,9 @@ class OpenVpnLogic : public PageProtocolLogicBase AUTO_PROPERTY(int, progressBarResetValue) AUTO_PROPERTY(int, progressBarResetMaximium) + AUTO_PROPERTY(QString, openVpnLastConfigText) + AUTO_PROPERTY(bool, isThirdPartyConfig) + public: Q_INVOKABLE void onPushButtonProtoOpenVpnSaveClicked(); @@ -42,7 +45,7 @@ public: explicit OpenVpnLogic(UiLogic *uiLogic, QObject *parent = nullptr); ~OpenVpnLogic() = default; - void updateProtocolPage(const QJsonObject &openvpnConfig, DockerContainer container, bool haveAuthData) override; + void updateProtocolPage(const QJsonObject &openvpnConfig, DockerContainer container, bool haveAuthData, bool isThirdPartyConfig) override; QJsonObject getProtocolConfigFromPage(QJsonObject oldConfig) override; private: diff --git a/client/ui/pages_logic/protocols/OtherProtocolsLogic.cpp b/client/ui/pages_logic/protocols/OtherProtocolsLogic.cpp index 50b17a46..e10956a4 100644 --- a/client/ui/pages_logic/protocols/OtherProtocolsLogic.cpp +++ b/client/ui/pages_logic/protocols/OtherProtocolsLogic.cpp @@ -36,7 +36,7 @@ OtherProtocolsLogic::~OtherProtocolsLogic() #endif } -void OtherProtocolsLogic::updateProtocolPage(const QJsonObject &config, DockerContainer container, bool haveAuthData) +void OtherProtocolsLogic::updateProtocolPage(const QJsonObject &config, DockerContainer container, bool haveAuthData, bool isThirdPartyConfig) { set_labelTftpUserNameText(config.value(config_key::userName).toString()); set_labelTftpPasswordText(config.value(config_key::password).toString(protocols::sftp::defaultUserName)); diff --git a/client/ui/pages_logic/protocols/OtherProtocolsLogic.h b/client/ui/pages_logic/protocols/OtherProtocolsLogic.h index 508c2914..e4d3c6ab 100644 --- a/client/ui/pages_logic/protocols/OtherProtocolsLogic.h +++ b/client/ui/pages_logic/protocols/OtherProtocolsLogic.h @@ -27,7 +27,7 @@ public: explicit OtherProtocolsLogic(UiLogic *uiLogic, QObject *parent = nullptr); ~OtherProtocolsLogic(); - void updateProtocolPage(const QJsonObject &config, DockerContainer container, bool haveAuthData) override; + void updateProtocolPage(const QJsonObject &config, DockerContainer container, bool haveAuthData, bool isThirdPartyConfig) override; //QJsonObject getProtocolConfigFromPage(QJsonObject oldConfig) override; private: diff --git a/client/ui/pages_logic/protocols/PageProtocolLogicBase.h b/client/ui/pages_logic/protocols/PageProtocolLogicBase.h index ab66f8a9..75e0ac2e 100644 --- a/client/ui/pages_logic/protocols/PageProtocolLogicBase.h +++ b/client/ui/pages_logic/protocols/PageProtocolLogicBase.h @@ -17,7 +17,7 @@ public: explicit PageProtocolLogicBase(UiLogic *uiLogic, QObject *parent = nullptr); ~PageProtocolLogicBase() = default; - virtual void updateProtocolPage(const QJsonObject &config, DockerContainer container, bool haveAuthData) {} + virtual void updateProtocolPage(const QJsonObject &config, DockerContainer container, bool haveAuthData, bool isThirdPartyConfig) {} virtual QJsonObject getProtocolConfigFromPage(QJsonObject oldConfig) { return QJsonObject(); } }; diff --git a/client/ui/pages_logic/protocols/ShadowSocksLogic.cpp b/client/ui/pages_logic/protocols/ShadowSocksLogic.cpp index 1d5da936..e23f0bf8 100644 --- a/client/ui/pages_logic/protocols/ShadowSocksLogic.cpp +++ b/client/ui/pages_logic/protocols/ShadowSocksLogic.cpp @@ -21,7 +21,7 @@ ShadowSocksLogic::ShadowSocksLogic(UiLogic *logic, QObject *parent): } -void ShadowSocksLogic::updateProtocolPage(const QJsonObject &ssConfig, DockerContainer container, bool haveAuthData) +void ShadowSocksLogic::updateProtocolPage(const QJsonObject &ssConfig, DockerContainer container, bool haveAuthData, bool isThirdPartyConfig) { set_pageEnabled(haveAuthData); set_pushButtonSaveVisible(haveAuthData); diff --git a/client/ui/pages_logic/protocols/ShadowSocksLogic.h b/client/ui/pages_logic/protocols/ShadowSocksLogic.h index 4e566825..6d2fbd1b 100644 --- a/client/ui/pages_logic/protocols/ShadowSocksLogic.h +++ b/client/ui/pages_logic/protocols/ShadowSocksLogic.h @@ -26,7 +26,7 @@ public: explicit ShadowSocksLogic(UiLogic *uiLogic, QObject *parent = nullptr); ~ShadowSocksLogic() = default; - void updateProtocolPage(const QJsonObject &ssConfig, DockerContainer container, bool haveAuthData) override; + void updateProtocolPage(const QJsonObject &ssConfig, DockerContainer container, bool haveAuthData, bool isThirdPartyConfig) override; QJsonObject getProtocolConfigFromPage(QJsonObject oldConfig) override; private: diff --git a/client/ui/qml/Pages/Protocols/PageProtoOpenVPN.qml b/client/ui/qml/Pages/Protocols/PageProtoOpenVPN.qml index 9c00172c..5722c68b 100644 --- a/client/ui/qml/Pages/Protocols/PageProtoOpenVPN.qml +++ b/client/ui/qml/Pages/Protocols/PageProtoOpenVPN.qml @@ -36,370 +36,389 @@ PageProtocolBase { ColumnLayout { id: content - enabled: logic.pageEnabled anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right - - LabelType { - id: lb_subnet - height: 21 - text: qsTr("VPN Addresses Subnet") - } - TextFieldType { - id: tf_subnet - - implicitWidth: parent.width - height: 31 - text: logic.lineEditSubnetText - onEditingFinished: { - logic.lineEditSubnetText = text - } - } - - // - LabelType { - id: lb_proto - Layout.topMargin: 20 - height: 21 - text: qsTr("Network protocol") - } - Rectangle { - id: rect_proto - implicitWidth: root.width - 60 - height: 71 - border.width: 1 - border.color: "lightgray" - radius: 2 - RadioButtonType { - x: 10 - y: 40 - width: 171 - height: 19 - text: qsTr("TCP") - enabled: logic.radioButtonTcpEnabled - checked: logic.radioButtonTcpChecked - onCheckedChanged: { - UiLogic.radioButtonTcpChecked = checked - } - } - RadioButtonType { - x: 10 - y: 10 - width: 171 - height: 19 - text: qsTr("UDP") - checked: logic.radioButtonUdpChecked - onCheckedChanged: { - logic.radioButtonUdpChecked = checked - } - enabled: logic.radioButtonUdpEnabled - } - } - - // - RowLayout { - Layout.topMargin: 10 - Layout.fillWidth: true + ColumnLayout { + visible: !logic.isThirdPartyConfig + enabled: logic.pageEnabled LabelType { - id: lb_port - height: 31 - text: qsTr("Port") - Layout.preferredWidth: root.width / 2 - 10 + id: lb_subnet + height: 21 + text: qsTr("VPN Addresses Subnet") } TextFieldType { - id: tf_port + id: tf_subnet + + implicitWidth: parent.width + height: 31 + text: logic.lineEditSubnetText + onEditingFinished: { + logic.lineEditSubnetText = text + } + } + + // + LabelType { + id: lb_proto + Layout.topMargin: 20 + height: 21 + text: qsTr("Network protocol") + } + Rectangle { + id: rect_proto + implicitWidth: root.width - 60 + height: 71 + border.width: 1 + border.color: "lightgray" + radius: 2 + RadioButtonType { + x: 10 + y: 40 + width: 171 + height: 19 + text: qsTr("TCP") + enabled: logic.radioButtonTcpEnabled + checked: logic.radioButtonTcpChecked + onCheckedChanged: { + UiLogic.radioButtonTcpChecked = checked + } + } + RadioButtonType { + x: 10 + y: 10 + width: 171 + height: 19 + text: qsTr("UDP") + checked: logic.radioButtonUdpChecked + onCheckedChanged: { + logic.radioButtonUdpChecked = checked + } + enabled: logic.radioButtonUdpEnabled + } + } + + // + RowLayout { + Layout.topMargin: 10 Layout.fillWidth: true + LabelType { + id: lb_port + height: 31 + text: qsTr("Port") + Layout.preferredWidth: root.width / 2 - 10 + } + TextFieldType { + id: tf_port + Layout.fillWidth: true + + height: 31 + text: logic.lineEditPortText + onEditingFinished: { + logic.lineEditPortText = text + } + enabled: logic.lineEditPortEnabled + } + } + + + + // + CheckBoxType { + id: check_auto_enc + + implicitWidth: parent.width + height: 21 + text: qsTr("Auto-negotiate encryption") + checked: logic.checkBoxAutoEncryptionChecked + onCheckedChanged: { + logic.checkBoxAutoEncryptionChecked = checked + } + onClicked: { + logic.checkBoxAutoEncryptionClicked() + } + } + + // + LabelType { + id: lb_cipher + height: 21 + text: qsTr("Cipher") + } + + ComboBoxType { + id: cb_cipher + implicitWidth: parent.width height: 31 - text: logic.lineEditPortText - onEditingFinished: { - logic.lineEditPortText = text - } - enabled: logic.lineEditPortEnabled - } - } - - - - // - CheckBoxType { - id: check_auto_enc - - implicitWidth: parent.width - height: 21 - text: qsTr("Auto-negotiate encryption") - checked: logic.checkBoxAutoEncryptionChecked - onCheckedChanged: { - logic.checkBoxAutoEncryptionChecked = checked - } - onClicked: { - logic.checkBoxAutoEncryptionClicked() - } - } - - // - LabelType { - id: lb_cipher - height: 21 - text: qsTr("Cipher") - } - - ComboBoxType { - id: cb_cipher - implicitWidth: parent.width - - height: 31 - model: [ - qsTr("AES-256-GCM"), - qsTr("AES-192-GCM"), - qsTr("AES-128-GCM"), - qsTr("AES-256-CBC"), - qsTr("AES-192-CBC"), - qsTr("AES-128-CBC"), - qsTr("ChaCha20-Poly1305"), - qsTr("ARIA-256-CBC"), - qsTr("CAMELLIA-256-CBC"), - qsTr("none") - ] - currentIndex: { - for (let i = 0; i < model.length; ++i) { - if (logic.comboBoxVpnCipherText === model[i]) { - return i + model: [ + qsTr("AES-256-GCM"), + qsTr("AES-192-GCM"), + qsTr("AES-128-GCM"), + qsTr("AES-256-CBC"), + qsTr("AES-192-CBC"), + qsTr("AES-128-CBC"), + qsTr("ChaCha20-Poly1305"), + qsTr("ARIA-256-CBC"), + qsTr("CAMELLIA-256-CBC"), + qsTr("none") + ] + currentIndex: { + for (let i = 0; i < model.length; ++i) { + if (logic.comboBoxVpnCipherText === model[i]) { + return i + } } + return -1 } - return -1 + onCurrentTextChanged: { + logic.comboBoxVpnCipherText = currentText + } + enabled: !check_auto_enc.checked } - onCurrentTextChanged: { - logic.comboBoxVpnCipherText = currentText - } - enabled: !check_auto_enc.checked - } - // - LabelType { - id: lb_hash - height: 21 - Layout.topMargin: 20 - text: qsTr("Hash") - } - ComboBoxType { - id: cb_hash - height: 31 - implicitWidth: parent.width - model: [ - qsTr("SHA512"), - qsTr("SHA384"), - qsTr("SHA256"), - qsTr("SHA3-512"), - qsTr("SHA3-384"), - qsTr("SHA3-256"), - qsTr("whirlpool"), - qsTr("BLAKE2b512"), - qsTr("BLAKE2s256"), - qsTr("SHA1") - ] - currentIndex: { - for (let i = 0; i < model.length; ++i) { - if (logic.comboBoxVpnHashText === model[i]) { - return i + // + LabelType { + id: lb_hash + height: 21 + Layout.topMargin: 20 + text: qsTr("Hash") + } + ComboBoxType { + id: cb_hash + height: 31 + implicitWidth: parent.width + model: [ + qsTr("SHA512"), + qsTr("SHA384"), + qsTr("SHA256"), + qsTr("SHA3-512"), + qsTr("SHA3-384"), + qsTr("SHA3-256"), + qsTr("whirlpool"), + qsTr("BLAKE2b512"), + qsTr("BLAKE2s256"), + qsTr("SHA1") + ] + currentIndex: { + for (let i = 0; i < model.length; ++i) { + if (logic.comboBoxVpnHashText === model[i]) { + return i + } } + return -1 } - return -1 - } - onCurrentTextChanged: { - logic.comboBoxVpnHashText = currentText - } - enabled: !check_auto_enc.checked - } - - CheckBoxType { - id: check_tls - implicitWidth: parent.width - Layout.topMargin: 20 - height: 21 - text: qsTr("Enable TLS auth") - checked: logic.checkBoxTlsAuthChecked - onCheckedChanged: { - logic.checkBoxTlsAuthChecked = checked + onCurrentTextChanged: { + logic.comboBoxVpnHashText = currentText + } + enabled: !check_auto_enc.checked } - } + CheckBoxType { + id: check_tls + implicitWidth: parent.width + Layout.topMargin: 20 + height: 21 + text: qsTr("Enable TLS auth") + checked: logic.checkBoxTlsAuthChecked + onCheckedChanged: { + logic.checkBoxTlsAuthChecked = checked + } - CheckBoxType { - id: check_block_dns - implicitWidth: parent.width - height: 21 - text: qsTr("Block DNS requests outside of VPN") - checked: logic.checkBoxBlockDnsChecked - onCheckedChanged: { - logic.checkBoxBlockDnsChecked = checked - } - } - - - BasicButtonType { - id: pb_client_config - - implicitWidth: parent.width - height: 21 - text: qsTr("Additional client config commands →") - background: Item { - anchors.fill: parent } - contentItem: Text { - anchors.fill: parent - font.family: "Lato" - font.styleName: "normal" - font.pixelSize: 16 - color: "#15CDCB"; - text: pb_client_config.text - horizontalAlignment: Text.AlignLeft - verticalAlignment: Text.AlignVCenter + CheckBoxType { + id: check_block_dns + implicitWidth: parent.width + height: 21 + text: qsTr("Block DNS requests outside of VPN") + checked: logic.checkBoxBlockDnsChecked + onCheckedChanged: { + logic.checkBoxBlockDnsChecked = checked + } } - antialiasing: true - checkable: true - checked: StartPageLogic.pushButtonConnectKeyChecked - } - Rectangle { - id: rect_client_conf - implicitWidth: root.width - 60 - height: 101 - border.width: 1 - border.color: "lightgray" - radius: 2 - visible: pb_client_config.checked - ScrollView { - anchors.fill: parent - TextArea { - id: te_client_config + BasicButtonType { + id: pb_client_config + + implicitWidth: parent.width + height: 21 + text: qsTr("Additional client config commands →") + background: Item { + anchors.fill: parent + } + + contentItem: Text { + anchors.fill: parent font.family: "Lato" font.styleName: "normal" font.pixelSize: 16 - color: "#181922" - text: logic.textAreaAdditionalClientConfig - onEditingFinished: { - logic.textAreaAdditionalClientConfig = text + color: "#15CDCB"; + text: pb_client_config.text + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + antialiasing: true + checkable: true + checked: StartPageLogic.pushButtonConnectKeyChecked + } + + Rectangle { + id: rect_client_conf + implicitWidth: root.width - 60 + height: 101 + border.width: 1 + border.color: "lightgray" + radius: 2 + visible: pb_client_config.checked + + ScrollView { + anchors.fill: parent + TextArea { + id: te_client_config + font.family: "Lato" + font.styleName: "normal" + font.pixelSize: 16 + color: "#181922" + text: logic.textAreaAdditionalClientConfig + onEditingFinished: { + logic.textAreaAdditionalClientConfig = text + } } } + + } - } + BasicButtonType { + id: pb_server_config + implicitWidth: parent.width + height: 21 + text: qsTr("Additional server config commands →") + background: Item { + anchors.fill: parent + } - BasicButtonType { - id: pb_server_config - - implicitWidth: parent.width - height: 21 - text: qsTr("Additional server config commands →") - background: Item { - anchors.fill: parent - } - - contentItem: Text { - anchors.fill: parent - font.family: "Lato" - font.styleName: "normal" - font.pixelSize: 16 - color: "#15CDCB"; - text: pb_server_config.text - horizontalAlignment: Text.AlignLeft - verticalAlignment: Text.AlignVCenter - } - antialiasing: true - checkable: true - checked: StartPageLogic.pushButtonConnectKeyChecked - } - - Rectangle { - id: rect_server_conf - implicitWidth: root.width - 60 - height: 101 - border.width: 1 - border.color: "lightgray" - radius: 2 - visible: pb_server_config.checked - - ScrollView { - anchors.fill: parent - TextArea { - id: te_server_config + contentItem: Text { + anchors.fill: parent font.family: "Lato" font.styleName: "normal" font.pixelSize: 16 - color: "#181922" - text: logic.textAreaAdditionalServerConfig - onEditingFinished: { - logic.textAreaAdditionalServerConfig = text - } + color: "#15CDCB"; + text: pb_server_config.text + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter } + antialiasing: true + checkable: true + checked: StartPageLogic.pushButtonConnectKeyChecked } + Rectangle { + id: rect_server_conf + implicitWidth: root.width - 60 + height: 101 + border.width: 1 + border.color: "lightgray" + radius: 2 + visible: pb_server_config.checked - } + ScrollView { + anchors.fill: parent + TextArea { + id: te_server_config + font.family: "Lato" + font.styleName: "normal" + font.pixelSize: 16 + color: "#181922" + text: logic.textAreaAdditionalServerConfig + onEditingFinished: { + logic.textAreaAdditionalServerConfig = text + } + } + } - LabelType { - id: label_proto_openvpn_info - height: 41 - visible: logic.labelProtoOpenVpnInfoVisible - text: logic.labelProtoOpenVpnInfoText - } + } - Rectangle { - id: it_save - implicitWidth: parent.width - Layout.topMargin: 20 - height: 40 + LabelType { + id: label_proto_openvpn_info - BlueButtonType { - id: pb_save - z: 1 + height: 41 + visible: logic.labelProtoOpenVpnInfoVisible + text: logic.labelProtoOpenVpnInfoText + } + + Rectangle { + id: it_save + implicitWidth: parent.width + Layout.topMargin: 20 height: 40 - text: qsTr("Save and restart VPN") - width: parent.width - visible: logic.pushButtonSaveVisible - onClicked: { - logic.onPushButtonProtoOpenVpnSaveClicked() - } - } - ProgressBar { - id: progress_save - anchors.fill: pb_save - from: 0 - to: logic.progressBarResetMaximium - value: logic.progressBarResetValue - visible: logic.progressBarResetVisible - background: Rectangle { - implicitWidth: parent.width - implicitHeight: parent.height - color: "#100A44" - radius: 4 - } - - contentItem: Item { - implicitWidth: parent.width - implicitHeight: parent.height - Rectangle { - width: progress_save.visualPosition * parent.width - height: parent.height - radius: 4 - color: Qt.rgba(255, 255, 255, 0.15); + BlueButtonType { + id: pb_save + z: 1 + height: 40 + text: qsTr("Save and restart VPN") + width: parent.width + visible: logic.pushButtonSaveVisible + onClicked: { + logic.onPushButtonProtoOpenVpnSaveClicked() } } + + ProgressBar { + id: progress_save + anchors.fill: pb_save + from: 0 + to: logic.progressBarResetMaximium + value: logic.progressBarResetValue + visible: logic.progressBarResetVisible + background: Rectangle { + implicitWidth: parent.width + implicitHeight: parent.height + color: "#100A44" + radius: 4 + } + + contentItem: Item { + implicitWidth: parent.width + implicitHeight: parent.height + Rectangle { + width: progress_save.visualPosition * parent.width + height: parent.height + radius: 4 + color: Qt.rgba(255, 255, 255, 0.15); + } + } + } + } } - } + ColumnLayout { + visible: logic.isThirdPartyConfig + TextAreaType { + id: ta_config + Layout.topMargin: 5 + Layout.bottomMargin: 20 + Layout.fillWidth: true + Layout.leftMargin: 1 + Layout.rightMargin: 1 + Layout.preferredHeight: fl.height - 70 + flickableDirection: Flickable.AutoFlickIfNeeded + + textArea.readOnly: true + textArea.text: logic.openVpnLastConfigText + } + } + } } } From 1a443076644d51988d8a3fdc09a32ae820e90287 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Tue, 1 Nov 2022 23:24:58 +0300 Subject: [PATCH 25/43] added protocol_defs for new config keywords --- client/protocols/protocols_defs.h | 5 +++++ client/settings.cpp | 2 +- client/ui/pages_logic/StartPageLogic.cpp | 4 ++-- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/client/protocols/protocols_defs.h b/client/protocols/protocols_defs.h index c158822d..9e121f56 100644 --- a/client/protocols/protocols_defs.h +++ b/client/protocols/protocols_defs.h @@ -58,6 +58,11 @@ constexpr char additional_server_config[] = "additional_server_config"; // proto config keys constexpr char last_config[] = "last_config"; + +constexpr char is_third_party_config[] = "is_third_party_config"; + +constexpr char openvpn[] = "openvpn"; + } namespace protocols { diff --git a/client/settings.cpp b/client/settings.cpp index ccef9e40..8202eb27 100644 --- a/client/settings.cpp +++ b/client/settings.cpp @@ -317,5 +317,5 @@ bool Settings::isThirdPartyConfig(int serverIndex) const { if (serverIndex < 0) return false; const QJsonObject &s = server(serverIndex); - return s.value("isThirdPartyConfig").toBool(); + return s.value(config_key::is_third_party_config).toBool(); } diff --git a/client/ui/pages_logic/StartPageLogic.cpp b/client/ui/pages_logic/StartPageLogic.cpp index 6a6333e9..897caaaf 100644 --- a/client/ui/pages_logic/StartPageLogic.cpp +++ b/client/ui/pages_logic/StartPageLogic.cpp @@ -249,7 +249,7 @@ bool StartPageLogic::importConnectionFromOpenVpnConfig(const QString &config) QJsonObject containers; containers.insert(config_key::container, QJsonValue("amnezia-openvpn")); - containers.insert("openvpn", QJsonValue(lastConfig)); + containers.insert(config_key::openvpn, QJsonValue(lastConfig)); QJsonArray arr; arr.push_back(containers); @@ -275,7 +275,7 @@ bool StartPageLogic::importConnectionFromOpenVpnConfig(const QString &config) o[config_key::hostName] = hostNameMatch.captured(0).split(" ").at(1); } - o["isThirdPartyConfig"] = true; + o[config_key::is_third_party_config] = true; return importConnection(o); } From 99a6cd82b2a01fecc984ae776682b0a0733adac4 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Thu, 3 Nov 2022 23:39:58 +0300 Subject: [PATCH 26/43] added import of configs in wireguard format --- client/protocols/protocols_defs.h | 1 + client/ui/pages_logic/StartPageLogic.cpp | 47 ++++++++++++++++++- client/ui/pages_logic/StartPageLogic.h | 1 + .../qml/Pages/Protocols/PageProtoOpenVPN.qml | 1 + 4 files changed, 48 insertions(+), 2 deletions(-) diff --git a/client/protocols/protocols_defs.h b/client/protocols/protocols_defs.h index 9e121f56..462c7c51 100644 --- a/client/protocols/protocols_defs.h +++ b/client/protocols/protocols_defs.h @@ -62,6 +62,7 @@ constexpr char last_config[] = "last_config"; constexpr char is_third_party_config[] = "is_third_party_config"; constexpr char openvpn[] = "openvpn"; +constexpr char wireguard[] = "wireguard"; } diff --git a/client/ui/pages_logic/StartPageLogic.cpp b/client/ui/pages_logic/StartPageLogic.cpp index 897caaaf..47b893bc 100644 --- a/client/ui/pages_logic/StartPageLogic.cpp +++ b/client/ui/pages_logic/StartPageLogic.cpp @@ -24,11 +24,16 @@ QString checkConfigFormat(const QString &config) const QString openVpnConfigPatternDriver1 = "dev tun"; const QString openVpnConfigPatternDriver2 = "dev tap"; + const QString wireguardConfigPatternSectionInterface = "[Interface]"; + const QString wireguardConfigPatternSectionPeer = "[Peer]"; + if (config.contains(openVpnConfigPatternCli) && (config.contains(openVpnConfigPatternProto1) || config.contains(openVpnConfigPatternProto2)) && (config.contains(openVpnConfigPatternDriver1) || config.contains(openVpnConfigPatternDriver2))) { return "OpenVpn"; - } + } else if (config.contains(wireguardConfigPatternSectionInterface) && + config.contains(wireguardConfigPatternSectionPeer)) + return "Wireguard"; return "Amnezia"; } @@ -155,7 +160,7 @@ void StartPageLogic::onPushButtonImport() void StartPageLogic::onPushButtonImportOpenFile() { QString fileName = QFileDialog::getOpenFileName(nullptr, tr("Open profile"), - QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), "*.vpn *.ovpn"); + QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), "*.vpn *.ovpn *.conf"); if (fileName.isEmpty()) return; @@ -166,6 +171,8 @@ void StartPageLogic::onPushButtonImportOpenFile() auto configFormat = checkConfigFormat(QString(data)); if (configFormat == "OpenVpn") { importConnectionFromOpenVpnConfig(QString(data)); + } else if (configFormat == "Wireguard") { + importConnectionFromWireguardConfig(QString(data)); } else { importConnectionFromCode(QString(data)); } @@ -279,3 +286,39 @@ bool StartPageLogic::importConnectionFromOpenVpnConfig(const QString &config) return importConnection(o); } + +bool StartPageLogic::importConnectionFromWireguardConfig(const QString &config) +{ + QJsonObject lastConfig; + lastConfig[config_key::config] = config; + + const static QRegularExpression hostNameAndPortRegExp("Endpoint = (\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b):([0-9]*)"); + QRegularExpressionMatch hostNameAndPortMatch = hostNameAndPortRegExp.match(config); + QString hostName; + QString port; + if (hostNameAndPortMatch.hasMatch()) { + hostName = hostNameAndPortMatch.captured(1); + port = hostNameAndPortMatch.captured(2); + } + + + QJsonObject wireguardConfig; + wireguardConfig[config_key::last_config] = lastConfig; + wireguardConfig[config_key::port] = port; + wireguardConfig[config_key::transport_proto] = "udp"; + + QJsonObject containers; + containers.insert(config_key::container, QJsonValue("amnezia-wireguard")); + containers.insert(config_key::wireguard, QJsonValue(wireguardConfig)); + + QJsonArray arr; + arr.push_back(containers); + + QJsonObject o; + o[config_key::containers] = arr; + o[config_key::defaultContainer] = "amnezia-wireguard"; + o[config_key::description] = "Wireguard server"; + o[config_key::hostName] = hostName; + + return importConnection(o); +} diff --git a/client/ui/pages_logic/StartPageLogic.h b/client/ui/pages_logic/StartPageLogic.h index 830c9100..d5e72edc 100644 --- a/client/ui/pages_logic/StartPageLogic.h +++ b/client/ui/pages_logic/StartPageLogic.h @@ -33,6 +33,7 @@ public: bool importConnectionFromCode(QString code); bool importConnectionFromQr(const QByteArray &data); bool importConnectionFromOpenVpnConfig(const QString &config); + bool importConnectionFromWireguardConfig(const QString &config); public: explicit StartPageLogic(UiLogic *uiLogic, QObject *parent = nullptr); diff --git a/client/ui/qml/Pages/Protocols/PageProtoOpenVPN.qml b/client/ui/qml/Pages/Protocols/PageProtoOpenVPN.qml index 5722c68b..3ca79bdc 100644 --- a/client/ui/qml/Pages/Protocols/PageProtoOpenVPN.qml +++ b/client/ui/qml/Pages/Protocols/PageProtoOpenVPN.qml @@ -43,6 +43,7 @@ PageProtocolBase { ColumnLayout { visible: !logic.isThirdPartyConfig enabled: logic.pageEnabled + LabelType { id: lb_subnet height: 21 From 6941b7463e1b5878438a1136496e429c44d24611 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Fri, 4 Nov 2022 23:31:39 +0300 Subject: [PATCH 27/43] added display of wireguard configs, such a config will be displayed as raw text --- client/client.pro | 2 + client/protocols/protocols_defs.h | 2 +- client/resources.qrc | 1 + client/settings.cpp | 2 +- client/ui/pages_logic/StartPageLogic.cpp | 24 +++++--- .../ui/pages_logic/protocols/OpenVpnLogic.cpp | 5 +- .../pages_logic/protocols/WireGuardLogic.cpp | 30 ++++++++++ .../ui/pages_logic/protocols/WireGuardLogic.h | 26 ++++++++ .../Pages/Protocols/PageProtoWireGuard.qml | 60 +++++++++++++++++++ client/ui/uilogic.cpp | 4 +- 10 files changed, 142 insertions(+), 14 deletions(-) create mode 100644 client/ui/pages_logic/protocols/WireGuardLogic.cpp create mode 100644 client/ui/pages_logic/protocols/WireGuardLogic.h create mode 100644 client/ui/qml/Pages/Protocols/PageProtoWireGuard.qml diff --git a/client/client.pro b/client/client.pro index bf9db7ed..032fe5d3 100644 --- a/client/client.pro +++ b/client/client.pro @@ -74,6 +74,7 @@ HEADERS += \ ui/pages_logic/protocols/OtherProtocolsLogic.h \ ui/pages_logic/protocols/PageProtocolLogicBase.h \ ui/pages_logic/protocols/ShadowSocksLogic.h \ + ui/pages_logic/protocols/WireGuardLogic.h \ ui/property_helper.h \ ui/models/servers_model.h \ ui/uilogic.h \ @@ -136,6 +137,7 @@ SOURCES += \ ui/pages_logic/protocols/PageProtocolLogicBase.cpp \ ui/pages_logic/protocols/ShadowSocksLogic.cpp \ ui/models/servers_model.cpp \ + ui/pages_logic/protocols/WireGuardLogic.cpp \ ui/uilogic.cpp \ ui/qautostart.cpp \ ui/models/sites_model.cpp \ diff --git a/client/protocols/protocols_defs.h b/client/protocols/protocols_defs.h index 462c7c51..c5f15d5b 100644 --- a/client/protocols/protocols_defs.h +++ b/client/protocols/protocols_defs.h @@ -59,7 +59,7 @@ constexpr char additional_server_config[] = "additional_server_config"; // proto config keys constexpr char last_config[] = "last_config"; -constexpr char is_third_party_config[] = "is_third_party_config"; +constexpr char isThirdPartyConfig[] = "isThirdPartyConfig"; constexpr char openvpn[] = "openvpn"; constexpr char wireguard[] = "wireguard"; diff --git a/client/resources.qrc b/client/resources.qrc index a7e67e39..47d10f7c 100644 --- a/client/resources.qrc +++ b/client/resources.qrc @@ -160,5 +160,6 @@ ui/qml/Pages/PageQrDecoderIos.qml server_scripts/website_tor/Dockerfile ui/qml/Pages/PageViewConfig.qml + ui/qml/Pages/Protocols/PageProtoWireGuard.qml diff --git a/client/settings.cpp b/client/settings.cpp index 8202eb27..fc3a5825 100644 --- a/client/settings.cpp +++ b/client/settings.cpp @@ -317,5 +317,5 @@ bool Settings::isThirdPartyConfig(int serverIndex) const { if (serverIndex < 0) return false; const QJsonObject &s = server(serverIndex); - return s.value(config_key::is_third_party_config).toBool(); + return s.value(config_key::isThirdPartyConfig).toBool(); } diff --git a/client/ui/pages_logic/StartPageLogic.cpp b/client/ui/pages_logic/StartPageLogic.cpp index 47b893bc..37b557e0 100644 --- a/client/ui/pages_logic/StartPageLogic.cpp +++ b/client/ui/pages_logic/StartPageLogic.cpp @@ -267,22 +267,22 @@ bool StartPageLogic::importConnectionFromOpenVpnConfig(const QString &config) o[config_key::description] = "OpenVpn server"; - const static QRegularExpression dnsRegExp("dhcp-option DNS \\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b"); + const static QRegularExpression dnsRegExp("dhcp-option DNS (\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b)"); QRegularExpressionMatchIterator dnsMatch = dnsRegExp.globalMatch(config); if (dnsMatch.hasNext()) { - o[config_key::dns1] = dnsMatch.next().captured(0).split(" ").at(2); + o[config_key::dns1] = dnsMatch.next().captured(1); } if (dnsMatch.hasNext()) { - o[config_key::dns2] = dnsMatch.next().captured(0).split(" ").at(2); + o[config_key::dns2] = dnsMatch.next().captured(1); } - const static QRegularExpression hostNameRegExp("remote \\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b"); + const static QRegularExpression hostNameRegExp("remote (\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b)"); QRegularExpressionMatch hostNameMatch = hostNameRegExp.match(config); if (hostNameMatch.hasMatch()) { - o[config_key::hostName] = hostNameMatch.captured(0).split(" ").at(1); + o[config_key::hostName] = hostNameMatch.captured(1); } - o[config_key::is_third_party_config] = true; + o[config_key::isThirdPartyConfig] = true; return importConnection(o); } @@ -301,9 +301,8 @@ bool StartPageLogic::importConnectionFromWireguardConfig(const QString &config) port = hostNameAndPortMatch.captured(2); } - QJsonObject wireguardConfig; - wireguardConfig[config_key::last_config] = lastConfig; + wireguardConfig[config_key::last_config] = QString(QJsonDocument(lastConfig).toJson());; wireguardConfig[config_key::port] = port; wireguardConfig[config_key::transport_proto] = "udp"; @@ -318,7 +317,16 @@ bool StartPageLogic::importConnectionFromWireguardConfig(const QString &config) o[config_key::containers] = arr; o[config_key::defaultContainer] = "amnezia-wireguard"; o[config_key::description] = "Wireguard server"; + + const static QRegularExpression dnsRegExp("DNS = (\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b).*(\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b)"); + QRegularExpressionMatch dnsMatch = dnsRegExp.match(config); + if (dnsMatch.hasMatch()) { + o[config_key::dns1] = dnsMatch.captured(1); + o[config_key::dns2] = dnsMatch.captured(2); + } + o[config_key::hostName] = hostName; + o[config_key::isThirdPartyConfig] = true; return importConnection(o); } diff --git a/client/ui/pages_logic/protocols/OpenVpnLogic.cpp b/client/ui/pages_logic/protocols/OpenVpnLogic.cpp index 7d726670..fbbf1c86 100644 --- a/client/ui/pages_logic/protocols/OpenVpnLogic.cpp +++ b/client/ui/pages_logic/protocols/OpenVpnLogic.cpp @@ -91,11 +91,12 @@ void OpenVpnLogic::updateProtocolPage(const QJsonObject &openvpnConfig, DockerCo auto lastConfig = openvpnConfig.value(config_key::last_config).toString(); auto lastConfigJson = QJsonDocument::fromJson(lastConfig.toUtf8()).object(); QStringList lines = lastConfigJson.value(config_key::config).toString().replace("\r", "").split("\n"); + QString openVpnLastConfigText; for (const QString &l: lines) { - m_openVpnLastConfigText.append(l + "\n"); + openVpnLastConfigText.append(l + "\n"); } - emit openVpnLastConfigTextChanged(m_openVpnLastConfigText); + set_openVpnLastConfigText(m_openVpnLastConfigText); set_isThirdPartyConfig(isThirdPartyConfig); } diff --git a/client/ui/pages_logic/protocols/WireGuardLogic.cpp b/client/ui/pages_logic/protocols/WireGuardLogic.cpp new file mode 100644 index 00000000..d4c18df6 --- /dev/null +++ b/client/ui/pages_logic/protocols/WireGuardLogic.cpp @@ -0,0 +1,30 @@ +#include "WireGuardLogic.h" +#include "core/servercontroller.h" +#include +#include "../../uilogic.h" + +using namespace amnezia; +using namespace PageEnumNS; + +WireGuardLogic::WireGuardLogic(UiLogic *logic, QObject *parent): + PageProtocolLogicBase(logic, parent) +{ + +} + +void WireGuardLogic::updateProtocolPage(const QJsonObject &wireGuardConfig, DockerContainer container, bool haveAuthData, bool isThirdPartyConfig) +{ + qDebug() << "WireGuardLogic::updateProtocolPage"; + + auto lastConfigJsonDoc = QJsonDocument::fromJson(wireGuardConfig.value(config_key::last_config).toString().toUtf8()); + auto lastConfigJson = lastConfigJsonDoc.object(); + + QString wireGuardLastConfigText; + QStringList lines = lastConfigJson.value(config_key::config).toString().replace("\r", "").split("\n"); + for (const QString &l: lines) { + wireGuardLastConfigText.append(l + "\n"); + } + + set_wireGuardLastConfigText(wireGuardLastConfigText); + set_isThirdPartyConfig(isThirdPartyConfig); +} diff --git a/client/ui/pages_logic/protocols/WireGuardLogic.h b/client/ui/pages_logic/protocols/WireGuardLogic.h new file mode 100644 index 00000000..19e9f90d --- /dev/null +++ b/client/ui/pages_logic/protocols/WireGuardLogic.h @@ -0,0 +1,26 @@ +#ifndef WIREGUARDLOGIC_H +#define WIREGUARDLOGIC_H + +#include "PageProtocolLogicBase.h" + +class UiLogic; + +class WireGuardLogic : public PageProtocolLogicBase +{ + Q_OBJECT + + AUTO_PROPERTY(QString, wireGuardLastConfigText) + AUTO_PROPERTY(bool, isThirdPartyConfig) + +public: + explicit WireGuardLogic(UiLogic *uiLogic, QObject *parent = nullptr); + ~WireGuardLogic() = default; + + void updateProtocolPage(const QJsonObject &wireGuardConfig, DockerContainer container, bool haveAuthData, bool isThirdPartyConfig) override; + +private: + UiLogic *m_uiLogic; + +}; + +#endif // WIREGUARDLOGIC_H diff --git a/client/ui/qml/Pages/Protocols/PageProtoWireGuard.qml b/client/ui/qml/Pages/Protocols/PageProtoWireGuard.qml new file mode 100644 index 00000000..e4dde50e --- /dev/null +++ b/client/ui/qml/Pages/Protocols/PageProtoWireGuard.qml @@ -0,0 +1,60 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Layouts 1.15 +import ProtocolEnum 1.0 +import "../" +import "../../Controls" +import "../../Config" + +PageProtocolBase { + id: root + protocol: ProtocolEnum.WireGuard + logic: UiLogic.protocolLogic(protocol) + + BackButton { + id: back + } + Caption { + id: caption + text: qsTr("WireGuard Settings") + } + + Flickable { + id: fl + width: root.width + anchors.top: caption.bottom + anchors.topMargin: 20 + anchors.bottom: parent.bottom + anchors.bottomMargin: 20 + anchors.left: root.left + anchors.leftMargin: 30 + anchors.right: root.right + anchors.rightMargin: 30 + + contentHeight: content.height + clip: true + + ColumnLayout { + id: content + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + TextAreaType { + id: ta_config + + Layout.topMargin: 5 + Layout.bottomMargin: 20 + Layout.fillWidth: true + Layout.leftMargin: 1 + Layout.rightMargin: 1 + Layout.preferredHeight: fl.height - 70 + flickableDirection: Flickable.AutoFlickIfNeeded + + textArea.readOnly: true + textArea.text: logic.wireGuardLastConfigText + } + } + } + +} diff --git a/client/ui/uilogic.cpp b/client/ui/uilogic.cpp index e1468da7..81492023 100644 --- a/client/ui/uilogic.cpp +++ b/client/ui/uilogic.cpp @@ -71,7 +71,7 @@ #include "pages_logic/protocols/OpenVpnLogic.h" #include "pages_logic/protocols/ShadowSocksLogic.h" #include "pages_logic/protocols/OtherProtocolsLogic.h" - +#include "pages_logic/protocols/WireGuardLogic.h" using namespace amnezia; using namespace PageEnumNS; @@ -94,7 +94,7 @@ UiLogic::UiLogic(std::shared_ptr settings, std::shared_ptrinsert(Proto::WireGuard, new WireguardLogic(this)); + m_protocolLogicMap.insert(Proto::WireGuard, new WireGuardLogic(this)); m_protocolLogicMap.insert(Proto::Dns, new OtherProtocolsLogic(this)); m_protocolLogicMap.insert(Proto::Sftp, new OtherProtocolsLogic(this)); From 7270e701d4a4f8518e8a00781d68faf81a669835 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Sat, 5 Nov 2022 19:40:51 +0300 Subject: [PATCH 28/43] hostname is now added to the server name for imported openvpn and wireguard configs --- client/ui/pages_logic/StartPageLogic.cpp | 38 +++++++++++++++--------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/client/ui/pages_logic/StartPageLogic.cpp b/client/ui/pages_logic/StartPageLogic.cpp index 37b557e0..67f83918 100644 --- a/client/ui/pages_logic/StartPageLogic.cpp +++ b/client/ui/pages_logic/StartPageLogic.cpp @@ -16,7 +16,13 @@ #endif namespace { -QString checkConfigFormat(const QString &config) +enum class ConfigTypes { + Amnezia, + OpenVpn, + WireGuard +}; + +ConfigTypes checkConfigFormat(const QString &config) { const QString openVpnConfigPatternCli = "client"; const QString openVpnConfigPatternProto1 = "proto tcp"; @@ -30,11 +36,11 @@ QString checkConfigFormat(const QString &config) if (config.contains(openVpnConfigPatternCli) && (config.contains(openVpnConfigPatternProto1) || config.contains(openVpnConfigPatternProto2)) && (config.contains(openVpnConfigPatternDriver1) || config.contains(openVpnConfigPatternDriver2))) { - return "OpenVpn"; + return ConfigTypes::OpenVpn; } else if (config.contains(wireguardConfigPatternSectionInterface) && config.contains(wireguardConfigPatternSectionPeer)) - return "Wireguard"; - return "Amnezia"; + return ConfigTypes::WireGuard; + return ConfigTypes::Amnezia; } } @@ -169,9 +175,9 @@ void StartPageLogic::onPushButtonImportOpenFile() QByteArray data = file.readAll(); auto configFormat = checkConfigFormat(QString(data)); - if (configFormat == "OpenVpn") { + if (configFormat == ConfigTypes::OpenVpn) { importConnectionFromOpenVpnConfig(QString(data)); - } else if (configFormat == "Wireguard") { + } else if (configFormat == ConfigTypes::WireGuard) { importConnectionFromWireguardConfig(QString(data)); } else { importConnectionFromCode(QString(data)); @@ -261,10 +267,17 @@ bool StartPageLogic::importConnectionFromOpenVpnConfig(const QString &config) QJsonArray arr; arr.push_back(containers); + QString hostName; + const static QRegularExpression hostNameRegExp("remote (.*) [0-9]*"); + QRegularExpressionMatch hostNameMatch = hostNameRegExp.match(config); + if (hostNameMatch.hasMatch()) { + hostName = hostNameMatch.captured(1); + } + QJsonObject o; o[config_key::containers] = arr; o[config_key::defaultContainer] = "amnezia-openvpn"; - o[config_key::description] = "OpenVpn server"; + o[config_key::description] = QString("OpenVpn server %1").arg(hostName); const static QRegularExpression dnsRegExp("dhcp-option DNS (\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b)"); @@ -276,11 +289,7 @@ bool StartPageLogic::importConnectionFromOpenVpnConfig(const QString &config) o[config_key::dns2] = dnsMatch.next().captured(1); } - const static QRegularExpression hostNameRegExp("remote (\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b)"); - QRegularExpressionMatch hostNameMatch = hostNameRegExp.match(config); - if (hostNameMatch.hasMatch()) { - o[config_key::hostName] = hostNameMatch.captured(1); - } + o[config_key::hostName] = hostName; o[config_key::isThirdPartyConfig] = true; @@ -292,7 +301,7 @@ bool StartPageLogic::importConnectionFromWireguardConfig(const QString &config) QJsonObject lastConfig; lastConfig[config_key::config] = config; - const static QRegularExpression hostNameAndPortRegExp("Endpoint = (\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b):([0-9]*)"); + const static QRegularExpression hostNameAndPortRegExp("Endpoint = (.*):([0-9]*)"); QRegularExpressionMatch hostNameAndPortMatch = hostNameAndPortRegExp.match(config); QString hostName; QString port; @@ -316,7 +325,7 @@ bool StartPageLogic::importConnectionFromWireguardConfig(const QString &config) QJsonObject o; o[config_key::containers] = arr; o[config_key::defaultContainer] = "amnezia-wireguard"; - o[config_key::description] = "Wireguard server"; + o[config_key::description] = QString("Wireguard server %1").arg(hostName); const static QRegularExpression dnsRegExp("DNS = (\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b).*(\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b)"); QRegularExpressionMatch dnsMatch = dnsRegExp.match(config); @@ -326,6 +335,7 @@ bool StartPageLogic::importConnectionFromWireguardConfig(const QString &config) } o[config_key::hostName] = hostName; + o[config_key::isThirdPartyConfig] = true; return importConnection(o); From 97457f17c15294e586009fa333a6f7a3a3172bd0 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Tue, 29 Nov 2022 22:11:02 +0300 Subject: [PATCH 29/43] fixed options parsing for enable macos NOTARIZE_APP --- deploy/build_macos.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/build_macos.sh b/deploy/build_macos.sh index 09dd85f7..fa955215 100755 --- a/deploy/build_macos.sh +++ b/deploy/build_macos.sh @@ -3,7 +3,7 @@ echo "Build script started ..." set -o errexit -o nounset -while getopts n: flag +while getopts n flag do case "${flag}" in n) NOTARIZE_APP=1;; From dcb5828313c9d2378c9a0aa6b4869ecd5c20af97 Mon Sep 17 00:00:00 2001 From: pokamest Date: Wed, 30 Nov 2022 01:51:18 +0100 Subject: [PATCH 30/43] GitHub actions (#133) GitHub actions --- .github/workflows/deploy.yml | 255 +++++++++++++++++++++++++++++++++++ deploy/build_windows.bat | 2 +- 2 files changed, 256 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/deploy.yml diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 00000000..a5605263 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,255 @@ +name: 'Deploy workflow' + +on: [push] + +jobs: + Build-Linux-Ubuntu: + name: 'Build-Linux-Ubuntu' + runs-on: ubuntu-latest + + env: + QT_VERSION: 5.15.2 + QIF_VERSION: 4.5 + + steps: + - name: 'Install Qt' + uses: jurplel/install-qt-action@v3 + with: + version: ${{ env.QT_VERSION }} + host: 'linux' + target: 'desktop' + arch: 'gcc_64' + dir: ${{ runner.temp }} + setup-python: 'true' + tools: 'tools_ifw' + set-env: 'true' + extra: '--external 7z' + + - name: 'Get sources' + uses: actions/checkout@v3 + with: + submodules: 'true' + fetch-depth: 10 + + - name: 'Setup ccache' + uses: hendrikmuhs/ccache-action@v1.2 + + - name: 'Build project' + run: | + 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 + +# ------------------------------------------------------ + + Build-Windows: + name: Build-Windows + runs-on: windows-latest + continue-on-error: true + + strategy: + matrix: + arch: [32, 64] + include: + - qt-arch: 'win32_msvc2019' + arch: 32 + - qt-msvc-path: 'msvc2019' + arch: 32 + - msvc-arch: 'x86' + arch: 32 + - qt-arch: 'win64_msvc2019_64' + arch: 64 + - qt-msvc-path: 'msvc2019_64' + arch: 64 + - msvc-arch: 'x64' + arch: 64 + + env: + QT_VERSION: 5.15.2 + QIF_VERSION: 4.5 + BUILD_ARCH: ${{ matrix.arch }} + + steps: + - name: 'Get sources' + uses: actions/checkout@v3 + with: + submodules: 'true' + fetch-depth: 10 + + - name: 'Setup ccache' + uses: hendrikmuhs/ccache-action@v1.2 + + - name: 'Install Qt' + uses: jurplel/install-qt-action@v3 + with: + version: ${{ env.QT_VERSION }} + host: 'windows' + target: 'desktop' + arch: '${{ matrix.qt-arch }}' + dir: ${{ runner.temp }} + setup-python: 'true' + tools: 'tools_ifw' + set-env: 'true' + extra: '--external 7z' + + - name: 'Setup mvsc' + uses: ilammy/msvc-dev-cmd@v1 + with: + arch: ${{ matrix.msvc-arch }} + + - name: 'Build project' + shell: cmd + run: | + set BUILD_ARCH=${{ env.BUILD_ARCH }} + set QT_BIN_DIR="${{ runner.temp }}\\Qt\\${{ env.QT_VERSION }}\\${{ matrix.qt-msvc-path }}\\bin" + set QIF_BIN_DIR="${{ runner.temp }}\\Qt\\Tools\\QtInstallerFramework\\${{ env.QIF_VERSION }}\\bin" + call deploy\\build_windows.bat + +# ------------------------------------------------------ + + Build-IOS: + name: 'Build-IOS' + runs-on: macos-latest + + env: + QT_VERSION: 5.15.2 + QIF_VERSION: 4.4 + + steps: + - name: 'Setup xcode' + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: '13.4' + + - name: 'Install Qt' + uses: jurplel/install-qt-action@v3 + with: + version: ${{ env.QT_VERSION }} + host: 'mac' + target: 'ios' + dir: ${{ runner.temp }} + setup-python: 'true' + set-env: 'true' + extra: '--external 7z' + + - name: 'Install go' + uses: actions/setup-go@v3 + + - name: 'Setup gomobile' + run: | + export PATH=$PATH:~/go/bin + go install golang.org/x/mobile/cmd/gomobile@latest + gomobile init + + - name: 'Get sources' + uses: actions/checkout@v3 + with: + submodules: 'true' + fetch-depth: 10 + + - name: 'Setup ccache' + uses: hendrikmuhs/ccache-action@v1.2 + + - name: 'Build project' + run: | + export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/ios/bin" + export QT_IOS_BIN=$QT_BIN_DIR + export PATH=$PATH:~/go/bin + bash deploy/build_ios.sh + +# ------------------------------------------------------ + + Build-MacOS: + name: 'Build-MacOS' + runs-on: macos-latest + + env: + QT_VERSION: 5.15.2 + QIF_VERSION: 4.5 + + steps: + - name: 'Setup xcode' + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: '13.4' + + - name: 'Install Qt' + uses: jurplel/install-qt-action@v3 + with: + version: ${{ env.QT_VERSION }} + host: 'mac' + target: 'desktop' + arch: 'clang_64' + dir: ${{ runner.temp }} + setup-python: 'true' + tools: 'tools_ifw' + set-env: 'true' + extra: '--external 7z' + + - name: 'Get sources' + uses: actions/checkout@v3 + with: + submodules: 'true' + fetch-depth: 10 + + - name: 'Setup ccache' + uses: hendrikmuhs/ccache-action@v1.2 + + - name: 'Build project' + run: | + export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/clang_64/bin" + export QIF_BIN_DIR="${{ runner.temp }}/Qt/Tools/QtInstallerFramework/${{ env.QIF_VERSION }}/bin" + bash deploy/build_macos.sh + +# ------------------------------------------------------ + + Build-Android: + name: 'Build-Android' + runs-on: ubuntu-latest + + env: + QT_VERSION: 5.15.2 + QIF_VERSION: 4.5 + + steps: + - name: 'Install Qt' + uses: jurplel/install-qt-action@v3 + with: + version: ${{ env.QT_VERSION }} + host: 'linux' + target: 'android' + arch: 'android' + dir: ${{ runner.temp }} + setup-python: 'true' + set-env: 'true' + extra: '--external 7z' + + - name: 'Get sources' + uses: actions/checkout@v3 + with: + submodules: 'true' + fetch-depth: 10 + + - name: 'Setup ccache' + uses: hendrikmuhs/ccache-action@v1.2 + + - name: 'Setup Java' + uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: '8' + + - name: 'Build project' + run: | + export NDK_VERSION=21d + export ANDROID_NDK_PLATFORM=android-21 + export ANDROID_NDK_HOME=${{ runner.temp }}/android-ndk-r${NDK_VERSION} + export ANDROID_NDK_ROOT=$ANDROID_NDK_HOME + + if [ ! -f $ANDROID_NDK_ROOT/ndk-build ]; then + wget https://dl.google.com/android/repository/android-ndk-r${NDK_VERSION}-linux-x86_64.zip -qO ${{ runner.temp }}/ndk.zip && + unzip -q -d ${{ runner.temp }} ${{ runner.temp }}/ndk.zip ; + fi + + export QT_BIN_DIR=${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/android/bin + bash deploy/build_android.sh \ No newline at end of file diff --git a/deploy/build_windows.bat b/deploy/build_windows.bat index c82a54ee..b58c4286 100644 --- a/deploy/build_windows.bat +++ b/deploy/build_windows.bat @@ -92,7 +92,7 @@ dir %OUT_APP_DIR% cd %OUT_APP_DIR% echo "Compressing data..." -"%QIF_BIN_DIR:"=%\archivegen" -c 9 %INSTALLER_DATA_DIR:"=%\%APP_NAME:"=%.7z ./ +"%QIF_BIN_DIR:"=%\archivegen" -c 9 %INSTALLER_DATA_DIR:"=%\%APP_NAME:"=%.7z . cd "%RELEASE_DIR:"=%\installer" echo "Creating installer..." From 0da63062d774b571de2ee5759f63efe107a85f2b Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Sat, 3 Dec 2022 15:23:52 +0300 Subject: [PATCH 31/43] bugfix: fixed transition to the "Installed services" page when importing a config that does not have installed containers --- client/ui/pages_logic/StartPageLogic.cpp | 8 -------- client/ui/pages_logic/ViewConfigLogic.cpp | 14 ++++++++++++-- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/client/ui/pages_logic/StartPageLogic.cpp b/client/ui/pages_logic/StartPageLogic.cpp index 1e28d6cd..306db93b 100644 --- a/client/ui/pages_logic/StartPageLogic.cpp +++ b/client/ui/pages_logic/StartPageLogic.cpp @@ -166,14 +166,6 @@ bool StartPageLogic::importConnection(const QJsonObject &profile) return false; } - if (!profile.contains(config_key::containers)) { - uiLogic()->selectedServerIndex = m_settings->defaultServerIndex(); - uiLogic()->selectedDockerContainer = m_settings->defaultContainer(uiLogic()->selectedServerIndex); - uiLogic()->onUpdateAllPages(); - - emit uiLogic()->goToPage(Page::ServerContainers); - } - return true; } diff --git a/client/ui/pages_logic/ViewConfigLogic.cpp b/client/ui/pages_logic/ViewConfigLogic.cpp index 17d8814e..1dfe9abe 100644 --- a/client/ui/pages_logic/ViewConfigLogic.cpp +++ b/client/ui/pages_logic/ViewConfigLogic.cpp @@ -61,7 +61,17 @@ void ViewConfigLogic::importConfig() m_settings->addServer(configJson()); m_settings->setDefaultServer(m_settings->serversCount() - 1); - emit uiLogic()->goToPage(Page::Vpn); - emit uiLogic()->setStartPage(Page::Vpn); + + if (!configJson().contains(config_key::containers) || configJson().value(config_key::containers).toArray().isEmpty()) { + uiLogic()->selectedServerIndex = m_settings->defaultServerIndex(); + uiLogic()->selectedDockerContainer = m_settings->defaultContainer(uiLogic()->selectedServerIndex); + uiLogic()->onUpdateAllPages(); + emit uiLogic()->goToPage(Page::Vpn); + emit uiLogic()->setStartPage(Page::Vpn); + emit uiLogic()->goToPage(Page::ServerContainers); + } else { + emit uiLogic()->goToPage(Page::Vpn); + emit uiLogic()->setStartPage(Page::Vpn); + } } From a9217810e75a00370dfb55eb654e1d9ba23ecd74 Mon Sep 17 00:00:00 2001 From: pokamest Date: Sun, 4 Dec 2022 21:54:22 +0100 Subject: [PATCH 32/43] Remove travis builds and tiny refactoring --- .travis.yml | 343 ------------------ client/client.pro | 4 +- .../windows/amneziavpn.rc} | 2 +- 3 files changed, 3 insertions(+), 346 deletions(-) delete mode 100644 .travis.yml rename client/{platform_win/vpnclient.rc => platforms/windows/amneziavpn.rc} (95%) diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 9e16ef5f..00000000 --- a/.travis.yml +++ /dev/null @@ -1,343 +0,0 @@ -branches: - only: - - master - - dev - - /\d+\.\d+/ - -jobs: - include: - - name: MacOS - os: osx - osx_image: xcode13.4 - language: cpp - - env: - - PATH=/usr/local/opt/ccache/libexec:$PATH - - QT_VERSION=5.15.2 - - QIF_VERSION=4.4 - - QT_BIN_DIR=$HOME/Qt/$QT_VERSION/clang_64/bin - - QIF_BIN_DIR=$QT_BIN_DIR/../../../Tools/QtInstallerFramework/$QIF_VERSION/bin - - install: - - | - if [ ! -f $QT_BIN_DIR/qmake ]; then \ - brew install p7zip ccache && \ - python3 -m pip install --upgrade pip && \ - python3 -m pip install -U aqtinstall requests py7zr && \ - python3 -m pip show aqtinstall && \ - python3 -m aqt install-qt mac desktop $QT_VERSION clang_64 -m all -O $HOME/Qt && \ - python3 -m aqt install-tool mac desktop tools_ifw -O $HOME/Qt ; \ - fi - - script: - - bash deploy/build_macos.sh - - after_script: - - ccache --show-stats - - deploy: - provider: releases - token: $GH_TOKEN - cleanup: false - file: - - "AmneziaVPN.dmg" - on: - tags: true - branch: master - - cache: - - ccache - - directories: - - $HOME/Qt - - $HOME/Library/Caches/Homebrew - - # ------------------------------------------------------ - - name: Windows_x64 - os: windows - language: cpp - - env: - - PATH=/c/Python39:/c/Python39/Scripts:$PATH - - QT_VERSION=5.15.2 - - QIF_VERSION=4.4 - - QT_BIN_DIR="c:\\Qt\\$QT_VERSION\\msvc2019_64\\bin" - - QIF_BIN_DIR="c:\\Qt\\Tools\\QtInstallerFramework\\${QIF_VERSION}\\bin" - - BUILD_ARCH=64 - - MSVC_PATH_WIN="C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community" - - MSVC_PATH="/C/Program Files (x86)/Microsoft Visual Studio/2019/Community" - - install: - - if [ ! -f "$MSVC_PATH/VC/Auxiliary/Build/vcvars64.bat" ]; then choco install --ignorepackagecodes --no-progress -y visualstudio2019buildtools --package-parameters "--add Microsoft.VisualStudio.Workload.VCTools --add Microsoft.VisualStudio.Workload.NativeDesktop --add Microsoft.VisualStudio.Component.VC.ATLMFC --includeRecommended --nocache --installPath $MSVC_PATH_WIN"; fi - - if [ ! -f /C/Qt/$QT_VERSION/msvc2019_64/bin/qmake ]; then choco install python --version 3.9.1; fi - - | - if [ ! -f /C/Qt/$QT_VERSION/msvc2019_64/bin/qmake ]; then \ - python -m pip install --upgrade pip && \ - pip3 install -U aqtinstall requests py7zr && \ - pip3 show aqtinstall && \ - aqt install-qt windows desktop $QT_VERSION win64_msvc2019_64 -m all -O /C/Qt && \ - aqt install-tool windows desktop tools_ifw -O /C/Qt ; \ - fi - - choco install ccache - - script: - - echo set BUILD_ARCH=$BUILD_ARCH > winbuild.bat - - echo set QT_BIN_DIR="$QT_BIN_DIR" >> winbuild.bat - - echo set QIF_BIN_DIR="$QIF_BIN_DIR" >> winbuild.bat - - echo call \""%MSVC_PATH_WIN%\\VC\\Auxiliary\\Build\\vcvars${BUILD_ARCH}.bat\"" >> winbuild.bat - - echo call \""%MSVC_PATH_WIN%\\Common7\\Tools\\VsDevCmd.bat\" -arch=amd64" >> winbuild.bat - - echo call deploy\\build_windows.bat >> winbuild.bat - - cmd //c winbuild.bat - - after_script: - - ccache --show-stats - - deploy: - provider: releases - token: $GH_TOKEN - cleanup: false - file: - - "AmneziaVPN_x64.exe" - on: - tags: true - branch: master - - cache: - directories: - - /C/Qt - - $MSVC_PATH - -# ------------------------------------------------------ - - name: Windows_x32 - os: windows - language: cpp - - env: - - PATH=/c/Python39:/c/Python39/Scripts:$PATH - - QT_VERSION=5.15.2 - - QIF_VERSION=4.4 - - QT_BIN_DIR="c:\\Qt\\${QT_VERSION}\\msvc2019\\bin" - - QIF_BIN_DIR="c:\\Qt\\Tools\\QtInstallerFramework\\${QIF_VERSION}\\bin" - - BUILD_ARCH=32 - - MSVC_PATH_WIN="C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community" - - MSVC_PATH="/C/Program Files (x86)/Microsoft Visual Studio/2019/Community" - - install: - - if [ ! -f "$MSVC_PATH/VC/Auxiliary/Build/vcvars64.bat" ]; then choco install --ignorepackagecodes --no-progress -y visualstudio2019buildtools --package-parameters "--add Microsoft.VisualStudio.Workload.VCTools --add Microsoft.VisualStudio.Workload.NativeDesktop --add Microsoft.VisualStudio.Component.VC.ATLMFC --includeRecommended --nocache --installPath $MSVC_PATH_WIN"; fi - - if [ ! -f /C/Qt/$QT_VERSION/msvc2019/bin/qmake ]; then choco install python --version 3.9.1; fi - - | - if [ ! -f /C/Qt/$QT_VERSION/msvc2019/bin/qmake ]; then \ - python -m pip install --upgrade pip && \ - pip3 install -U aqtinstall requests py7zr && \ - pip3 show aqtinstall && \ - aqt install-qt windows desktop $QT_VERSION win32_msvc2019 -m all -O /C/Qt && \ - aqt install-tool windows desktop tools_ifw -O /C/Qt ; \ - fi - - choco install ccache - - script: - - echo set BUILD_ARCH=$BUILD_ARCH > winbuild.bat - - echo set QT_BIN_DIR="$QT_BIN_DIR" >> winbuild.bat - - echo set QIF_BIN_DIR="$QIF_BIN_DIR" >> winbuild.bat - - echo call \""%MSVC_PATH_WIN%\\VC\\Auxiliary\\Build\\vcvars${BUILD_ARCH}.bat\"" >> winbuild.bat - - echo call \""%MSVC_PATH_WIN%\\Common7\\Tools\\VsDevCmd.bat\"" >> winbuild.bat - - echo call deploy\\build_windows.bat >> winbuild.bat - - cmd //c winbuild.bat - - after_script: - - ccache --show-stats - - deploy: - provider: releases - token: $GH_TOKEN - cleanup: false - file: - - "AmneziaVPN_x32.exe" - on: - tags: true - branch: master - - cache: - directories: - - /C/Qt - - $MSVC_PATH - -# ------------------------------------------------------ - - name: Linux - os: linux - language: cpp - dist: focal - - addons: - apt: - packages: - - p7zip - - p7zip-full - - python3 - - python3-pip - - libgl-dev - - mesa-common-dev - - libpulse-dev - - libxcb-* - - libxkbcommon-x11-0 - - env: - - QT_VERSION=5.15.2 - - QIF_VERSION=4.4 - - QT_BIN_DIR=$HOME/Qt/$QT_VERSION/gcc_64/bin - - QIF_BIN_DIR=$QT_BIN_DIR/../../../Tools/QtInstallerFramework/$QIF_VERSION/bin - - install: - - | - if [ ! -f $QT_BIN_DIR/qmake ]; then \ - python3 -m pip install --user $(whoami) --upgrade pip && \ - export PATH=$HOME/.local/bin:$PATH && \ - python3 -m pip install -U aqtinstall requests py7zr && \ - python3 -m pip show aqtinstall && \ - python3 -m aqt install-qt linux desktop $QT_VERSION gcc_64 -m all -O $HOME/Qt && \ - python3 -m aqt install-tool linux desktop tools_ifw -O $HOME/Qt ; \ - fi - - script: - - bash deploy/build_linux.sh - - after_script: - - ccache --show-stats - - deploy: - provider: releases - token: $GH_TOKEN - cleanup: false - file: - - "AmneziaVPN.bundle" - on: - tags: true - branch: master - - cache: - - ccache - - directories: - - $HOME/Qt - - $HOME/.ccache - -# ------------------------------------------------------ - - name: Android - os: linux - language: cpp - dist: focal - env: - - QT_VERSION=5.15.2 - - QT_BIN_DIR=$HOME/Qt/$QT_VERSION/android/bin - - ANDROID_API_VERSION=android-21 - - ANDROID_HOME=$HOME/sdk - - ANDROID_SDK_ROOT=$ANDROID_HOME - - LOCAL_ANDROID_HOME=$ANDROID_HOME - - LOCAL_ANDROID_SDK_ROOT=$ANDROID_HOME - - NDK_VERSION=21d - - ANDROID_NDK_PLATFORM=android-21 - - ANDROID_NDK_HOME=$HOME/android-ndk-r${NDK_VERSION} - - ANDROID_NDK_ROOT=$ANDROID_NDK_HOME - - ANDROID_NDK_HOST=linux-x86_64 - - LOCAL_ANDROID_NDK_HOME=$ANDROID_NDK_HOME - - LOCAL_ANDROID_NDK_HOST_PLATFORM=$ANDROID_NDK_HOST - - JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64 - - TERM=dumb - - addons: - apt: - packages: - - p7zip - - python3 - - python3-pip - - openjdk-8-jdk - - install: - - | - if [ ! -f $QT_BIN_DIR/qmake ]; then \ - python3 -m pip install -U aqtinstall requests py7zr && \ - python3 -m pip show aqtinstall && \ - python3 -m aqt install-qt linux android $QT_VERSION -m all -O $HOME/Qt ; \ - fi - - - | - if [ ! -f $ANDROID_SDK_ROOT/tools/bin/sdkmanager ]; then \ - echo "Download Android SDK" && \ - wget https://dl.google.com/android/repository/sdk-tools-linux-4333796.zip -qO $HOME/sdk.zip > /dev/null && \ - unzip -q -d $ANDROID_SDK_ROOT $HOME/sdk.zip && \ - echo "Download tools" && \ - yes | $ANDROID_SDK_ROOT/tools/bin/sdkmanager --licenses > /dev/null 2>&1 && \ - $ANDROID_SDK_ROOT/tools/bin/sdkmanager --install "cmdline-tools;latest" "platform-tools" "platforms;android-30" "build-tools;30.0.2" > /dev/null 2>&1 || exit 1 ; \ - fi - - | - if [ ! -f $ANDROID_NDK_ROOT/ndk-build ]; then \ - wget https://dl.google.com/android/repository/android-ndk-r${NDK_VERSION}-linux-x86_64.zip -qO $HOME/ndk.zip && - unzip -q -d $HOME $HOME/ndk.zip ; \ - fi - - script: - - bash deploy/build_android.sh - - after_script: - - ccache --show-stats - - deploy: - provider: releases - token: $GH_TOKEN - cleanup: false - file: - - "AmneziaVPN.aab" - on: - tags: true - branch: master - - cache: - - ccache - - directories: - - $HOME/Qt - - $HOME/.gradle/caches/ - - $HOME/.gradle/wrapper/ - - $HOME/.android/build-cache - - $ANDROID_NDK_HOME - - $ANDROID_SDK_ROOT - -# ------------------------------------------------------ - - name: iOS - os: osx - osx_image: xcode13.4 - language: cpp - - env: - - PATH=/usr/local/opt/ccache/libexec:~/go/bin:$PATH - - QT_VERSION=5.15.2 - - QT_BIN_DIR=$HOME/Qt/$QT_VERSION/ios/bin - - QT_IOS_BIN=$QT_BIN_DIR - - install: - - | - if [ ! -f $QT_BIN_DIR/qmake ]; then \ - brew install p7zip ccache && \ - python3 -m pip install --upgrade pip && \ - python3 -m pip install -U aqtinstall requests py7zr && \ - python3 -m pip show aqtinstall && \ - python3 -m aqt install-qt mac ios $QT_VERSION -m all -O $HOME/Qt ; \ - fi - - brew install golang - - go install golang.org/x/mobile/cmd/gomobile@latest - - gomobile init - - script: - - bash deploy/build_ios.sh - - after_script: - - ccache --show-stats - - cache: - - ccache - - directories: - - $HOME/Qt - - $HOME/Library/Caches/Homebrew - -before_cache: - - if [ "${TRAVIS_OS_NAME}" = "osx" ]; then brew cleanup; fi - # Cache only .git files under "/usr/local/Homebrew" so "brew update" does not take 5min every build - - if [ "${TRAVIS_OS_NAME}" = "osx" ]; then find /usr/local/Homebrew \! -regex ".+\.git.+" -delete; fi - diff --git a/client/client.pro b/client/client.pro index bf9db7ed..7840ae6a 100644 --- a/client/client.pro +++ b/client/client.pro @@ -155,8 +155,8 @@ TRANSLATIONS = \ win32 { DEFINES += MVPN_WINDOWS - OTHER_FILES += platform_win/vpnclient.rc - RC_FILE = platform_win/vpnclient.rc + OTHER_FILES += platforms/windows/amneziavpn.rc + RC_FILE = platforms/windows/amneziavpn.rc HEADERS += \ protocols/ikev2_vpn_protocol_windows.h \ diff --git a/client/platform_win/vpnclient.rc b/client/platforms/windows/amneziavpn.rc similarity index 95% rename from client/platform_win/vpnclient.rc rename to client/platforms/windows/amneziavpn.rc index b12fbfcb..05365077 100644 --- a/client/platform_win/vpnclient.rc +++ b/client/platforms/windows/amneziavpn.rc @@ -1,7 +1,7 @@ #include LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US -IDI_ICON1 ICON "../images/app.ico" +IDI_ICON1 ICON "../../images/app.ico" #define VER_FILEVERSION 2,0,0,0 #define VER_FILEVERSION_STR "2.0.0.0\0" From cad0dabe42a9800e931f41087e40848b86b5c56a Mon Sep 17 00:00:00 2001 From: Dmitriy Karpushin Date: Thu, 8 Dec 2022 11:51:28 +0300 Subject: [PATCH 33/43] Export/import of configuration filed on Android --- client/android/AndroidManifest.xml | 96 +++++++- client/android/res/xml/fileprovider.xml | 4 + .../src/org/amnezia/vpn/IPCContract.kt | 5 + .../android/src/org/amnezia/vpn/VPNService.kt | 75 ++++++ .../src/org/amnezia/vpn/VPNServiceBinder.kt | 233 +++++++++++------- .../src/org/amnezia/vpn/qt/VPNActivity.java | 37 --- .../src/org/amnezia/vpn/qt/VPNActivity.kt | 196 +++++++++++++++ client/client.pro | 10 + .../platforms/android/android_controller.cpp | 34 ++- client/platforms/android/android_controller.h | 10 +- .../qml/Pages/Share/PageShareProtoAmnezia.qml | 2 +- .../qml/Pages/Share/PageShareProtoCloak.qml | 2 +- .../qml/Pages/Share/PageShareProtoOpenVPN.qml | 2 +- .../Pages/Share/PageShareProtoWireGuard.qml | 2 +- client/ui/uilogic.cpp | 7 +- 15 files changed, 568 insertions(+), 147 deletions(-) create mode 100644 client/android/res/xml/fileprovider.xml create mode 100644 client/android/src/org/amnezia/vpn/IPCContract.kt delete mode 100644 client/android/src/org/amnezia/vpn/qt/VPNActivity.java create mode 100644 client/android/src/org/amnezia/vpn/qt/VPNActivity.kt diff --git a/client/android/AndroidManifest.xml b/client/android/AndroidManifest.xml index cb70759d..55cffe20 100644 --- a/client/android/AndroidManifest.xml +++ b/client/android/AndroidManifest.xml @@ -1,5 +1,10 @@ - + @@ -18,12 +23,72 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -79,10 +144,17 @@ - + + + + @@ -95,10 +167,24 @@ - + + + + + + + + diff --git a/client/android/res/xml/fileprovider.xml b/client/android/res/xml/fileprovider.xml new file mode 100644 index 00000000..426348c0 --- /dev/null +++ b/client/android/res/xml/fileprovider.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/client/android/src/org/amnezia/vpn/IPCContract.kt b/client/android/src/org/amnezia/vpn/IPCContract.kt new file mode 100644 index 00000000..4ae3596d --- /dev/null +++ b/client/android/src/org/amnezia/vpn/IPCContract.kt @@ -0,0 +1,5 @@ +package org.amnezia.vpn + +const val IMPORT_COMMAND_CODE = 1 +const val IMPORT_ACTION_CODE = "import_action" +const val IMPORT_CONFIG_KEY = "CONFIG_DATA_KEY" diff --git a/client/android/src/org/amnezia/vpn/VPNService.kt b/client/android/src/org/amnezia/vpn/VPNService.kt index a1f8550c..23e9ff0c 100644 --- a/client/android/src/org/amnezia/vpn/VPNService.kt +++ b/client/android/src/org/amnezia/vpn/VPNService.kt @@ -15,6 +15,8 @@ import android.os.* import android.system.ErrnoException import android.system.Os import android.system.OsConstants +import android.text.TextUtils +import androidx.core.content.FileProvider import com.wireguard.android.util.SharedLibraryLoader import com.wireguard.config.* import com.wireguard.crypto.Key @@ -151,6 +153,31 @@ class VPNService : BaseVpnService(), LocalDnsService.Interface { private var flags = 0 private var startId = 0 + private lateinit var mMessenger: Messenger + + internal class ExternalConfigImportHandler( + context: Context, + private val serviceBinder: VPNServiceBinder, + private val applicationContext: Context = context.applicationContext + ) : Handler() { + + override fun handleMessage(msg: Message) { + when (msg.what) { + IMPORT_COMMAND_CODE -> { + val data = msg.data.getString(IMPORT_CONFIG_KEY) + + if (data != null) { + serviceBinder.importConfig(data) + } + } + + else -> { + super.handleMessage(msg) + } + } + } + } + fun init() { if (mAlreadyInitialised) { return @@ -188,6 +215,14 @@ class VPNService : BaseVpnService(), LocalDnsService.Interface { */ override fun onBind(intent: Intent): IBinder { Log.v(tag, "Aman: onBind....................") + + if (intent.action != null && intent.action == IMPORT_ACTION_CODE) { + Log.v(tag, "Service bind for import of config") + mMessenger = Messenger(ExternalConfigImportHandler(this, mBinder)) + return mMessenger.binder + } + + Log.v(tag, "Regular service bind") when (mProtocol) { "shadowsocks" -> { when (intent.action) { @@ -840,4 +875,44 @@ class VPNService : BaseVpnService(), LocalDnsService.Interface { override fun close() = Os.close(fd) } + fun saveAsFile(configContent: String?, suggestedFileName: String): String { + val rootDirPath = cacheDir.absolutePath + val rootDir = File(rootDirPath) + + if (!rootDir.exists()) { + rootDir.mkdirs() + } + + val fileName = if (!TextUtils.isEmpty(suggestedFileName)) suggestedFileName else "amnezia.cfg" + + val file = File(rootDir, fileName) + + try { + file.bufferedWriter().use { out -> out.write(configContent) } + return file.toString() + } catch (e: Exception) { + e.printStackTrace() + } + + return "" + } + + fun shareFile(attachmentFile: String?) { + try { + val intent = Intent(Intent.ACTION_SEND) + intent.type = "text/*" + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + + val file = File(attachmentFile) + val uri = FileProvider.getUriForFile(this, "${BuildConfig.APPLICATION_ID}.fileprovider", file) + intent.putExtra(Intent.EXTRA_STREAM, uri) + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + + val createChooser = Intent.createChooser(intent, "Config sharing") + createChooser.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + startActivity(createChooser) + } catch (e: Exception) { + Log.i(tag, e.message.toString()) + } + } } diff --git a/client/android/src/org/amnezia/vpn/VPNServiceBinder.kt b/client/android/src/org/amnezia/vpn/VPNServiceBinder.kt index d81d5077..640a0657 100644 --- a/client/android/src/org/amnezia/vpn/VPNServiceBinder.kt +++ b/client/android/src/org/amnezia/vpn/VPNServiceBinder.kt @@ -17,6 +17,7 @@ class VPNServiceBinder(service: VPNService) : Binder() { private val tag = "VPNServiceBinder" private var mListener: IBinder? = null private var mResumeConfig: JSONObject? = null + private var mImportedConfig: String? = null /** * The codes this Binder does accept in [onTransact] @@ -31,6 +32,7 @@ class VPNServiceBinder(service: VPNService) : Binder() { const val resumeActivate = 7 const val setNotificationText = 8 const val setFallBackNotification = 9 + const val shareConfig = 10 } /** @@ -70,101 +72,148 @@ class VPNServiceBinder(service: VPNService) : Binder() { return true } - ACTIONS.resumeActivate -> { - // [data] is empty - // Activate the current tunnel - try { - mResumeConfig?.let { this.mService.turnOn(it) } - } catch (e: Exception) { - Log.e(tag, "An Error occurred while enabling the VPN: ${e.localizedMessage}") - } - return true - } - - ACTIONS.deactivate -> { - // [data] here is empty - this.mService.turnOff() - return true - } - - ACTIONS.registerEventListener -> { - // [data] contains the Binder that we need to dispatch the Events - val binder = data.readStrongBinder() - mListener = binder - val obj = JSONObject() - obj.put("connected", mService.isUp) - obj.put("time", mService.connectionTime) - dispatchEvent(EVENTS.init, obj.toString()) - return true - } - - ACTIONS.requestStatistic -> { - dispatchEvent(EVENTS.statisticUpdate, mService.status.toString()) - return true - } - - ACTIONS.requestGetLog -> { - // Grabs all the Logs and dispatch new Log Event - dispatchEvent(EVENTS.backendLogs, Log.getContent()) - return true - } - ACTIONS.requestCleanupLog -> { - Log.clearFile() - return true - } - ACTIONS.setNotificationText -> { - NotificationUtil.update(data) - return true - } - ACTIONS.setFallBackNotification -> { - NotificationUtil.saveFallBackMessage(data, mService) - return true - } - IBinder.LAST_CALL_TRANSACTION -> { - Log.e(tag, "The OS Requested to shut down the VPN") - this.mService.turnOff() - return true - } - - else -> { - Log.e(tag, "Received invalid bind request \t Code -> $code") - // If we're hitting this there is probably something wrong in the client. - return false - } - } - return false - } - - /** - * Dispatches an Event to all registered Binders - * [code] the Event that happened - see [EVENTS] - * To register an Eventhandler use [onTransact] with - * [ACTIONS.registerEventListener] - */ - fun dispatchEvent(code: Int, payload: String?) { + ACTIONS.resumeActivate -> { + // [data] is empty + // Activate the current tunnel try { - mListener?.let { - if (it.isBinderAlive) { - val data = Parcel.obtain() - data.writeByteArray(payload?.toByteArray(charset("UTF-8"))) - it.transact(code, data, Parcel.obtain(), 0) - } - } - } catch (e: DeadObjectException) { - // If the QT Process is killed (not just inactive) - // we cant access isBinderAlive, so nothing to do here. + mResumeConfig?.let { this.mService.turnOn(it) } + } catch (e: Exception) { + Log.e(tag, "An Error occurred while enabling the VPN: ${e.localizedMessage}") } + return true } - /** - * The codes we Are Using in case of [dispatchEvent] - */ - object EVENTS { - const val init = 0 - const val connected = 1 - const val disconnected = 2 - const val statisticUpdate = 3 - const val backendLogs = 4 - const val activationError = 5 + ACTIONS.deactivate -> { + // [data] here is empty + this.mService.turnOff() + return true + } + + ACTIONS.registerEventListener -> { + // [data] contains the Binder that we need to dispatch the Events + val binder = data.readStrongBinder() + mListener = binder + val obj = JSONObject() + obj.put("connected", mService.isUp) + obj.put("time", mService.connectionTime) + dispatchEvent(EVENTS.init, obj.toString()) + + //// + if (mImportedConfig != null) { + Log.i(tag, "register: config not null") + dispatchEvent(EVENTS.configImport, mImportedConfig) + mImportedConfig = null + } else { + Log.i(tag, "register: config is null") + } + + return true + } + + ACTIONS.requestStatistic -> { + dispatchEvent(EVENTS.statisticUpdate, mService.status.toString()) + return true + } + + ACTIONS.requestGetLog -> { + // Grabs all the Logs and dispatch new Log Event + dispatchEvent(EVENTS.backendLogs, Log.getContent()) + return true + } + + ACTIONS.requestCleanupLog -> { + Log.clearFile() + return true + } + + ACTIONS.setNotificationText -> { + NotificationUtil.update(data) + return true + } + + ACTIONS.setFallBackNotification -> { + NotificationUtil.saveFallBackMessage(data, mService) + return true + } + + ACTIONS.shareConfig -> { + val byteArray = data.createByteArray() + val json = byteArray?.let { String(it) } + val config = JSONObject(json) + val configContent = config.getString("data") + val suggestedName = config.getString("suggestedName") + + val filePath = mService.saveAsFile(configContent, suggestedName) + Log.i(tag, "save file: $filePath") + + mService.shareFile(filePath) + return true + } + + IBinder.LAST_CALL_TRANSACTION -> { + Log.e(tag, "The OS Requested to shut down the VPN") + this.mService.turnOff() + return true + } + + else -> { + Log.e(tag, "Received invalid bind request \t Code -> $code") + // If we're hitting this there is probably something wrong in the client. + return false } } + + return false + } + + /** + * Dispatches an Event to all registered Binders + * [code] the Event that happened - see [EVENTS] + * To register an Eventhandler use [onTransact] with + * [ACTIONS.registerEventListener] + */ + fun dispatchEvent(code: Int, payload: String?) { + try { + mListener?.let { + if (it.isBinderAlive) { + val data = Parcel.obtain() + data.writeByteArray(payload?.toByteArray(charset("UTF-8"))) + it.transact(code, data, Parcel.obtain(), 0) + } + } + } catch (e: DeadObjectException) { + // If the QT Process is killed (not just inactive) + // we cant access isBinderAlive, so nothing to do here. + } + } + + /** + * The codes we Are Using in case of [dispatchEvent] + */ + object EVENTS { + const val init = 0 + const val connected = 1 + const val disconnected = 2 + const val statisticUpdate = 3 + const val backendLogs = 4 + const val activationError = 5 + const val configImport = 6 + } + + fun importConfig(config: String) { + val obj = JSONObject() + obj.put("config", config) + + val resultString = obj.toString() + + Log.i(tag, "Transact import config request") + + if (mListener != null) { + Log.i(tag, "binder alive") + dispatchEvent(EVENTS.configImport, resultString) + } else { + Log.i(tag, "binder NOT alive") + mImportedConfig = resultString + } + } +} diff --git a/client/android/src/org/amnezia/vpn/qt/VPNActivity.java b/client/android/src/org/amnezia/vpn/qt/VPNActivity.java deleted file mode 100644 index 11d99063..00000000 --- a/client/android/src/org/amnezia/vpn/qt/VPNActivity.java +++ /dev/null @@ -1,37 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.amnezia.vpn.qt; - -import android.view.KeyEvent; - -public class VPNActivity extends org.qtproject.qt5.android.bindings.QtActivity { - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) { - onBackPressed(); - return true; - } - return super.onKeyDown(keyCode, event); - } - -// TODO finalize -// https://github.com/mozilla-mobile/mozilla-vpn-client/blob/6acff5dd9f072380a04c3fa12e9f3c98dbdd7a26/src/platforms/android/androidvpnactivity.h - @Override - public void onBackPressed() { -// super.onBackPressed(); - try { - if (!handleBackButton()) { - // Move the activity into paused state if back button was pressed - moveTaskToBack(true); -// finish(); - } - } catch (Exception e) { - } - } - - // Returns true if MVPN has handled the back button - native boolean handleBackButton(); -} diff --git a/client/android/src/org/amnezia/vpn/qt/VPNActivity.kt b/client/android/src/org/amnezia/vpn/qt/VPNActivity.kt new file mode 100644 index 00000000..c5b5107e --- /dev/null +++ b/client/android/src/org/amnezia/vpn/qt/VPNActivity.kt @@ -0,0 +1,196 @@ +package org.amnezia.vpn.qt; + +import android.Manifest +import android.content.ComponentName +import android.content.ContentResolver +import android.content.Context +import android.content.Intent +import android.content.ServiceConnection +import android.content.pm.PackageManager +import android.net.Uri +import android.os.* +import android.provider.MediaStore +import android.util.Log +import android.view.KeyEvent +import android.widget.Toast +import androidx.core.app.ActivityCompat +import androidx.core.content.ContextCompat +import org.amnezia.vpn.VPNService +import org.amnezia.vpn.VPNServiceBinder +import org.amnezia.vpn.IMPORT_COMMAND_CODE +import org.amnezia.vpn.IMPORT_ACTION_CODE +import org.amnezia.vpn.IMPORT_CONFIG_KEY +import org.qtproject.qt5.android.bindings.QtActivity +import java.io.* + +class VPNActivity : org.qtproject.qt5.android.bindings.QtActivity() { + + private var configString: String? = null + private var vpnServiceBinder: Messenger? = null + private var isBound = false + + private val TAG = "VPNActivity" + private val STORAGE_PERMISSION_CODE = 42 + + + override fun onCreate(savedInstanceState: Bundle?) { + val newIntent = intent + val newIntentAction = newIntent.action + + if (newIntent != null && newIntentAction != null) { + configString = processIntent(newIntent, newIntentAction) + } + + super.onCreate(savedInstanceState) + } + + override fun onNewIntent(newIntent: Intent) { + intent = newIntent + + val newIntentAction = newIntent.action + + if (newIntent != null && newIntentAction != null && newIntentAction != Intent.ACTION_MAIN) { + if (isReadStorageAllowed()) { + configString = processIntent(newIntent, newIntentAction) + } else { + requestStoragePermission() + } + } + + super.onNewIntent(intent) + } + + override fun onResume() { + super.onResume() + + if (configString != null && !isBound) { + bindVpnService() + } + } + + override fun onPause() { + if (vpnServiceBinder != null && isBound) { + unbindService(connection) + isBound = false + } + super.onPause() + } + + private fun isReadStorageAllowed(): Boolean { + val permissionStatus = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) + return permissionStatus == PackageManager.PERMISSION_GRANTED + } + + private fun requestStoragePermission() { + ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), STORAGE_PERMISSION_CODE) + } + + override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { + if (requestCode == STORAGE_PERMISSION_CODE) { + if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + Log.d(TAG, "Storage read permission granted") + + if (configString != null) { + bindVpnService() + } + } else { + Toast.makeText(this, "Oops you just denied the permission", Toast.LENGTH_LONG).show() + } + } + } + + private fun bindVpnService() { + try { + val intent = Intent(this, VPNService::class.java) + intent.action = IMPORT_ACTION_CODE + + bindService(intent, connection, Context.BIND_AUTO_CREATE) + } catch (e: Exception) { + e.printStackTrace() + } + } + + private fun processIntent(intent: Intent, action: String): String? { + val scheme = intent.scheme + + if (scheme == null) { + return null + } + + if (action.compareTo(Intent.ACTION_VIEW) == 0) { + val resolver = contentResolver + + if (scheme.compareTo(ContentResolver.SCHEME_CONTENT) == 0) { + val uri = intent.data + val name: String? = getContentName(resolver, uri) + + Log.d(TAG, "Content intent detected: " + action + " : " + intent.dataString + " : " + intent.type + " : " + name) + + val input = resolver.openInputStream(uri!!) + + return input?.bufferedReader()?.use(BufferedReader::readText) + } else if (scheme.compareTo(ContentResolver.SCHEME_FILE) == 0) { + val uri = intent.data + val name = uri!!.lastPathSegment + + Log.d(TAG, "File intent detected: " + action + " : " + intent.dataString + " : " + intent.type + " : " + name) + + val input = resolver.openInputStream(uri) + + return input?.bufferedReader()?.use(BufferedReader::readText) + } + } + + return null + } + + private fun getContentName(resolver: ContentResolver?, uri: Uri?): String? { + val cursor = resolver!!.query(uri!!, null, null, null, null) + + cursor.use { + cursor!!.moveToFirst() + val nameIndex = cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME) + return if (nameIndex >= 0) { + return cursor.getString(nameIndex) + } else { + null + } + } + } + + private var connection: ServiceConnection = object : ServiceConnection { + override fun onServiceConnected(className: ComponentName, binder: IBinder) { + vpnServiceBinder = Messenger(binder) + + if (configString != null) { + val msg: Message = Message.obtain(null, IMPORT_COMMAND_CODE, 0, 0) + val bundle = Bundle() + bundle.putString(IMPORT_CONFIG_KEY, configString!!) + msg.data = bundle + + try { + vpnServiceBinder?.send(msg) + } catch (e: RemoteException) { + e.printStackTrace() + } + + configString = null + } + + isBound = true + } + + override fun onServiceDisconnected(className: ComponentName) { + vpnServiceBinder = null + isBound = false + } + } + + override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean { + if (keyCode == KeyEvent.KEYCODE_BACK && event.repeatCount == 0) { + onBackPressed() + return true + } + return super.onKeyDown(keyCode, event) + } +} diff --git a/client/client.pro b/client/client.pro index 7840ae6a..ca309455 100644 --- a/client/client.pro +++ b/client/client.pro @@ -263,9 +263,19 @@ android { android/gradlew.bat \ android/gradle.properties \ android/res/values/libs.xml \ + android/res/xml/fileprovider.xml \ + android/src/org/amnezia/vpn/AuthHelper.java \ + android/src/org/amnezia/vpn/IPCContract.kt \ + android/src/org/amnezia/vpn/NotificationUtil.kt \ android/src/org/amnezia/vpn/OpenVPNThreadv3.kt \ + android/src/org/amnezia/vpn/Prefs.kt \ + android/src/org/amnezia/vpn/VpnLogger.kt \ android/src/org/amnezia/vpn/VpnService.kt \ android/src/org/amnezia/vpn/VpnServiceBinder.kt \ + android/src/org/amnezia/vpn/qt/AmneziaApp.kt \ + android/src/org/amnezia/vpn/qt/PackageManagerHelper.java \ + android/src/org/amnezia/vpn/qt/VPNActivity.kt \ + android/src/org/amnezia/vpn/qt/VPNApplication.java \ android/src/org/amnezia/vpn/qt/VPNPermissionHelper.kt ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android diff --git a/client/platforms/android/android_controller.cpp b/client/platforms/android/android_controller.cpp index 6b2c1aa0..af30fe05 100644 --- a/client/platforms/android/android_controller.cpp +++ b/client/platforms/android/android_controller.cpp @@ -16,19 +16,21 @@ #include "android_controller.h" #include "core/errorstrings.h" +#include "ui/pages_logic/StartPageLogic.h" // Binder Codes for VPNServiceBinder // See also - VPNServiceBinder.kt // Actions that are Requestable const int ACTION_ACTIVATE = 1; const int ACTION_DEACTIVATE = 2; -const int ACTION_REGISTERLISTENER = 3; +const int ACTION_REGISTER_LISTENER = 3; const int ACTION_REQUEST_STATISTIC = 4; const int ACTION_REQUEST_GET_LOG = 5; const int ACTION_REQUEST_CLEANUP_LOG = 6; const int ACTION_RESUME_ACTIVATE = 7; const int ACTION_SET_NOTIFICATION_TEXT = 8; const int ACTION_SET_NOTIFICATION_FALLBACK = 9; +const int ACTION_SHARE_CONFIG = 10; // Event Types that will be Dispatched after registration const int EVENT_INIT = 0; @@ -37,6 +39,7 @@ const int EVENT_DISCONNECTED = 2; const int EVENT_STATISTIC_UPDATE = 3; const int EVENT_BACKEND_LOGS = 4; const int EVENT_ACTIVATION_ERROR = 5; +const int EVENT_CONFIG_IMPORT = 6; namespace { AndroidController* s_instance = nullptr; @@ -57,10 +60,12 @@ AndroidController* AndroidController::instance() { return s_instance; } -bool AndroidController::initialize() +bool AndroidController::initialize(StartPageLogic *startPageLogic) { qDebug() << "Initializing"; + m_startPageLogic = startPageLogic; + // Hook in the native implementation for startActivityForResult into the JNI JNINativeMethod methods[]{{"startActivityForResult", "(Landroid/content/Intent;)V", @@ -148,6 +153,16 @@ void AndroidController::setNotificationText(const QString& title, m_serviceBinder.transact(ACTION_SET_NOTIFICATION_TEXT, data, nullptr); } +void AndroidController::shareConfig(const QString& configContent, const QString& suggestedName) { + QJsonObject rootObject; + rootObject["data"] = configContent; + rootObject["suggestedName"] = suggestedName; + QJsonDocument doc(rootObject); + QAndroidParcel parcel; + parcel.writeData(doc.toJson()); + m_serviceBinder.transact(ACTION_SHARE_CONFIG, parcel, nullptr); +} + /* * Sets fallback Notification text that should be shown in case the VPN * switches into the Connected state without the app open @@ -187,6 +202,10 @@ void AndroidController::cleanupBackendLogs() { m_serviceBinder.transact(ACTION_REQUEST_CLEANUP_LOG, nullParcel, nullptr); } +void AndroidController::importConfig(const QString& data){ + m_startPageLogic->importConnectionFromCode(data); +} + void AndroidController::onServiceConnected( const QString& name, const QAndroidBinder& serviceBinder) { qDebug() << "Server " + name + " connected"; @@ -198,7 +217,7 @@ void AndroidController::onServiceConnected( // Send the Service our Binder to recive incoming Events QAndroidParcel binderParcel; binderParcel.writeBinder(m_binder); - m_serviceBinder.transact(ACTION_REGISTERLISTENER, binderParcel, nullptr); + m_serviceBinder.transact(ACTION_REGISTER_LISTENER, binderParcel, nullptr); } void AndroidController::onServiceDisconnected(const QString& name) { @@ -279,7 +298,14 @@ bool AndroidController::VPNBinder::onTransact(int code, case EVENT_ACTIVATION_ERROR: qDebug() << "Transact: error"; emit m_controller->connectionStateChanged(VpnProtocol::Error); - + break; + case EVENT_CONFIG_IMPORT: + qDebug() << "Transact: config import"; + doc = QJsonDocument::fromJson(data.readData()); + buffer = doc.object()["config"].toString(); + qDebug() << "Transact: config string" << buffer; + m_controller->importConfig(buffer); + break; default: qWarning() << "Transact: Invalid!"; break; diff --git a/client/platforms/android/android_controller.h b/client/platforms/android/android_controller.h index a4734414..c2a65381 100644 --- a/client/platforms/android/android_controller.h +++ b/client/platforms/android/android_controller.h @@ -4,11 +4,13 @@ #include #include +#include "ui/uilogic.h" +#include "ui/pages_logic/StartPageLogic.h" + #include "protocols/vpnprotocol.h" using namespace amnezia; - class AndroidController : public QObject, public QAndroidServiceConnection { Q_OBJECT @@ -19,7 +21,7 @@ public: virtual ~AndroidController() override = default; - bool initialize(); + bool initialize(StartPageLogic *startPageLogic); ErrorCode start(); void stop(); @@ -27,9 +29,11 @@ public: void checkStatus(); void setNotificationText(const QString& title, const QString& message, int timerSec); + void shareConfig(const QString& data, const QString& suggestedName); void setFallbackConnectedNotification(); void getBackendLogs(std::function&& callback); void cleanupBackendLogs(); + void importConfig(const QString& data); // from QAndroidServiceConnection void onServiceConnected(const QString& name, const QAndroidBinder& serviceBinder) override; @@ -58,6 +62,8 @@ private: //Protocol m_protocol; QJsonObject m_vpnConfig; + StartPageLogic *m_startPageLogic; + bool m_serviceConnected = false; std::function m_logCallback; diff --git a/client/ui/qml/Pages/Share/PageShareProtoAmnezia.qml b/client/ui/qml/Pages/Share/PageShareProtoAmnezia.qml index e4b0ff83..2afecbad 100644 --- a/client/ui/qml/Pages/Share/PageShareProtoAmnezia.qml +++ b/client/ui/qml/Pages/Share/PageShareProtoAmnezia.qml @@ -112,7 +112,7 @@ New encryption keys pair will be generated.") Layout.bottomMargin: 10 Layout.fillWidth: true Layout.preferredHeight: 40 - text: qsTr("Save to file") + text: Qt.platform.os === "android" ? qsTr("Share") : qsTr("Save to file") enabled: tfShareCode.textArea.length > 0 visible: tfShareCode.textArea.length > 0 diff --git a/client/ui/qml/Pages/Share/PageShareProtoCloak.qml b/client/ui/qml/Pages/Share/PageShareProtoCloak.qml index bd415c95..fe92c1e7 100644 --- a/client/ui/qml/Pages/Share/PageShareProtoCloak.qml +++ b/client/ui/qml/Pages/Share/PageShareProtoCloak.qml @@ -94,7 +94,7 @@ PageShareProtocolBase { Layout.fillWidth: true Layout.preferredHeight: 40 - text: qsTr("Save to file") + text: Qt.platform.os === "android" ? qsTr("Share") : qsTr("Save to file") enabled: tfShareCode.textArea.length > 0 visible: tfShareCode.textArea.length > 0 diff --git a/client/ui/qml/Pages/Share/PageShareProtoOpenVPN.qml b/client/ui/qml/Pages/Share/PageShareProtoOpenVPN.qml index 5ca587b7..ab4a9a3d 100644 --- a/client/ui/qml/Pages/Share/PageShareProtoOpenVPN.qml +++ b/client/ui/qml/Pages/Share/PageShareProtoOpenVPN.qml @@ -93,7 +93,7 @@ PageShareProtocolBase { Layout.preferredHeight: 40 width: parent.width - 60 - text: qsTr("Save to file") + text: Qt.platform.os === "android" ? qsTr("Share") : qsTr("Save to file") enabled: tfShareCode.textArea.length > 0 visible: tfShareCode.textArea.length > 0 diff --git a/client/ui/qml/Pages/Share/PageShareProtoWireGuard.qml b/client/ui/qml/Pages/Share/PageShareProtoWireGuard.qml index f5746e20..336964ac 100644 --- a/client/ui/qml/Pages/Share/PageShareProtoWireGuard.qml +++ b/client/ui/qml/Pages/Share/PageShareProtoWireGuard.qml @@ -91,7 +91,7 @@ PageShareProtocolBase { Layout.preferredHeight: 40 Layout.fillWidth: true - text: qsTr("Save to file") + text: Qt.platform.os === "android" ? qsTr("Share") : qsTr("Save to file") enabled: tfShareCode.textArea.length > 0 visible: tfShareCode.textArea.length > 0 diff --git a/client/ui/uilogic.cpp b/client/ui/uilogic.cpp index e1468da7..1922a1ab 100644 --- a/client/ui/uilogic.cpp +++ b/client/ui/uilogic.cpp @@ -134,7 +134,7 @@ void UiLogic::initalizeUiLogic() pageLogic()->onConnectionStateChanged(VpnProtocol::Connected); } }); - if (!AndroidController::instance()->initialize()) { + if (!AndroidController::instance()->initialize(pageLogic())) { qCritical() << QString("Init failed") ; emit VpnProtocol::Error; return; @@ -595,8 +595,9 @@ void UiLogic::saveTextFile(const QString& desc, const QString& suggestedName, QS if (fileName.isEmpty()) return; if (!fileName.toString().endsWith(ext)) fileName = QUrl(fileName.toString() + ext); #elif defined Q_OS_ANDROID - fileName = QFileDialog::getSaveFileUrl(nullptr, suggestedName, - QUrl::fromLocalFile(docDir), "*" + ext); + qDebug() << "UiLogic::shareConfig" << data; + AndroidController::instance()->shareConfig(data, suggestedName); + return; #endif if (fileName.isEmpty()) return; From 814b66c04a58435f248bb22d99e0af39a22df36f Mon Sep 17 00:00:00 2001 From: dartsyms <12047225+dartsyms@users.noreply.github.com> Date: Mon, 12 Dec 2022 16:16:12 +0400 Subject: [PATCH 34/43] Various fixes for iOS (#126) * Readme update, solution for wireguard make in M1 machines, * import file and restore enabled. * xcode_patcher.rb fixed, Now no need to add openVPN framewrok in Embed frameworks manually. * Now xcode_patcher.rb will add OpenVPN Framework to Embed Frameworks in main target, instead of Network extension. * Update iosvpnprotocol.swift * Protocol wasn't detected because it is working on localized description of tunnel, fixed cases. * Code cleanup * Speed issue fixed for wireguard. * GetDeviceIp and bytes(speed of OpenVPN) fixed. *Device IP method wasn't working as expected, so I replaced. and for speed in OpenVPN we need to handle message seperately for bytes. *QR progress added with progressbar and text. --- .gitignore | 1 + README.md | 5 + client/3rd/ShadowSocks/ss_ios.xcconfig | 23 --- client/client.pro | 1 + client/constants.h | 18 +-- client/fastlane/report.xml | 58 ------- client/ios/app/Info.plist | 6 +- client/macos/networkextension/Info.plist | 2 +- client/platforms/ios/QtAppDelegate.mm | 12 +- client/platforms/ios/iostunnel.swift | 17 ++- client/platforms/ios/iosvpnprotocol.swift | 75 +++++---- client/scripts/apple_compile.sh | 31 +--- client/scripts/xcode_patcher.rb | 167 +++------------------ client/ui/pages_logic/AppSettingsLogic.cpp | 4 +- client/ui/pages_logic/StartPageLogic.cpp | 5 +- client/ui/qml/Pages/PageQrDecoderIos.qml | 20 ++- client/ui/uilogic.cpp | 2 - client/versionfull.pri | 4 +- client/vpnconnection.cpp | 51 ++++++- client/vpnconnection.h | 8 + client/xcode.xconfig | 2 +- 21 files changed, 189 insertions(+), 323 deletions(-) delete mode 100644 client/3rd/ShadowSocks/ss_ios.xcconfig delete mode 100644 client/fastlane/report.xml diff --git a/.gitignore b/.gitignore index 1b4e16b2..e2fdf13d 100644 --- a/.gitignore +++ b/.gitignore @@ -125,3 +125,4 @@ captures/ # Android Profiling *.hprof +client/3rd/ShadowSocks/ss_ios.xcconfig diff --git a/README.md b/README.md index 4d5cd364..884e2205 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,11 @@ Error 1 Add a user defined variable to both AmneziaVPN and WireGuardNetworkExtension targets' build settings with key `PATH` and value `${PATH}/path/to/bin/folder/with/go/executable`, e.g. `${PATH}:/usr/local/go/bin`. +if above error still persists on you M1 Mac, then most proably you need to install arch based cmake +``` +arch -arm64 brew install cmake +``` + Build might fail with "source files not found" error the first time you try it, because modern XCode build system compiles dependencies in parallel, and some dependencies end up being built after the ones that require them. In this case simply restart the build. diff --git a/client/3rd/ShadowSocks/ss_ios.xcconfig b/client/3rd/ShadowSocks/ss_ios.xcconfig deleted file mode 100644 index 8b67bd01..00000000 --- a/client/3rd/ShadowSocks/ss_ios.xcconfig +++ /dev/null @@ -1,23 +0,0 @@ -SUPPORTED_PLATFORMS = iphoneos -TARGETED_DEVICE_FAMILY = 1,2 - -HEADER_SEARCH_PATHS = $(inherited) $(SRCROOT)/ShadowSocks -//HEADER_SEARCH_PATHS = $(inherited) $(SRCROOT)/ShadowSocks $(SRCROOT)/ShadowSocks/libcares/include $(SRCROOT)/ShadowSocks/libev/arm64/include $(SRCROOT)/ShadowSocks/libsodium/include $(SRCROOT)/ShadowSocks/mbedtls/include $(SRCROOT)/ShadowSocks/pcre/arm64/include $(SRCROOT)/ShadowSocks/shadowsocks-libev/include - -//CLANG_CXX_LANGUAGE_STANDARD = gnu++14 -//CLANG_CXX_LIBRARY = libc++ - -//////////////////////////////////////////////////////////////////////////////// -// -// iOS-specific settings -// -IPHONEOS_DEPLOYMENT_TARGET = 9.3 - -SDKROOT[arch=arm64] = iphoneos -SDKROOT[arch=armv7] = iphoneos -SDKROOT[arch=armv7s] = iphoneos - -VALID_ARCHS[sdk=iphoneos*] = arm64 -PROJECT_TEMP_DIR = /Users/sanchez/work/vied/ios/vpn/desktop-client-bkp/client/3rd/ShadowSocks/build/ShadowSocks.build -CONFIGURATION_BUILD_DIR = /Users/sanchez/work/vied/ios/vpn/desktop-client-bkp/client/3rd/ShadowSocks/build/Release-iphoneos -BUILT_PRODUCTS_DIR = /Users/sanchez/work/vied/ios/vpn/desktop-client-bkp/client/3rd/ShadowSocks/build/Release-iphoneos diff --git a/client/client.pro b/client/client.pro index 7840ae6a..439ceffa 100644 --- a/client/client.pro +++ b/client/client.pro @@ -301,6 +301,7 @@ ios { LIBS += -framework Foundation LIBS += -framework StoreKit LIBS += -framework UserNotifications + LIBS += -framework AVFoundation DEFINES += MVPN_IOS diff --git a/client/constants.h b/client/constants.h index 9059eccc..90f5e6eb 100644 --- a/client/constants.h +++ b/client/constants.h @@ -107,21 +107,5 @@ constexpr const char* PLATFORM_NAME = constexpr const char* PLACEHOLDER_USER_DNS = "127.0.0.1"; -#if defined(MVPN_ADJUST) -// These are the two auto-generated token from the Adjust dashboard for the -// "Subscription Completed" event. We have two since in the Adjust dashboard we -// have defined two apps for iOS and Android with a event token each. -constexpr const char* ADJUST_SUBSCRIPTION_COMPLETED = -# if defined(MVPN_IOS) - "jl72xm" -# elif defined(MVPN_ANDROID) - "o1mn9m" -# else - "" -# endif - ; -#endif - -}; // namespace Constants - +}; #endif // CONSTANTS_H diff --git a/client/fastlane/report.xml b/client/fastlane/report.xml deleted file mode 100644 index e0cdc969..00000000 --- a/client/fastlane/report.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/client/ios/app/Info.plist b/client/ios/app/Info.plist index 20a28b2a..f5681a20 100644 --- a/client/ios/app/Info.plist +++ b/client/ios/app/Info.plist @@ -38,13 +38,15 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - 2 + 7 ITSAppUsesNonExemptEncryption LSRequiresIPhoneOS + UIFileSharingEnabled + LSSupportsOpeningDocumentsInPlace - + NSCameraUsageDescription Amnezia VPN needs access to the camera for reading QR-codes. UILaunchStoryboardName diff --git a/client/macos/networkextension/Info.plist b/client/macos/networkextension/Info.plist index 08fc4d0a..96d82459 100644 --- a/client/macos/networkextension/Info.plist +++ b/client/macos/networkextension/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - 2 + $(CURRENT_PROJECT_VERSION) ITSAppUsesNonExemptEncryption LSMinimumSystemVersion diff --git a/client/platforms/ios/QtAppDelegate.mm b/client/platforms/ios/QtAppDelegate.mm index f63bea35..e6ec8444 100644 --- a/client/platforms/ios/QtAppDelegate.mm +++ b/client/platforms/ios/QtAppDelegate.mm @@ -2,7 +2,9 @@ #include -@implementation QtAppDelegate +@implementation QtAppDelegate { + UIView *_screen; +} +(QtAppDelegate *)sharedQtAppDelegate { static dispatch_once_t pred; @@ -26,6 +28,13 @@ { // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. + _screen = [UIScreen.mainScreen snapshotViewAfterScreenUpdates: false]; + UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle: UIBlurEffectStyleDark]; + UIVisualEffectView *blurBackround = [[UIVisualEffectView alloc] initWithEffect: blurEffect]; + [_screen addSubview: blurBackround]; + blurBackround.frame = _screen.frame; + UIWindow *_window = UIApplication.sharedApplication.keyWindow; + [_window addSubview: _screen]; } - (void)applicationDidEnterBackground:(UIApplication *)application @@ -44,6 +53,7 @@ - (void)applicationDidBecomeActive:(UIApplication *)application { // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. + [_screen removeFromSuperview]; } - (void)applicationWillTerminate:(UIApplication *)application diff --git a/client/platforms/ios/iostunnel.swift b/client/platforms/ios/iostunnel.swift index 5209306d..6d87a18d 100644 --- a/client/platforms/ios/iostunnel.swift +++ b/client/platforms/ios/iostunnel.swift @@ -125,7 +125,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider { case .wireguard: handleWireguardAppMessage(messageData, completionHandler: completionHandler) case .openvpn: - handleWireguardAppMessage(messageData, completionHandler: completionHandler) + handleOpenVPNAppMessage(messageData, completionHandler: completionHandler) case .shadowsocks: break // handleShadowSocksAppMessage(messageData, completionHandler: completionHandler) @@ -297,6 +297,21 @@ class PacketTunnelProvider: NEPacketTunnelProvider { } } + private func handleOpenVPNAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) { + guard let completionHandler = completionHandler else { return } + if messageData.count == 1 && messageData[0] == 0 { + let bytesin = ovpnAdapter.transportStatistics.bytesIn + let strBytesin = "rx_bytes=" + String(bytesin); + + let bytesout = ovpnAdapter.transportStatistics.bytesOut + let strBytesout = "tx_bytes=" + String(bytesout); + + let strData = strBytesin + "\n" + strBytesout; + let data = Data(strData.utf8) + completionHandler(data) + } + } + /* private func handleShadowSocksAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) { guard let completionHandler = completionHandler else { return } diff --git a/client/platforms/ios/iosvpnprotocol.swift b/client/platforms/ios/iosvpnprotocol.swift index 2c028915..95505b21 100644 --- a/client/platforms/ios/iosvpnprotocol.swift +++ b/client/platforms/ios/iosvpnprotocol.swift @@ -388,7 +388,7 @@ public class IOSVpnProtocolImpl : NSObject { proto.providerBundleIdentifier = vpnBundleID tunnel!.protocolConfiguration = proto - tunnel!.localizedDescription = vpnName + tunnel!.localizedDescription = "Amnezia Wireguard" tunnel!.isEnabled = true tunnel!.saveToPreferences { [unowned self] saveError in @@ -527,8 +527,9 @@ public class IOSVpnProtocolImpl : NSObject { @objc func checkStatus(callback: @escaping (String, String, String) -> Void) { Logger.global?.log(message: "Check status") // assert(tunnel != nil) - + print("check status") let protoType = (tunnel!.localizedDescription ?? "").toTunnelType + print(protoType); switch protoType { case .wireguard: @@ -559,7 +560,7 @@ public class IOSVpnProtocolImpl : NSObject { print("server IP: \(serverIpv4Gateway)") - let deviceIpv4Address = getTunIPAddress() + let deviceIpv4Address = getWiFiAddress() print("device IP: \(serverIpv4Gateway)") if deviceIpv4Address == nil { callback("", "", "") @@ -610,8 +611,9 @@ public class IOSVpnProtocolImpl : NSObject { print("server IP: \(serverIpv4Gateway)") - let deviceIpv4Address = getTunIPAddress() - print("device IP: \(serverIpv4Gateway)") + + let deviceIpv4Address = getWiFiAddress() + print("device IP: \(deviceIpv4Address)") if deviceIpv4Address == nil { callback("", "", "") return @@ -690,38 +692,45 @@ public class IOSVpnProtocolImpl : NSObject { } } - private func getTunIPAddress() -> String? { - var address: String? = nil - var interfaces: UnsafeMutablePointer? = nil - var temp_addr: UnsafeMutablePointer? = nil - var success: Int = 0 - - // retrieve the current interfaces - returns 0 on success - success = Int(getifaddrs(&interfaces)) - if success == 0 { - // Loop through linked list of interfaces - temp_addr = interfaces - while temp_addr != nil { - if temp_addr?.pointee.ifa_addr == nil { - continue + func getWiFiAddress() -> String? { + var address : String? + + // Get list of all interfaces on the local machine: + var ifaddr : UnsafeMutablePointer? + guard getifaddrs(&ifaddr) == 0 else { return nil } + guard let firstAddr = ifaddr else { return nil } + + // For each interface ... + for ifptr in sequence(first: firstAddr, next: { $0.pointee.ifa_next }) { + let interface = ifptr.pointee + + // Check for IPv4 or IPv6 interface: + let addrFamily = interface.ifa_addr.pointee.sa_family + //if addrFamily == UInt8(AF_INET) || addrFamily == UInt8(AF_INET6) { // **ipv6 committed + if addrFamily == UInt8(AF_INET){ + + // Check interface name: + let name = String(cString: interface.ifa_name) + if name == "en0" { + + // Convert interface address to a human readable string: + var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST)) + getnameinfo(interface.ifa_addr, socklen_t(interface.ifa_addr.pointee.sa_len), + &hostname, socklen_t(hostname.count), + nil, socklen_t(0), NI_NUMERICHOST) + address = String(cString: hostname) } - if temp_addr?.pointee.ifa_addr.pointee.sa_family == UInt8(AF_INET) { - // Check if interface is en0 which is the wifi connection on the iPhone - if let name = temp_addr?.pointee.ifa_name, ((String(utf8String: name)?.contains("tun")) != nil) { - // Get NSString from C String - if let value = temp_addr?.pointee.ifa_addr as? sockaddr_in { - address = String(utf8String: inet_ntoa(value.sin_addr)) - } - } - } - temp_addr = temp_addr?.pointee.ifa_next } } - freeifaddrs(interfaces) + freeifaddrs(ifaddr) + return address } + + } + enum TunnelType: String { case wireguard, openvpn, shadowsocks, empty } @@ -729,9 +738,9 @@ enum TunnelType: String { extension String { var toTunnelType: TunnelType { switch self { - case "wireguard": return .wireguard - case "openvpn": return .openvpn - case "shadowsocks": return .shadowsocks + case "Amnezia Wireguard": return .wireguard + case "Amnezia OpenVPN": return .openvpn + case "Amnezia ShadowSocks": return .shadowsocks default: return .empty } diff --git a/client/scripts/apple_compile.sh b/client/scripts/apple_compile.sh index fb44af30..403460fa 100755 --- a/client/scripts/apple_compile.sh +++ b/client/scripts/apple_compile.sh @@ -10,13 +10,11 @@ fi RELEASE=1 OS= NETWORKEXTENSION= -ADJUST_SDK_TOKEN= -ADJUST="CONFIG-=adjust" WORKINGDIR=`pwd` helpFunction() { print G "Usage:" - print N "\t$0 [-d|--debug] [-n|--networkextension] [-a|--adjusttoken ]" + print N "\t$0 [-d|--debug] [-n|--networkextension]" print N "" print N "By default, the project is compiled in release mode. Use -d or --debug for a debug build." print N "Use -n or --networkextension to force the network-extension component for MacOS too." @@ -26,7 +24,6 @@ helpFunction() { print G "Config variables:" print N "\tQT_MACOS_BIN=" print N "\tQT_IOS_BIN=" - print N "\tMVPN_IOS_ADJUST_TOKEN=" print N "" exit 0 } @@ -38,11 +35,6 @@ while [[ $# -gt 0 ]]; do key="$1" case $key in - -a | --adjusttoken) - ADJUST_SDK_TOKEN="$2" - shift - shift - ;; -d | --debug) RELEASE= shift @@ -97,11 +89,6 @@ if [[ "$OS" != "macos" ]] && [[ "$OS" != "ios" ]] && [[ "$OS" != "macostest" ]]; helpFunction fi -if ! [[ "$ADJUST_SDK_TOKEN" ]] && [[ "$MVPN_IOS_ADJUST_TOKEN" ]]; then - print Y "Using the MVPN_IOS_ADJUST_TOKEN value for the adjust token" - ADJUST_SDK_TOKEN=$MVPN_IOS_ADJUST_TOKEN -fi - if [[ "$OS" == "ios" ]]; then # Network-extension is the default for IOS NETWORKEXTENSION=1 @@ -150,7 +137,6 @@ MACOS_FLAGS=" QTPLUGIN+=qsvg CONFIG-=static CONFIG+=balrog - MVPN_MACOS=1 " MACOSTEST_FLAGS=" @@ -160,7 +146,6 @@ MACOSTEST_FLAGS=" " IOS_FLAGS=" - MVPN_IOS=1 Q_OS_IOS=1 " @@ -183,11 +168,6 @@ elif [ "$OS" = "macostest" ]; then PLATFORM=$MACOSTEST_FLAGS elif [ "$OS" = "ios" ]; then PLATFORM=$IOS_FLAGS - if [[ "$ADJUST_SDK_TOKEN" ]]; then - printn Y "ADJUST_SDK_TOKEN: " - print G "$ADJUST_SDK_TOKEN" - ADJUST="CONFIG+=adjust" - fi else killProcess "Why are we here?" fi @@ -249,7 +229,7 @@ else print Y "No Tun2Socks will be built" fi -print Y "Creating the xcode project via qmake..." +print Y "Creating the Xcode project via qmake..." $QMAKE \ VERSION=$SHORTVERSION \ BUILD_ID=$FULLVERSION \ @@ -258,11 +238,10 @@ $QMAKE \ $VPNMODE \ $WEMODE \ $PLATFORM \ - $ADJUST \ ./client.pro || killProcess "Compilation failed" print Y "Patching the xcode project..." -ruby scripts/xcode_patcher.rb "AmneziaVPN.xcodeproj" "$SHORTVERSION" "$FULLVERSION" "$OSRUBY" "$NETWORKEXTENSION" "$ADJUST_SDK_TOKEN" || killProcess "Failed to merge xcode with wireguard" +ruby scripts/xcode_patcher.rb "AmneziaVPN.xcodeproj" "$SHORTVERSION" "$FULLVERSION" "$OSRUBY" "$NETWORKEXTENSION" || killProcess "Failed to merge xcode with wireguard" print G "done." if command -v "sed" &>/dev/null; then @@ -270,6 +249,6 @@ print G "done." sed -i '' '/Original<\/string>/d' AmneziaVPN.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings fi -# print Y "Opening in XCode..." -# open AmneziaVPN.xcodeproj print G "All done!" +print Y "Opening project in Xcode..." +open AmneziaVPN.xcodeproj diff --git a/client/scripts/xcode_patcher.rb b/client/scripts/xcode_patcher.rb index 0a0550ca..6f9c4c41 100644 --- a/client/scripts/xcode_patcher.rb +++ b/client/scripts/xcode_patcher.rb @@ -9,7 +9,7 @@ class XCodeprojPatcher attr :target_main attr :target_extension - def run(file, shortVersion, fullVersion, platform, networkExtension, configHash, adjust_sdk_token) + def run(file, shortVersion, fullVersion, platform, networkExtension, configHash) open_project file setup_project open_target_main @@ -19,13 +19,7 @@ class XCodeprojPatcher group = @project.main_group.new_group('Configuration') @configFile = group.new_file('xcode.xconfig') - setup_target_main shortVersion, fullVersion, platform, networkExtension, configHash, adjust_sdk_token - -# if platform == 'macos' -# setup_target_loginitem shortVersion, fullVersion, configHash -# setup_target_nativemessaging shortVersion, fullVersion, configHash -# end - + setup_target_main shortVersion, fullVersion, platform, networkExtension, configHash if networkExtension setup_target_extension shortVersion, fullVersion, platform, configHash @@ -59,7 +53,7 @@ class XCodeprojPatcher end - def setup_target_main(shortVersion, fullVersion, platform, networkExtension, configHash, adjust_sdk_token) + def setup_target_main(shortVersion, fullVersion, platform, networkExtension, configHash) @target_main.build_configurations.each do |config| config.base_configuration_reference = @configFile @@ -72,10 +66,8 @@ class XCodeprojPatcher "$(PROJECT_DIR)/3rd", "$(PROJECT_DIR)/3rd/OpenVPNAdapter/build/Release-iphoneos", "$(PROJECT_DIR)/3rd/ShadowSocks/build/Release-iphoneos", -# "$(PROJECT_DIR)/3rd/PacketProcessor/build/Release-iphoneos", "$(PROJECT_DIR)/3rd/outline-go-tun2socks/build/ios", "${PROJECT_DIR}/3rd/CocoaAsyncSocket/build/Release-iphoneos", -# "${PROJECT_DIR}/3rd/CocoaLumberjack/build/Release-iphoneos", ] # Versions and names @@ -91,9 +83,6 @@ class XCodeprojPatcher config.build_settings['INFOPLIST_FILE'] ||= platform + '/app/Info.plist' if platform == 'ios' config.build_settings['CODE_SIGN_ENTITLEMENTS'] ||= 'ios/app/main.entitlements' - if adjust_sdk_token != "" - config.build_settings['ADJUST_SDK_TOKEN'] = adjust_sdk_token - end elsif networkExtension config.build_settings['CODE_SIGN_ENTITLEMENTS'] ||= 'macos/app/app.entitlements' else @@ -104,7 +93,7 @@ class XCodeprojPatcher config.build_settings['ENABLE_BITCODE'] ||= 'NO' if platform == 'ios' config.build_settings['SDKROOT'] = 'iphoneos' if platform == 'ios' config.build_settings['SWIFT_PRECOMPILE_BRIDGING_HEADER'] = 'NO' if platform == 'ios' - config.build_settings['PATH'] = '${PATH}:/usr/local/go/bin:/usr/local/bin:/opt/homebrew/bin' + config.build_settings['PATH'] = '${PATH}:/opt/local/bin:/usr/local/go/bin:/usr/local/bin:/opt/homebrew/bin' groupId = ""; if (platform == 'macos') @@ -173,96 +162,23 @@ class XCodeprojPatcher } end - if (platform == 'ios' && adjust_sdk_token != "") + if(platform == 'ios') + frameworks_group = @project.groups.find { |group| group.display_name == 'Frameworks' } frameworks_build_phase = @target_main.build_phases.find { |build_phase| build_phase.to_s == 'FrameworksBuildPhase' } - framework_ref = frameworks_group.new_file('AdServices.framework') - build_file = frameworks_build_phase.add_file_reference(framework_ref) - build_file.settings = { 'ATTRIBUTES' => ['Weak'] } + embed_frameworks_build_phase = project.new(Xcodeproj::Project::Object::PBXCopyFilesBuildPhase) + embed_frameworks_build_phase.name = 'Embed Frameworks' + embed_frameworks_build_phase.symbol_dst_subfolder_spec = :frameworks + @target_main.build_phases << embed_frameworks_build_phase - framework_ref = frameworks_group.new_file('iAd.framework') - frameworks_build_phase.add_file_reference(framework_ref) + framework_ref = frameworks_group.new_file('3rd/OpenVPNAdapter/build/Release-iphoneos/OpenVPNAdapter.framework') + build_file = embed_frameworks_build_phase.add_file_reference(framework_ref) - # Adjust SDK - group = @project.main_group.new_group('AdjustSDK') - - [ - '3rd/adjust-ios-sdk/Adjust/ADJActivityHandler.h', - '3rd/adjust-ios-sdk/Adjust/ADJActivityKind.h', - '3rd/adjust-ios-sdk/Adjust/ADJActivityPackage.h', - '3rd/adjust-ios-sdk/Adjust/ADJActivityState.h', - '3rd/adjust-ios-sdk/Adjust/ADJAdjustFactory.h', - '3rd/adjust-ios-sdk/Adjust/ADJAdRevenue.h', - '3rd/adjust-ios-sdk/Adjust/ADJAttribution.h', - '3rd/adjust-ios-sdk/Adjust/ADJAttributionHandler.h', - '3rd/adjust-ios-sdk/Adjust/ADJBackoffStrategy.h', - '3rd/adjust-ios-sdk/Adjust/ADJAdditions/NSData+ADJAdditions.h', - '3rd/adjust-ios-sdk/Adjust/ADJAdditions/NSNumber+ADJAdditions.h', - '3rd/adjust-ios-sdk/Adjust/ADJAdditions/NSString+ADJAdditions.h', - '3rd/adjust-ios-sdk/Adjust/ADJConfig.h', - '3rd/adjust-ios-sdk/Adjust/ADJEvent.h', - '3rd/adjust-ios-sdk/Adjust/ADJEventFailure.h', - '3rd/adjust-ios-sdk/Adjust/ADJEventSuccess.h', - '3rd/adjust-ios-sdk/Adjust/ADJLinkResolution.h', - '3rd/adjust-ios-sdk/Adjust/ADJLogger.h', - '3rd/adjust-ios-sdk/Adjust/ADJPackageBuilder.h', - '3rd/adjust-ios-sdk/Adjust/ADJPackageHandler.h', - '3rd/adjust-ios-sdk/Adjust/ADJPackageParams.h', - '3rd/adjust-ios-sdk/Adjust/ADJRequestHandler.h', - '3rd/adjust-ios-sdk/Adjust/ADJResponseData.h', - '3rd/adjust-ios-sdk/Adjust/ADJSdkClickHandler.h', - '3rd/adjust-ios-sdk/Adjust/ADJSessionFailure.h', - '3rd/adjust-ios-sdk/Adjust/ADJSessionParameters.h', - '3rd/adjust-ios-sdk/Adjust/ADJSessionSuccess.h', - '3rd/adjust-ios-sdk/Adjust/ADJSubscription.h', - '3rd/adjust-ios-sdk/Adjust/ADJThirdPartySharing.h', - '3rd/adjust-ios-sdk/Adjust/ADJTimerCycle.h', - '3rd/adjust-ios-sdk/Adjust/ADJTimerOnce.h', - '3rd/adjust-ios-sdk/Adjust/ADJUrlStrategy.h', - '3rd/adjust-ios-sdk/Adjust/ADJUserDefaults.h', - '3rd/adjust-ios-sdk/Adjust/Adjust.h', - '3rd/adjust-ios-sdk/Adjust/ADJUtil.h', - '3rd/adjust-ios-sdk/Adjust/ADJActivityHandler.m', - '3rd/adjust-ios-sdk/Adjust/ADJActivityKind.m', - '3rd/adjust-ios-sdk/Adjust/ADJActivityPackage.m', - '3rd/adjust-ios-sdk/Adjust/ADJActivityState.m', - '3rd/adjust-ios-sdk/Adjust/ADJAdjustFactory.m', - '3rd/adjust-ios-sdk/Adjust/ADJAdRevenue.m', - '3rd/adjust-ios-sdk/Adjust/ADJAttribution.m', - '3rd/adjust-ios-sdk/Adjust/ADJAttributionHandler.m', - '3rd/adjust-ios-sdk/Adjust/ADJBackoffStrategy.m', - '3rd/adjust-ios-sdk/Adjust/ADJAdditions/NSData+ADJAdditions.m', - '3rd/adjust-ios-sdk/Adjust/ADJAdditions/NSNumber+ADJAdditions.m', - '3rd/adjust-ios-sdk/Adjust/ADJAdditions/NSString+ADJAdditions.m', - '3rd/adjust-ios-sdk/Adjust/ADJConfig.m', - '3rd/adjust-ios-sdk/Adjust/ADJEvent.m', - '3rd/adjust-ios-sdk/Adjust/ADJEventFailure.m', - '3rd/adjust-ios-sdk/Adjust/ADJEventSuccess.m', - '3rd/adjust-ios-sdk/Adjust/ADJLinkResolution.m', - '3rd/adjust-ios-sdk/Adjust/ADJLogger.m', - '3rd/adjust-ios-sdk/Adjust/ADJPackageBuilder.m', - '3rd/adjust-ios-sdk/Adjust/ADJPackageHandler.m', - '3rd/adjust-ios-sdk/Adjust/ADJPackageParams.m', - '3rd/adjust-ios-sdk/Adjust/ADJRequestHandler.m', - '3rd/adjust-ios-sdk/Adjust/ADJResponseData.m', - '3rd/adjust-ios-sdk/Adjust/ADJSdkClickHandler.m', - '3rd/adjust-ios-sdk/Adjust/ADJSessionFailure.m', - '3rd/adjust-ios-sdk/Adjust/ADJSessionParameters.m', - '3rd/adjust-ios-sdk/Adjust/ADJSessionSuccess.m', - '3rd/adjust-ios-sdk/Adjust/ADJSubscription.m', - '3rd/adjust-ios-sdk/Adjust/ADJThirdPartySharing.m', - '3rd/adjust-ios-sdk/Adjust/ADJTimerCycle.m', - '3rd/adjust-ios-sdk/Adjust/ADJTimerOnce.m', - '3rd/adjust-ios-sdk/Adjust/ADJUrlStrategy.m', - '3rd/adjust-ios-sdk/Adjust/ADJUserDefaults.m', - '3rd/adjust-ios-sdk/Adjust/Adjust.m', - '3rd/adjust-ios-sdk/Adjust/ADJUtil.m', - ].each { |filename| - file = group.new_file(filename) - file_reference = @target_main.add_file_references([file], '-fobjc-arc') - } + frameworks_build_phase.add_file_reference(framework_ref) + build_file.settings = { 'ATTRIBUTES' => ['CodeSignOnCopy', 'RemoveHeadersOnCopy'] } end + end def setup_target_extension(shortVersion, fullVersion, platform, configHash) @@ -288,12 +204,9 @@ class XCodeprojPatcher "$(PROJECT_DIR)/3rd/OpenVPNAdapter/build/Release-iphoneos", "$(PROJECT_DIR)/3rd/libleaf/lib", "$(PROJECT_DIR)/3rd/ShadowSocks/build/Release-iphoneos", -# "$(PROJECT_DIR)/3rd/PacketProcessor/build/Release-iphoneos", "$(PROJECT_DIR)/3rd/outline-go-tun2socks/build/ios", "${PROJECT_DIR}/3rd/CocoaAsyncSocket/build/Release-iphoneos", -# "${PROJECT_DIR}/3rd/CocoaLumberjack/build/Release-iphoneos", ] -# config.build_settings['LIBRARY_SEARCH_PATHS'] = [config.build_settings['LIBRARY_SEARCH_PATHS'], "$(PROJECT_DIR)/3rd/libleaf/lib"] # Versions and names config.build_settings['MARKETING_VERSION'] ||= shortVersion @@ -325,7 +238,7 @@ class XCodeprojPatcher "-framework", "OpenGLES", ] - config.build_settings['PATH'] = '${PATH}:/usr/local/go/bin' + config.build_settings['PATH'] = '${PATH}:/opt/local/bin:/usr/local/go/bin' end groupId = ""; @@ -379,17 +292,7 @@ class XCodeprojPatcher 'platforms/ios/iostunnel.swift', 'platforms/ios/ioslogger.swift', 'platforms/ios/iosinterface.swift', -# 'platforms/ios/ssprovider.swift', 'platforms/ios/iosglue.mm', -# 'platforms/ios/ssconnectivity.h', -# 'platforms/ios/ssconnectivity.m', -# 'platforms/ios/iosopenvpn2ssadapter.h', -# 'platforms/ios/iosopenvpn2ssadapter.m', -# 'platforms/ios/sspacket.h', -# 'platforms/ios/sspacket.m', -# 'platforms/ios/ssadapterpacketflow.h', -# 'platforms/ios/tun2ssprovider.swift', -# 'platforms/ios/tun2sockswriter.swift', ].each { |filename| file = group.new_file(filename) @target_extension.add_file_references([file]) @@ -402,40 +305,10 @@ class XCodeprojPatcher framework_ref = frameworks_group.new_file('libwg-go.a') frameworks_build_phase.add_file_reference(framework_ref) - -# framework_ref = frameworks_group.new_file('3rd/libleaf/lib/libleaf.a') -# frameworks_build_phase.add_file_reference(framework_ref) framework_ref = frameworks_group.new_file('NetworkExtension.framework') frameworks_build_phase.add_file_reference(framework_ref) -# framework_ref = frameworks_group.new_file('3rd/OpenVPNAdapter/build/Release-iphoneos/LZ4.framework') -# frameworks_build_phase.add_file_reference(framework_ref) -# -# framework_ref = frameworks_group.new_file('3rd/OpenVPNAdapter/build/Release-iphoneos/mbedTLS.framework') -# frameworks_build_phase.add_file_reference(framework_ref) -# -# framework_ref = frameworks_group.new_file('3rd/OpenVPNAdapter/build/Release-iphoneos/OpenVPNClient.framework') -# frameworks_build_phase.add_file_reference(framework_ref) - - framework_ref = frameworks_group.new_file('3rd/OpenVPNAdapter/build/Release-iphoneos/OpenVPNAdapter.framework') - frameworks_build_phase.add_file_reference(framework_ref) - -# framework_ref = frameworks_group.new_file('3rd/ShadowSocks/build/Release-iphoneos/ShadowSocks.framework') -# frameworks_build_phase.add_file_reference(framework_ref) -# -# framework_ref = frameworks_group.new_file('3rd/CocoaAsyncSocket/build/Release-iphoneos/CocoaAsyncSocket.framework') -# frameworks_build_phase.add_file_reference(framework_ref) -# -# framework_ref = frameworks_group.new_file('3rd/outline-go-tun2socks/build/ios/Tun2socks.xcframework') -# frameworks_build_phase.add_file_reference(framework_ref) - -# framework_ref = frameworks_group.new_file('3rd/CocoaLumberjack/build/Release-iphoneos/CocoaLumberjack.framework') -# frameworks_build_phase.add_file_reference(framework_ref) - - - - # This fails: @target_main.add_dependency @target_extension container_proxy = @project.new(Xcodeproj::Project::PBXContainerItemProxy) container_proxy.container_portal = @project.root_object.uuid @@ -492,6 +365,7 @@ class XCodeprojPatcher framework_ref = frameworks_group.new_file('balrog/balrog.a') frameworks_build_phase.add_file_reference(framework_ref) + # This fails: @target_main.add_dependency target_balrog container_proxy = @project.new(Xcodeproj::Project::PBXContainerItemProxy) container_proxy.container_portal = @project.root_object.uuid @@ -599,7 +473,7 @@ class XCodeprojPatcher # other configs config.build_settings['INFOPLIST_FILE'] ||= 'macos/loginitem/Info.plist' - config.build_settings['CODE_SIGN_ENTITLEMENTS'] ||= 'macos/loginitem/MozillaVPNLoginItem.entitlements' + config.build_settings['CODE_SIGN_ENTITLEMENTS'] ||= 'macos/loginitem/MozillaVPNLoginItem.entitlements' #TODO need to check this config.build_settings['CODE_SIGN_IDENTITY'] = 'Apple Development' config.build_settings['SKIP_INSTALL'] = 'YES' @@ -708,7 +582,7 @@ class XCodeprojPatcher copy_nativeMessagingManifest.dst_path = 'Contents/Resources/utils' group = @project.main_group.new_group('WireGuardHelper') - file = group.new_file 'extension/app/manifests/macos/mozillavpn.json' + file = group.new_file 'extension/app/manifests/macos/mozillavpn.json' #TODO Need to check this nativeMessagingManifest_file = copy_nativeMessagingManifest.add_file_reference file nativeMessagingManifest_file.settings = { "ATTRIBUTES" => ['RemoveHeadersOnCopy'] } @@ -744,8 +618,7 @@ configFile.each { |line| platform = "macos" platform = "ios" if ARGV[3] == "ios" networkExtension = true if ARGV[4] == "1" -adjust_sdk_token = ARGV[5] r = XCodeprojPatcher.new -r.run ARGV[0], ARGV[1], ARGV[2], platform, networkExtension, config, adjust_sdk_token +r.run ARGV[0], ARGV[1], ARGV[2], platform, networkExtension, config exit 0 diff --git a/client/ui/pages_logic/AppSettingsLogic.cpp b/client/ui/pages_logic/AppSettingsLogic.cpp index b2907c3d..b22918b2 100644 --- a/client/ui/pages_logic/AppSettingsLogic.cpp +++ b/client/ui/pages_logic/AppSettingsLogic.cpp @@ -83,8 +83,8 @@ void AppSettingsLogic::onPushButtonBackupAppConfigClicked() void AppSettingsLogic::onPushButtonRestoreAppConfigClicked() { - QString fileName = QFileDialog::getOpenFileName(nullptr, tr("Open backup"), - QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), "*.backup"); + QString fileName = QFileDialog::getOpenFileName(Q_NULLPTR, tr("Open backup"), + QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), "*.backup"); if (fileName.isEmpty()) return; diff --git a/client/ui/pages_logic/StartPageLogic.cpp b/client/ui/pages_logic/StartPageLogic.cpp index 1e28d6cd..a5a0a38d 100644 --- a/client/ui/pages_logic/StartPageLogic.cpp +++ b/client/ui/pages_logic/StartPageLogic.cpp @@ -135,9 +135,8 @@ void StartPageLogic::onPushButtonImport() void StartPageLogic::onPushButtonImportOpenFile() { - QString fileName = QFileDialog::getOpenFileName(nullptr, tr("Open profile"), - QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), "*.vpn"); - + QString fileName = QFileDialog::getOpenFileName(Q_NULLPTR, tr("Open profile"), + QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), tr("*.vpn")); if (fileName.isEmpty()) return; QFile file(fileName); diff --git a/client/ui/qml/Pages/PageQrDecoderIos.qml b/client/ui/qml/Pages/PageQrDecoderIos.qml index 7dd077dd..0e796312 100644 --- a/client/ui/qml/Pages/PageQrDecoderIos.qml +++ b/client/ui/qml/Pages/PageQrDecoderIos.qml @@ -40,11 +40,29 @@ PageBase { id: loader anchors.top: caption.bottom - anchors.bottom: parent.bottom + anchors.bottom: progressColumn.top anchors.left: parent.left anchors.right: parent.right } + Column{ + height: 40 + id: progressColumn + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + ProgressBar { + id: progress + anchors.left: parent.left + anchors.right: parent.right + value: QrDecoderLogic.totalChunksCount === 0? 0 : (QrDecoderLogic.receivedChunksCount/QrDecoderLogic.totalChunksCount) + } + Text { + id: chunksCount + text: "Progress: " + QrDecoderLogic.receivedChunksCount +"/"+QrDecoderLogic.totalChunksCount + } + } + Component { id: component diff --git a/client/ui/uilogic.cpp b/client/ui/uilogic.cpp index e1468da7..2d93d429 100644 --- a/client/ui/uilogic.cpp +++ b/client/ui/uilogic.cpp @@ -266,8 +266,6 @@ void UiLogic::onGotoCurrentProtocolsPage() emit goToPage(Page::ServerContainers); } - - //void UiLogic::showEvent(QShowEvent *event) //{ //#if defined Q_OS_MACX diff --git a/client/versionfull.pri b/client/versionfull.pri index 5fc93c79..5ee6310c 100644 --- a/client/versionfull.pri +++ b/client/versionfull.pri @@ -1,2 +1,2 @@ -# current build 1 -!defined(BUILDVERSION, var):BUILDVERSION = 2 \ No newline at end of file +# current build 6 +!defined(BUILDVERSION, var):BUILDVERSION = 7 \ No newline at end of file diff --git a/client/vpnconnection.cpp b/client/vpnconnection.cpp index 6e117ce4..6a981f0c 100644 --- a/client/vpnconnection.cpp +++ b/client/vpnconnection.cpp @@ -1,3 +1,4 @@ +#include "qtimer.h" #include #include #include @@ -34,7 +35,10 @@ VpnConnection::VpnConnection(std::shared_ptr settings, std::shared_ptr serverController, QObject* parent) : QObject(parent), m_settings(settings), m_configurator(configurator), - m_serverController(serverController) + m_serverController(serverController), + m_receivedBytes(0), + m_sentBytes(0), + m_isIOSConnected(false) { } @@ -48,11 +52,16 @@ VpnConnection::~VpnConnection() void VpnConnection::onBytesChanged(quint64 receivedBytes, quint64 sentBytes) { - emit bytesChanged(receivedBytes, sentBytes); + emit bytesChanged(receivedBytes - m_receivedBytes, sentBytes - m_sentBytes); + + m_receivedBytes = receivedBytes; + m_sentBytes = sentBytes; + } void VpnConnection::onConnectionStateChanged(VpnProtocol::VpnConnectionState state) { + #ifdef AMNEZIA_DESKTOP if (IpcClient::Interface()) { if (state == VpnProtocol::Connected){ @@ -94,9 +103,35 @@ void VpnConnection::onConnectionStateChanged(VpnProtocol::VpnConnectionState sta } } #endif + +#ifdef Q_OS_IOS + + qDebug() << state; + if(state == VpnProtocol::Connected){ + m_isIOSConnected = true; + checkIOSStatus(); + }else{ + + m_isIOSConnected = false; + m_receivedBytes = 0; + m_sentBytes = 0; + } +#endif emit connectionStateChanged(state); } +void VpnConnection::checkIOSStatus() +{ + QTimer::singleShot(1000, [this]() { + + if(m_isIOSConnected){ + iosVpnProtocol->checkStatus(); + checkIOSStatus(); + } + + } ); +} + const QString &VpnConnection::remoteAddress() const { return m_remoteAddress; @@ -333,7 +368,10 @@ void VpnConnection::connectToVpn(int serverIndex, m_vpnProtocol.reset(androidVpnProtocol); #elif defined Q_OS_IOS Proto proto = ContainerProps::defaultProtocol(container); - IOSVpnProtocol *iosVpnProtocol = new IOSVpnProtocol(proto, m_vpnConfiguration); + //if (iosVpnProtocol==NULL) { + iosVpnProtocol = new IOSVpnProtocol(proto, m_vpnConfiguration); + //} + // IOSVpnProtocol *iosVpnProtocol = new IOSVpnProtocol(proto, m_vpnConfiguration); if (!iosVpnProtocol->initialize()) { qDebug() << QString("Init failed") ; emit VpnProtocol::Error; @@ -384,12 +422,19 @@ void VpnConnection::disconnectFromVpn() VpnProtocol::VpnConnectionState VpnConnection::connectionState() { + + if (!m_vpnProtocol) return VpnProtocol::Disconnected; return m_vpnProtocol->connectionState(); + } bool VpnConnection::isConnected() const { +#ifdef Q_OS_IOS + +#endif + if (!m_vpnProtocol.data()) { return false; } diff --git a/client/vpnconnection.h b/client/vpnconnection.h index 18446e74..28c8ef0b 100644 --- a/client/vpnconnection.h +++ b/client/vpnconnection.h @@ -9,6 +9,7 @@ #include "protocols/vpnprotocol.h" #include "core/defs.h" #include "settings.h" +#include "protocols/ios_vpnprotocol.h" #ifdef AMNEZIA_DESKTOP #include "core/ipcclient.h" @@ -73,6 +74,7 @@ signals: protected slots: void onBytesChanged(quint64 receivedBytes, quint64 sentBytes); void onConnectionStateChanged(VpnProtocol::VpnConnectionState state); + void checkIOSStatus(); protected: QSharedPointer m_vpnProtocol; @@ -85,10 +87,16 @@ private: QJsonObject m_vpnConfiguration; QJsonObject m_routeMode; QString m_remoteAddress; + quint64 m_receivedBytes; + quint64 m_sentBytes; + bool m_isIOSConnected; //remove later move to isConnected, #ifdef AMNEZIA_DESKTOP IpcClient *m_IpcClient {nullptr}; #endif +#ifdef Q_OS_IOS + IOSVpnProtocol * iosVpnProtocol{nullptr}; +#endif }; #endif // VPNCONNECTION_H diff --git a/client/xcode.xconfig b/client/xcode.xconfig index 62778188..bbed6a13 100644 --- a/client/xcode.xconfig +++ b/client/xcode.xconfig @@ -2,4 +2,4 @@ DEVELOPMENT_TEAM = X7UJ388FXK GROUP_ID_IOS = group.org.amnezia.AmneziaVPN APP_ID_IOS = org.amnezia.AmneziaVPN -NETEXT_ID_IOS = org.amnezia.AmneziaVPN.network-extension +NETEXT_ID_IOS = org.amnezia.AmneziaVPN.network-extension \ No newline at end of file From f20f528a1140d1c203b5ca1b4ef3ba7c8b08cf81 Mon Sep 17 00:00:00 2001 From: Hamza ARBI <43709620+HamzArbi@users.noreply.github.com> Date: Mon, 12 Dec 2022 13:28:03 +0100 Subject: [PATCH 35/43] Server selection always in range (#130) * Server selection stays always in the list's range * Removed the usage of QZxing module from PageQrDecoder page * Fixed null spelling on qml instead of nil --- client/ui/pages_logic/ServerListLogic.cpp | 6 ++++++ client/ui/pages_logic/ServerListLogic.h | 6 ++++++ client/ui/qml/Pages/PageQrDecoder.qml | 4 ++-- client/ui/qml/Pages/PageQrDecoderIos.qml | 2 +- client/ui/qml/Pages/PageServerList.qml | 7 +++---- client/ui/qml/Pages/Protocols/PageProtoTorWebSite.qml | 1 - 6 files changed, 18 insertions(+), 8 deletions(-) diff --git a/client/ui/pages_logic/ServerListLogic.cpp b/client/ui/pages_logic/ServerListLogic.cpp index fe3f6512..42a629b3 100644 --- a/client/ui/pages_logic/ServerListLogic.cpp +++ b/client/ui/pages_logic/ServerListLogic.cpp @@ -15,6 +15,7 @@ void ServerListLogic::onServerListPushbuttonDefaultClicked(int index) { m_settings->setDefaultServer(index); uiLogic()->onUpdateAllPages(); + emit currServerIdxChanged(); } void ServerListLogic::onServerListPushbuttonSettingsClicked(int index) @@ -23,6 +24,11 @@ void ServerListLogic::onServerListPushbuttonSettingsClicked(int index) uiLogic()->goToPage(Page::ServerSettings); } +int ServerListLogic::currServerIdx() const +{ + return m_settings->defaultServerIndex(); +} + void ServerListLogic::onUpdatePage() { const QJsonArray &servers = m_settings->serversArray(); diff --git a/client/ui/pages_logic/ServerListLogic.h b/client/ui/pages_logic/ServerListLogic.h index 2671b97d..b4f47547 100644 --- a/client/ui/pages_logic/ServerListLogic.h +++ b/client/ui/pages_logic/ServerListLogic.h @@ -10,8 +10,11 @@ class ServerListLogic : public PageLogicBase Q_OBJECT READONLY_PROPERTY(QObject *, serverListModel) + Q_PROPERTY(int currServerIdx READ currServerIdx NOTIFY currServerIdxChanged) public: + int currServerIdx() const; + Q_INVOKABLE void onUpdatePage() override; Q_INVOKABLE void onServerListPushbuttonDefaultClicked(int index); Q_INVOKABLE void onServerListPushbuttonSettingsClicked(int index); @@ -20,5 +23,8 @@ public: explicit ServerListLogic(UiLogic *uiLogic, QObject *parent = nullptr); ~ServerListLogic() = default; +signals: + void currServerIdxChanged(); + }; #endif // SERVER_LIST_LOGIC_H diff --git a/client/ui/qml/Pages/PageQrDecoder.qml b/client/ui/qml/Pages/PageQrDecoder.qml index f432b742..61ce368f 100644 --- a/client/ui/qml/Pages/PageQrDecoder.qml +++ b/client/ui/qml/Pages/PageQrDecoder.qml @@ -26,7 +26,7 @@ PageBase { } Connections { - target: Qt.platform.os != "ios" ? QrDecoderLogic : nil + target: Qt.platform.os != "ios" ? QrDecoderLogic : null function onStartDecode() { console.debug("Starting QR decoder") loader.sourceComponent = component @@ -71,7 +71,7 @@ PageBase { anchors.right: parent.right autoOrientation: true fillMode: VideoOutput.PreserveAspectFit - filters: [ zxingFilter ] +// filters: [ zxingFilter ] Rectangle { diff --git a/client/ui/qml/Pages/PageQrDecoderIos.qml b/client/ui/qml/Pages/PageQrDecoderIos.qml index 0e796312..7e5783e8 100644 --- a/client/ui/qml/Pages/PageQrDecoderIos.qml +++ b/client/ui/qml/Pages/PageQrDecoderIos.qml @@ -25,7 +25,7 @@ PageBase { } Connections { - target: Qt.platform.os == "ios" ? QrDecoderLogic : nil + target: Qt.platform.os == "ios" ? QrDecoderLogic : null function onStartDecode() { console.debug("Starting QR decoder") loader.sourceComponent = component diff --git a/client/ui/qml/Pages/PageServerList.qml b/client/ui/qml/Pages/PageServerList.qml index 583de4b3..de6f90ab 100644 --- a/client/ui/qml/Pages/PageServerList.qml +++ b/client/ui/qml/Pages/PageServerList.qml @@ -42,6 +42,9 @@ PageBase { anchors.bottom: parent.bottom anchors.bottomMargin: 20 model: ServerListLogic.serverListModel + highlightRangeMode: ListView.ApplyRange + highlightMoveVelocity: -1 + currentIndex: ServerListLogic.currServerIdx spacing: 5 clip: true delegate: Item { @@ -55,10 +58,6 @@ PageBase { if (GC.isMobile()) { ServerListLogic.onServerListPushbuttonSettingsClicked(index) } - else { - listWidget_servers.currentIndex = index - } - mouse.accepted = false } onEntered: { diff --git a/client/ui/qml/Pages/Protocols/PageProtoTorWebSite.qml b/client/ui/qml/Pages/Protocols/PageProtoTorWebSite.qml index 1285e8bb..50f4bd4e 100644 --- a/client/ui/qml/Pages/Protocols/PageProtoTorWebSite.qml +++ b/client/ui/qml/Pages/Protocols/PageProtoTorWebSite.qml @@ -27,7 +27,6 @@ PageProtocolBase { anchors.top: caption.bottom anchors.left: root.left anchors.right: root.right - anchors.bottom: pb_save.top anchors.margins: 20 anchors.topMargin: 10 From 9c5e1faf46e11d8be81eac53ade7c5ed5e0f31dd Mon Sep 17 00:00:00 2001 From: pokamest Date: Mon, 12 Dec 2022 14:41:54 +0100 Subject: [PATCH 36/43] Build fixes --- README.md | 2 +- client/vpnconnection.cpp | 12 +++++------- client/vpnconnection.h | 6 ++++++ 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 884e2205..4d5f993c 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Amnezia VPN ## _The best client for self-hosted VPN_ -[![Build Status](https://travis-ci.com/amnezia-vpn/desktop-client.svg?branch=master)](https://travis-ci.com/amnezia-vpn/desktop-client) +[![Build Status](https://github.com/amnezia-vpn/desktop-client/actions/workflows/deploy.yml/badge.svg?branch=dev)] Amnezia is a VPN client with the key feature of deploying your own VPN server on you virtual server. diff --git a/client/vpnconnection.cpp b/client/vpnconnection.cpp index 6a981f0c..286ffc8c 100644 --- a/client/vpnconnection.cpp +++ b/client/vpnconnection.cpp @@ -105,13 +105,11 @@ void VpnConnection::onConnectionStateChanged(VpnProtocol::VpnConnectionState sta #endif #ifdef Q_OS_IOS - - qDebug() << state; - if(state == VpnProtocol::Connected){ + if (state == VpnProtocol::Connected){ m_isIOSConnected = true; checkIOSStatus(); - }else{ - + } + else { m_isIOSConnected = false; m_receivedBytes = 0; m_sentBytes = 0; @@ -120,17 +118,17 @@ void VpnConnection::onConnectionStateChanged(VpnProtocol::VpnConnectionState sta emit connectionStateChanged(state); } +#ifdef Q_OS_IOS void VpnConnection::checkIOSStatus() { QTimer::singleShot(1000, [this]() { - if(m_isIOSConnected){ iosVpnProtocol->checkStatus(); checkIOSStatus(); } - } ); } +#endif const QString &VpnConnection::remoteAddress() const { diff --git a/client/vpnconnection.h b/client/vpnconnection.h index 28c8ef0b..c8ebdfad 100644 --- a/client/vpnconnection.h +++ b/client/vpnconnection.h @@ -9,7 +9,10 @@ #include "protocols/vpnprotocol.h" #include "core/defs.h" #include "settings.h" + +#ifdef Q_OS_IOS #include "protocols/ios_vpnprotocol.h" +#endif #ifdef AMNEZIA_DESKTOP #include "core/ipcclient.h" @@ -74,7 +77,10 @@ signals: protected slots: void onBytesChanged(quint64 receivedBytes, quint64 sentBytes); void onConnectionStateChanged(VpnProtocol::VpnConnectionState state); + +#ifdef Q_OS_IOS void checkIOSStatus(); +#endif protected: QSharedPointer m_vpnProtocol; From 9222877306f5b71293072db715512049762f1750 Mon Sep 17 00:00:00 2001 From: Hamza ARBI Date: Sat, 17 Dec 2022 12:06:19 +0100 Subject: [PATCH 37/43] Show Scrollbars whenever the scroll is not obvious --- client/ui/qml/Pages/PageAppSetting.qml | 7 ++++++- client/ui/qml/Pages/PageServerList.qml | 8 ++++++-- client/ui/qml/Pages/PageShareConnection.qml | 1 + 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/client/ui/qml/Pages/PageAppSetting.qml b/client/ui/qml/Pages/PageAppSetting.qml index 7e8d415a..1b0f05c0 100644 --- a/client/ui/qml/Pages/PageAppSetting.qml +++ b/client/ui/qml/Pages/PageAppSetting.qml @@ -29,7 +29,7 @@ PageBase { anchors.left: root.left anchors.leftMargin: 30 anchors.right: root.right - anchors.rightMargin: 30 + anchors.rightMargin: 15 contentHeight: content.height clip: true @@ -40,6 +40,7 @@ PageBase { anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right + anchors.rightMargin: 15 CheckBoxType { visible: !GC.isMobile() @@ -161,6 +162,10 @@ PageBase { } } } + + ScrollBar.vertical: ScrollBar { + policy: fl.height > content.height? ScrollBar.AlwaysOff : ScrollBar.AlwaysOn + } } Logo { diff --git a/client/ui/qml/Pages/PageServerList.qml b/client/ui/qml/Pages/PageServerList.qml index de6f90ab..7791cc05 100644 --- a/client/ui/qml/Pages/PageServerList.qml +++ b/client/ui/qml/Pages/PageServerList.qml @@ -38,7 +38,7 @@ PageBase { x: 20 anchors.top: caption.bottom anchors.topMargin: 15 - width: parent.width + width: parent.width - 30 anchors.bottom: parent.bottom anchors.bottomMargin: 20 model: ServerListLogic.serverListModel @@ -49,7 +49,7 @@ PageBase { clip: true delegate: Item { height: 60 - width: root.width - 40 + width: listWidget_servers.width - 15 MouseArea { id: ms anchors.fill: parent @@ -174,5 +174,9 @@ PageBase { } } } + + ScrollBar.vertical: ScrollBar { + policy: ScrollBar.AlwaysOn + } } } diff --git a/client/ui/qml/Pages/PageShareConnection.qml b/client/ui/qml/Pages/PageShareConnection.qml index 9bd84bdd..b643a7ee 100644 --- a/client/ui/qml/Pages/PageShareConnection.qml +++ b/client/ui/qml/Pages/PageShareConnection.qml @@ -34,6 +34,7 @@ PageBase { anchors.top: caption.bottom anchors.bottom: root.bottom contentHeight: col.height + boundsBehavior: Flickable.StopAtBounds Column { id: col From c6bb33fa84282085e0a7937e9fbe13b585366137 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Sat, 17 Dec 2022 21:00:48 +0300 Subject: [PATCH 38/43] added display of "pure" config if it was imported in native format - changed the method of passing the isThirdPartyConfig parameter to page logic - removed adding hostname to the server name when importing configs in native format - fixed display of the "$proto settings" button when clicking on the protocol on the "Installed services" page --- client/settings.cpp | 7 --- client/settings.h | 2 - .../ui/pages_logic/ServerContainersLogic.cpp | 3 +- client/ui/pages_logic/StartPageLogic.cpp | 12 ++-- client/ui/pages_logic/ViewConfigLogic.cpp | 58 ++++++++++++------- .../ui/pages_logic/protocols/CloakLogic.cpp | 2 +- client/ui/pages_logic/protocols/CloakLogic.h | 2 +- .../ui/pages_logic/protocols/OpenVpnLogic.cpp | 6 +- .../ui/pages_logic/protocols/OpenVpnLogic.h | 2 +- .../protocols/OtherProtocolsLogic.cpp | 2 +- .../protocols/OtherProtocolsLogic.h | 2 +- .../protocols/PageProtocolLogicBase.h | 2 +- .../protocols/ShadowSocksLogic.cpp | 2 +- .../pages_logic/protocols/ShadowSocksLogic.h | 2 +- .../pages_logic/protocols/WireGuardLogic.cpp | 4 +- .../ui/pages_logic/protocols/WireGuardLogic.h | 2 +- client/ui/qml/Pages/PageServerContainers.qml | 1 + 17 files changed, 58 insertions(+), 53 deletions(-) diff --git a/client/settings.cpp b/client/settings.cpp index fc3a5825..6e470a33 100644 --- a/client/settings.cpp +++ b/client/settings.cpp @@ -312,10 +312,3 @@ ServerCredentials Settings::serverCredentials(int index) const return credentials; } - -bool Settings::isThirdPartyConfig(int serverIndex) const -{ - if (serverIndex < 0) return false; - const QJsonObject &s = server(serverIndex); - return s.value(config_key::isThirdPartyConfig).toBool(); -} diff --git a/client/settings.h b/client/settings.h index 56b28c18..c78b9a79 100644 --- a/client/settings.h +++ b/client/settings.h @@ -113,8 +113,6 @@ public: QByteArray backupAppConfig() const { return m_settings.backupAppConfig(); } bool restoreAppConfig(const QByteArray &cfg) { return m_settings.restoreAppConfig(cfg); } - bool isThirdPartyConfig(int serverIndex) const; - private: SecureQSettings m_settings; diff --git a/client/ui/pages_logic/ServerContainersLogic.cpp b/client/ui/pages_logic/ServerContainersLogic.cpp index ba873eb2..27f865b5 100644 --- a/client/ui/pages_logic/ServerContainersLogic.cpp +++ b/client/ui/pages_logic/ServerContainersLogic.cpp @@ -38,8 +38,7 @@ void ServerContainersLogic::onPushButtonProtoSettingsClicked(DockerContainer c, uiLogic()->selectedDockerContainer = c; uiLogic()->protocolLogic(p)->updateProtocolPage(m_settings->protocolConfig(uiLogic()->selectedServerIndex, uiLogic()->selectedDockerContainer, p), uiLogic()->selectedDockerContainer, - m_settings->haveAuthData(uiLogic()->selectedServerIndex), - m_settings->isThirdPartyConfig(uiLogic()->selectedServerIndex)); + m_settings->haveAuthData(uiLogic()->selectedServerIndex)); emit uiLogic()->goToProtocolPage(p); } diff --git a/client/ui/pages_logic/StartPageLogic.cpp b/client/ui/pages_logic/StartPageLogic.cpp index 2a738fcf..09ebde9d 100644 --- a/client/ui/pages_logic/StartPageLogic.cpp +++ b/client/ui/pages_logic/StartPageLogic.cpp @@ -250,6 +250,7 @@ bool StartPageLogic::importConnectionFromOpenVpnConfig(const QString &config) QJsonObject lastConfig; lastConfig[config_key::last_config] = QString(QJsonDocument(openVpnConfig).toJson()); + lastConfig[config_key::isThirdPartyConfig] = true; QJsonObject containers; containers.insert(config_key::container, QJsonValue("amnezia-openvpn")); @@ -268,7 +269,7 @@ bool StartPageLogic::importConnectionFromOpenVpnConfig(const QString &config) QJsonObject o; o[config_key::containers] = arr; o[config_key::defaultContainer] = "amnezia-openvpn"; - o[config_key::description] = QString("OpenVpn server %1").arg(hostName); + o[config_key::description] = QString("OpenVpn server"); const static QRegularExpression dnsRegExp("dhcp-option DNS (\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b)"); @@ -282,8 +283,6 @@ bool StartPageLogic::importConnectionFromOpenVpnConfig(const QString &config) o[config_key::hostName] = hostName; - o[config_key::isThirdPartyConfig] = true; - return importConnection(o); } @@ -302,7 +301,8 @@ bool StartPageLogic::importConnectionFromWireguardConfig(const QString &config) } QJsonObject wireguardConfig; - wireguardConfig[config_key::last_config] = QString(QJsonDocument(lastConfig).toJson());; + wireguardConfig[config_key::last_config] = QString(QJsonDocument(lastConfig).toJson()); + wireguardConfig[config_key::isThirdPartyConfig] = true; wireguardConfig[config_key::port] = port; wireguardConfig[config_key::transport_proto] = "udp"; @@ -316,7 +316,7 @@ bool StartPageLogic::importConnectionFromWireguardConfig(const QString &config) QJsonObject o; o[config_key::containers] = arr; o[config_key::defaultContainer] = "amnezia-wireguard"; - o[config_key::description] = QString("Wireguard server %1").arg(hostName); + o[config_key::description] = QString("Wireguard server"); const static QRegularExpression dnsRegExp("DNS = (\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b).*(\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b)"); QRegularExpressionMatch dnsMatch = dnsRegExp.match(config); @@ -327,7 +327,5 @@ bool StartPageLogic::importConnectionFromWireguardConfig(const QString &config) o[config_key::hostName] = hostName; - o[config_key::isThirdPartyConfig] = true; - return importConnection(o); } diff --git a/client/ui/pages_logic/ViewConfigLogic.cpp b/client/ui/pages_logic/ViewConfigLogic.cpp index 1dfe9abe..9ccd9d3e 100644 --- a/client/ui/pages_logic/ViewConfigLogic.cpp +++ b/client/ui/pages_logic/ViewConfigLogic.cpp @@ -13,6 +13,8 @@ void ViewConfigLogic::onUpdatePage() { set_configText(QJsonDocument(configJson()).toJson()); + auto s = configJson()[config_key::isThirdPartyConfig].toBool(); + m_openVpnLastConfigs = m_openVpnMalStrings = "