commit
891f990e35
109 changed files with 3795 additions and 1891 deletions
255
.github/workflows/deploy.yml
vendored
Normal file
255
.github/workflows/deploy.yml
vendored
Normal file
|
@ -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
|
7
.gitignore
vendored
7
.gitignore
vendored
|
@ -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
|
||||
|
@ -119,3 +125,4 @@ captures/
|
|||
|
||||
# Android Profiling
|
||||
*.hprof
|
||||
client/3rd/ShadowSocks/ss_ios.xcconfig
|
||||
|
|
|
@ -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
|
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -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
|
||||
|
|
335
.travis.yml
335
.travis.yml
|
@ -1,335 +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 && \
|
||||
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 ; \
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
dist: focal
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- p7zip
|
||||
- python3
|
||||
- python3-pip
|
||||
- libgl-dev
|
||||
- mesa-common-dev
|
||||
- libpulse-dev
|
||||
|
||||
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
|
||||
|
||||
# ------------------------------------------------------
|
||||
- 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
|
||||
|
||||
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
|
||||
|
||||
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 ; \
|
||||
fi
|
||||
- |
|
||||
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 LOCAL_ANDROID_NDK_HOME="$ANDROID_NDK_HOME" &&
|
||||
export LOCAL_ANDROID_NDK_HOST_PLATFORM="linux-x86_64" &&
|
||||
export PATH=$PATH:${ANDROID_NDK_HOME} &&
|
||||
env
|
||||
|
||||
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
|
||||
|
||||
# ------------------------------------------------------
|
||||
- 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 && \
|
||||
pip install -U aqtinstall requests py7zr && \
|
||||
pip show aqtinstall && \
|
||||
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
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
# Amnezia VPN
|
||||
## _The best client for self-hosted VPN_
|
||||
|
||||
[](https://travis-ci.com/amnezia-vpn/desktop-client)
|
||||
[]
|
||||
|
||||
Amnezia is a VPN client with the key feature of deploying your own VPN server on you virtual server.
|
||||
|
||||
|
@ -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.
|
||||
|
|
|
@ -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
|
856
client/3rd/qrcodegen/qrcodegen.cpp
Normal file
856
client/3rd/qrcodegen/qrcodegen.cpp
Normal file
|
@ -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 <algorithm>
|
||||
#include <cassert>
|
||||
#include <climits>
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
#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<uint8_t> &data) {
|
||||
if (data.size() > static_cast<unsigned int>(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<int>(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<uint32_t>(accumData), 10);
|
||||
accumData = 0;
|
||||
accumCount = 0;
|
||||
}
|
||||
}
|
||||
if (accumCount > 0) // 1 or 2 digits remaining
|
||||
bb.appendBits(static_cast<uint32_t>(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<int>(temp - ALPHANUMERIC_CHARSET);
|
||||
accumCount++;
|
||||
if (accumCount == 2) {
|
||||
bb.appendBits(static_cast<uint32_t>(accumData), 11);
|
||||
accumData = 0;
|
||||
accumCount = 0;
|
||||
}
|
||||
}
|
||||
if (accumCount > 0) // 1 character remaining
|
||||
bb.appendBits(static_cast<uint32_t>(accumData), 6);
|
||||
return QrSegment(Mode::ALPHANUMERIC, charCount, std::move(bb));
|
||||
}
|
||||
|
||||
|
||||
vector<QrSegment> QrSegment::makeSegments(const char *text) {
|
||||
// Select the most efficient segment encoding automatically
|
||||
vector<QrSegment> 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<uint8_t> bytes;
|
||||
for (; *text != '\0'; text++)
|
||||
bytes.push_back(static_cast<uint8_t>(*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<uint32_t>(assignVal), 8);
|
||||
else if (assignVal < (1 << 14)) {
|
||||
bb.appendBits(2, 2);
|
||||
bb.appendBits(static_cast<uint32_t>(assignVal), 14);
|
||||
} else if (assignVal < 1000000L) {
|
||||
bb.appendBits(6, 3);
|
||||
bb.appendBits(static_cast<uint32_t>(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<bool> &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<bool> &&dt) :
|
||||
mode(&md),
|
||||
numChars(numCh),
|
||||
data(std::move(dt)) {
|
||||
if (numCh < 0)
|
||||
throw std::domain_error("Invalid value");
|
||||
}
|
||||
|
||||
|
||||
int QrSegment::getTotalBits(const vector<QrSegment> &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<unsigned int>(INT_MAX - result))
|
||||
return -1; // The sum will overflow an int type
|
||||
result += static_cast<int>(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<bool> &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<QrSegment> segs = QrSegment::makeSegments(text);
|
||||
return encodeSegments(segs, ecl);
|
||||
}
|
||||
|
||||
|
||||
QrCode QrCode::encodeBinary(const vector<uint8_t> &data, Ecc ecl) {
|
||||
vector<QrSegment> segs{QrSegment::makeBytes(data)};
|
||||
return encodeSegments(segs, ecl);
|
||||
}
|
||||
|
||||
|
||||
QrCode QrCode::encodeSegments(const vector<QrSegment> &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<uint32_t>(seg.getMode().getModeBits()), 4);
|
||||
bb.appendBits(static_cast<uint32_t>(seg.getNumChars()), seg.getMode().numCharCountBits(version));
|
||||
bb.insert(bb.end(), seg.getData().begin(), seg.getData().end());
|
||||
}
|
||||
assert(bb.size() == static_cast<unsigned int>(dataUsedBits));
|
||||
|
||||
// Add terminator and pad up to a byte if applicable
|
||||
size_t dataCapacityBits = static_cast<size_t>(getNumDataCodewords(version, ecl)) * 8;
|
||||
assert(bb.size() <= dataCapacityBits);
|
||||
bb.appendBits(0, std::min(4, static_cast<int>(dataCapacityBits - bb.size())));
|
||||
bb.appendBits(0, (8 - static_cast<int>(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<uint8_t> 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<uint8_t> &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_t>(size);
|
||||
modules = vector<vector<bool> >(sz, vector<bool>(sz)); // Initially all light
|
||||
isFunction = vector<vector<bool> >(sz, vector<bool>(sz));
|
||||
|
||||
// Compute ECC, draw modules
|
||||
drawFunctionPatterns();
|
||||
const vector<uint8_t> 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<int> 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<long>(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<size_t>(x);
|
||||
size_t uy = static_cast<size_t>(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<size_t>(y)).at(static_cast<size_t>(x));
|
||||
}
|
||||
|
||||
|
||||
vector<uint8_t> QrCode::addEccAndInterleave(const vector<uint8_t> &data) const {
|
||||
if (data.size() != static_cast<unsigned int>(getNumDataCodewords(version, errorCorrectionLevel)))
|
||||
throw std::invalid_argument("Invalid argument");
|
||||
|
||||
// Calculate parameter numbers
|
||||
int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[static_cast<int>(errorCorrectionLevel)][version];
|
||||
int blockEccLen = ECC_CODEWORDS_PER_BLOCK [static_cast<int>(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<vector<uint8_t> > blocks;
|
||||
const vector<uint8_t> rsDiv = reedSolomonComputeDivisor(blockEccLen);
|
||||
for (int i = 0, k = 0; i < numBlocks; i++) {
|
||||
vector<uint8_t> dat(data.cbegin() + k, data.cbegin() + (k + shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1)));
|
||||
k += static_cast<int>(dat.size());
|
||||
const vector<uint8_t> 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<uint8_t> 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<unsigned int>(shortBlockLen - blockEccLen) || j >= static_cast<unsigned int>(numShortBlocks))
|
||||
result.push_back(blocks.at(j).at(i));
|
||||
}
|
||||
}
|
||||
assert(result.size() == static_cast<unsigned int>(rawCodewords));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void QrCode::drawCodewords(const vector<uint8_t> &data) {
|
||||
if (data.size() != static_cast<unsigned int>(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<size_t>(right - j); // Actual x coordinate
|
||||
bool upward = ((right + 1) & 2) == 0;
|
||||
size_t y = static_cast<size_t>(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<int>(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_t>(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<int,7> 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<int,7> 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<bool> &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<int>((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<int> QrCode::getAlignmentPatternPositions() const {
|
||||
if (version == 1)
|
||||
return vector<int>();
|
||||
else {
|
||||
int numAlign = version / 7 + 2;
|
||||
int step = (version == 32) ? 26 :
|
||||
(version * 4 + numAlign * 2 + 1) / (numAlign * 2 - 2) * 2;
|
||||
vector<int> 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<int>(ecl)][ver]
|
||||
* NUM_ERROR_CORRECTION_BLOCKS[static_cast<int>(ecl)][ver];
|
||||
}
|
||||
|
||||
|
||||
vector<uint8_t> 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<uint8_t> result(static_cast<size_t>(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<uint8_t> QrCode::reedSolomonComputeRemainder(const vector<uint8_t> &data, const vector<uint8_t> &divisor) {
|
||||
vector<uint8_t> 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<uint8_t>(z);
|
||||
}
|
||||
|
||||
|
||||
int QrCode::finderPenaltyCountPatterns(const std::array<int,7> &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<int,7> &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<int,7> &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<bool>() {}
|
||||
|
||||
|
||||
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 << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
|
||||
sb << "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n";
|
||||
sb << "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 ";
|
||||
sb << (qr.getSize() + border * 2) << " " << (qr.getSize() + border * 2) << "\" stroke=\"none\">\n";
|
||||
sb << "\t<rect width=\"100%\" height=\"100%\" fill=\"#FFFFFF\"/>\n";
|
||||
sb << "\t<path d=\"";
|
||||
for (int y = 0; y < qr.getSize(); y++) {
|
||||
for (int x = 0; x < qr.getSize(); x++) {
|
||||
if (qr.getModule(x, y)) {
|
||||
if (x != 0 || y != 0)
|
||||
sb << " ";
|
||||
sb << "M" << (x + border) << "," << (y + border) << "h1v1h-1z";
|
||||
}
|
||||
}
|
||||
}
|
||||
sb << "\" fill=\"#000000\"/>\n";
|
||||
sb << "</svg>\n";
|
||||
return sb.str();
|
||||
}
|
||||
}
|
551
client/3rd/qrcodegen/qrcodegen.hpp
Normal file
551
client/3rd/qrcodegen/qrcodegen.hpp
Normal file
|
@ -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 <array>
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
||||
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<std::uint8_t> &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<QrSegment> 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<bool> 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<bool> &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<bool> &&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<bool> &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<QrSegment> &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<std::uint8_t> &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<QrSegment> &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<std::vector<bool> > modules;
|
||||
|
||||
// Indicates function modules that are not subjected to masking. Discarded when constructor finishes.
|
||||
private: std::vector<std::vector<bool> > 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<std::uint8_t> &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<std::uint8_t> addEccAndInterleave(const std::vector<std::uint8_t> &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<std::uint8_t> &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<int> 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<std::uint8_t> reedSolomonComputeDivisor(int degree);
|
||||
|
||||
|
||||
// Returns the Reed-Solomon error correction codeword for the given data and divisor polynomials.
|
||||
private: static std::vector<std::uint8_t> reedSolomonComputeRemainder(const std::vector<std::uint8_t> &data, const std::vector<std::uint8_t> &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<int,7> &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<int,7> &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<int,7> &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<bool> {
|
||||
|
||||
/*---- 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);
|
||||
|
||||
}
|
5
client/3rd/qrcodegen/qrcodegen.pri
Normal file
5
client/3rd/qrcodegen/qrcodegen.pri
Normal file
|
@ -0,0 +1,5 @@
|
|||
INCLUDEPATH += $$PWD
|
||||
DEPENDPATH += $$PWD
|
||||
|
||||
HEADERS += $$PWD/qrcodegen.hpp
|
||||
SOURCES += $$PWD/qrcodegen.cpp
|
|
@ -1 +0,0 @@
|
|||
Subproject commit 2fd4dd60c04a29c6d1271fdd9ae25378b8f61ec8
|
|
@ -1,12 +1,11 @@
|
|||
#include "amnezia_application.h"
|
||||
|
||||
#include <QFontDatabase>
|
||||
#include <QStandardPaths>
|
||||
#include <QTimer>
|
||||
#include <QTranslator>
|
||||
|
||||
|
||||
#include "QZXing.h"
|
||||
|
||||
#include "core/servercontroller.h"
|
||||
#include "debug.h"
|
||||
#include "defines.h"
|
||||
|
@ -49,6 +48,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<Settings>(new Settings);
|
||||
m_serverController = std::shared_ptr<ServerController>(new ServerController(m_settings, this));
|
||||
m_configurator = std::shared_ptr<VpnConfigurator>(new VpnConfigurator(m_settings, m_serverController, this));
|
||||
|
@ -122,8 +139,6 @@ void AmneziaApplication::init()
|
|||
|
||||
void AmneziaApplication::registerTypes()
|
||||
{
|
||||
QZXing::registerQMLTypes();
|
||||
|
||||
qRegisterMetaType<VpnProtocol::VpnConnectionState>("VpnProtocol::VpnConnectionState");
|
||||
qRegisterMetaType<ServerCredentials>("ServerCredentials");
|
||||
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
<?xml version="1.0"?>
|
||||
<manifest package="org.amnezia.vpn" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="-- %%INSERT_VERSION_NAME%% --" android:versionCode="-- %%INSERT_VERSION_CODE%% --" android:installLocation="auto">
|
||||
<manifest
|
||||
package="org.amnezia.vpn"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:versionName="-- %%INSERT_VERSION_NAME%% --"
|
||||
android:versionCode="-- %%INSERT_VERSION_CODE%% --"
|
||||
android:installLocation="auto">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
|
@ -18,12 +23,72 @@
|
|||
|
||||
<supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
|
||||
|
||||
<application android:name=".qt.AmneziaApp" android:hardwareAccelerated="true" android:label="-- %%INSERT_APP_NAME%% --" android:extractNativeLibs="true" android:icon="@drawable/icon">
|
||||
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density" android:name=".qt.VPNActivity" android:label="-- %%INSERT_APP_NAME%% --" android:screenOrientation="unspecified" android:launchMode="singleTop" android:theme="@style/splashScreenTheme">
|
||||
<application
|
||||
android:name=".qt.AmneziaApp"
|
||||
android:hardwareAccelerated="true"
|
||||
android:label="-- %%INSERT_APP_NAME%% --"
|
||||
android:extractNativeLibs="true"
|
||||
android:icon="@drawable/icon">
|
||||
|
||||
<activity
|
||||
android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density"
|
||||
android:name=".qt.VPNActivity"
|
||||
android:label="-- %%INSERT_APP_NAME%% --"
|
||||
android:screenOrientation="unspecified"
|
||||
android:launchMode="singleInstance"
|
||||
android:taskAffinity=""
|
||||
android:theme="@style/splashScreenTheme">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter android:label="AmneziaVPN">
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:scheme="file"/>
|
||||
<data android:scheme="content"/>
|
||||
<data android:mimeType= "*/*"/>
|
||||
<data android:host="*"/>
|
||||
<data android:pathPattern=".*\\.vpn"/>
|
||||
<data android:pathPattern=".*\\..*\\.vpn"/>
|
||||
<data android:pathPattern=".*\\..*\\..*\\.vpn"/>
|
||||
<data android:pathPattern=".*\\..*\\..*\\..*\\.vpn"/>
|
||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.vpn"/>
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter android:label="AmneziaVPN">
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:scheme="file"/>
|
||||
<data android:scheme="content"/>
|
||||
<data android:mimeType= "*/*"/>
|
||||
<data android:host="*"/>
|
||||
<data android:pathPattern=".*\\.cfg"/>
|
||||
<data android:pathPattern=".*\\..*\\.cfg"/>
|
||||
<data android:pathPattern=".*\\..*\\..*\\.cfg"/>
|
||||
<data android:pathPattern=".*\\..*\\..*\\..*\\.cfg"/>
|
||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.cfg"/>
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter android:label="AmneziaVPN">
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:scheme="file"/>
|
||||
<data android:scheme="content"/>
|
||||
<data android:mimeType= "*/*"/>
|
||||
<data android:host="*"/>
|
||||
<data android:pathPattern=".*\\.conf"/>
|
||||
<data android:pathPattern=".*\\..*\\.conf"/>
|
||||
<data android:pathPattern=".*\\..*\\..*\\.conf"/>
|
||||
<data android:pathPattern=".*\\..*\\..*\\..*\\.conf"/>
|
||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.conf"/>
|
||||
</intent-filter>
|
||||
|
||||
<!-- Application arguments -->
|
||||
<!-- meta-data android:name="android.app.arguments" android:value="arg1 arg2 arg3"/ -->
|
||||
<!-- Application arguments -->
|
||||
|
@ -79,10 +144,17 @@
|
|||
<!-- extract android style -->
|
||||
<meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/splashscreen"/>
|
||||
</activity>
|
||||
<service android:name=".VPNService" android:permission="android.permission.BIND_VPN_SERVICE" android:process=":QtOnlyProcess">
|
||||
|
||||
<service
|
||||
android:name=".VPNService"
|
||||
android:permission="android.permission.BIND_VPN_SERVICE"
|
||||
android:process=":QtOnlyProcess"
|
||||
android:exported="true">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.net.VpnService"/>
|
||||
</intent-filter>
|
||||
|
||||
<meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
|
||||
<meta-data android:name="android.app.repository" android:value="default"/>
|
||||
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
|
||||
|
@ -95,10 +167,24 @@
|
|||
<meta-data android:name="android.app.static_init_classes" android:value="-- %%INSERT_INIT_CLASSES%% --"/>
|
||||
<meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
|
||||
</service>
|
||||
<service android:name="org.amnezia.vpn.qt.VPNPermissionHelper" android:permission="android.permission.BIND_VPN_SERVICE">
|
||||
|
||||
<service
|
||||
android:name=".qt.VPNPermissionHelper"
|
||||
android:permission="android.permission.BIND_VPN_SERVICE"
|
||||
android:exported="true">
|
||||
|
||||
<meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
|
||||
</service>
|
||||
<!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices -->
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="org.amnezia.vpn.fileprovider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
|
||||
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/fileprovider"/>
|
||||
</provider>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
|
4
client/android/res/xml/fileprovider.xml
Normal file
4
client/android/res/xml/fileprovider.xml
Normal file
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<paths>
|
||||
<cache-path name="cache" path="/" />
|
||||
</paths>
|
24
client/android/src/org/amnezia/vpn/AuthHelper.java
Normal file
24
client/android/src/org/amnezia/vpn/AuthHelper.java
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
5
client/android/src/org/amnezia/vpn/IPCContract.kt
Normal file
5
client/android/src/org/amnezia/vpn/IPCContract.kt
Normal file
|
@ -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"
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
196
client/android/src/org/amnezia/vpn/qt/VPNActivity.kt
Normal file
196
client/android/src/org/amnezia/vpn/qt/VPNActivity.kt
Normal file
|
@ -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<String?>, 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)
|
||||
}
|
||||
}
|
|
@ -3,18 +3,20 @@ QT += widgets core gui network xml remoteobjects quick svg
|
|||
TARGET = AmneziaVPN
|
||||
TEMPLATE = app
|
||||
|
||||
CONFIG += qtquickcompiler
|
||||
CONFIG += qzxing_multimedia \
|
||||
enable_decoder_qr_code \
|
||||
enable_encoder_qr_code
|
||||
# silent builds on CI env
|
||||
IS_CI=$$(CI)
|
||||
!isEmpty(IS_CI){
|
||||
message("Detected CI env")
|
||||
CONFIG += silent ccache
|
||||
}
|
||||
|
||||
DEFINES += QT_DEPRECATED_WARNINGS
|
||||
CONFIG += qtquickcompiler
|
||||
|
||||
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")
|
||||
|
||||
|
@ -72,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 \
|
||||
|
@ -134,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 \
|
||||
|
@ -153,8 +157,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 \
|
||||
|
@ -242,13 +246,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
|
||||
|
@ -263,9 +265,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
|
||||
|
@ -301,6 +313,7 @@ ios {
|
|||
LIBS += -framework Foundation
|
||||
LIBS += -framework StoreKit
|
||||
LIBS += -framework UserNotifications
|
||||
LIBS += -framework AVFoundation
|
||||
|
||||
DEFINES += MVPN_IOS
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) }});
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,58 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<testsuites>
|
||||
<testsuite name="fastlane.lanes">
|
||||
|
||||
|
||||
|
||||
|
||||
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.000215">
|
||||
|
||||
</testcase>
|
||||
|
||||
|
||||
<testcase classname="fastlane.lanes" name="1: Switch to certificates lane" time="0.000162">
|
||||
|
||||
</testcase>
|
||||
|
||||
|
||||
<testcase classname="fastlane.lanes" name="2: match" time="2.707099">
|
||||
|
||||
</testcase>
|
||||
|
||||
|
||||
<testcase classname="fastlane.lanes" name="3: notification" time="0.226435">
|
||||
|
||||
</testcase>
|
||||
|
||||
|
||||
<testcase classname="fastlane.lanes" name="4: clean_build_artifacts" time="0.000648">
|
||||
|
||||
</testcase>
|
||||
|
||||
|
||||
<testcase classname="fastlane.lanes" name="5: build_app" time="259.546685">
|
||||
|
||||
</testcase>
|
||||
|
||||
|
||||
<testcase classname="fastlane.lanes" name="6: testflight" time="84.84052">
|
||||
|
||||
</testcase>
|
||||
|
||||
|
||||
<testcase classname="fastlane.lanes" name="7: get_version_number" time="0.114899">
|
||||
|
||||
</testcase>
|
||||
|
||||
|
||||
<testcase classname="fastlane.lanes" name="8: get_build_number" time="1.332216">
|
||||
|
||||
</testcase>
|
||||
|
||||
|
||||
<testcase classname="fastlane.lanes" name="9: increment_build_number" time="2.325473">
|
||||
|
||||
</testcase>
|
||||
|
||||
</testsuite>
|
||||
</testsuites>
|
|
@ -38,13 +38,15 @@
|
|||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(MARKETING_VERSION)</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>2</string>
|
||||
<string>7</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UIFileSharingEnabled</key>
|
||||
<true/>
|
||||
<key>LSSupportsOpeningDocumentsInPlace</key>
|
||||
<false/>
|
||||
<true/>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>Amnezia VPN needs access to the camera for reading QR-codes.</string>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(MARKETING_VERSION)</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>2</string>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -4,11 +4,13 @@
|
|||
#include <QAndroidBinder>
|
||||
#include <QAndroidServiceConnection>
|
||||
|
||||
#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<void(const QString&)>&& 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<void(const QString&)> m_logCallback;
|
||||
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
#include "native.h"
|
||||
#include <QMetaObject>
|
||||
#if defined(Q_OS_ANDROID)
|
||||
#include <jni.h>
|
||||
#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<void**>(&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
|
|
@ -1,20 +0,0 @@
|
|||
#ifndef NATIVE_H
|
||||
#define NATIVE_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
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
|
|
@ -2,7 +2,9 @@
|
|||
|
||||
#include <QFile>
|
||||
|
||||
@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
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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<ifaddrs>? = nil
|
||||
var temp_addr: UnsafeMutablePointer<ifaddrs>? = nil
|
||||
var success: Int = 0
|
||||
func getWiFiAddress() -> String? {
|
||||
var address : String?
|
||||
|
||||
// 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
|
||||
// Get list of all interfaces on the local machine:
|
||||
var ifaddr : UnsafeMutablePointer<ifaddrs>?
|
||||
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
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#include <windows.h>
|
||||
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"
|
|
@ -53,8 +53,17 @@ 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";
|
||||
|
||||
constexpr char isThirdPartyConfig[] = "isThirdPartyConfig";
|
||||
|
||||
constexpr char openvpn[] = "openvpn";
|
||||
constexpr char wireguard[] = "wireguard";
|
||||
|
||||
}
|
||||
|
||||
namespace protocols {
|
||||
|
@ -82,6 +91,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 {
|
||||
|
|
|
@ -63,6 +63,7 @@
|
|||
<file>server_scripts/website_tor/run_container.sh</file>
|
||||
<file>ui/qml/main.qml</file>
|
||||
<file>ui/qml/TitleBar.qml</file>
|
||||
<file>ui/qml/Pages/PageBase.qml</file>
|
||||
<file>ui/qml/Pages/PageAppSetting.qml</file>
|
||||
<file>ui/qml/Pages/PageGeneralSettings.qml</file>
|
||||
<file>ui/qml/Pages/PageNetworkSetting.qml</file>
|
||||
|
@ -81,6 +82,28 @@
|
|||
<file>ui/qml/Pages/PageSites.qml</file>
|
||||
<file>ui/qml/Pages/PageStart.qml</file>
|
||||
<file>ui/qml/Pages/PageVPN.qml</file>
|
||||
<file>ui/qml/Pages/PageQrDecoder.qml</file>
|
||||
<file>ui/qml/Pages/PageAbout.qml</file>
|
||||
<file>ui/qml/Pages/PageQrDecoderIos.qml</file>
|
||||
<file>ui/qml/Pages/PageViewConfig.qml</file>
|
||||
<file>ui/qml/Pages/Protocols/PageProtoCloak.qml</file>
|
||||
<file>ui/qml/Pages/Protocols/PageProtoOpenVPN.qml</file>
|
||||
<file>ui/qml/Pages/Protocols/PageProtoShadowSocks.qml</file>
|
||||
<file>ui/qml/Pages/Protocols/PageProtoSftp.qml</file>
|
||||
<file>ui/qml/Pages/Protocols/PageProtoTorWebSite.qml</file>
|
||||
<file>ui/qml/Pages/Protocols/PageProtocolBase.qml</file>
|
||||
<file>ui/qml/Pages/Protocols/PageProtoWireGuard.qml</file>
|
||||
<file>ui/qml/Pages/InstallSettings/InstallSettingsBase.qml</file>
|
||||
<file>ui/qml/Pages/InstallSettings/SelectContainer.qml</file>
|
||||
<file>ui/qml/Pages/Share/PageShareProtoCloak.qml</file>
|
||||
<file>ui/qml/Pages/Share/PageShareProtocolBase.qml</file>
|
||||
<file>ui/qml/Pages/Share/PageShareProtoOpenVPN.qml</file>
|
||||
<file>ui/qml/Pages/Share/PageShareProtoSftp.qml</file>
|
||||
<file>ui/qml/Pages/Share/PageShareProtoShadowSocks.qml</file>
|
||||
<file>ui/qml/Pages/Share/PageShareProtoTorWebSite.qml</file>
|
||||
<file>ui/qml/Pages/Share/PageShareProtoAmnezia.qml</file>
|
||||
<file>ui/qml/Pages/Share/PageShareProtoWireGuard.qml</file>
|
||||
<file>ui/qml/Pages/Share/PageShareProtoIkev2.qml</file>
|
||||
<file>ui/qml/Controls/BasicButtonType.qml</file>
|
||||
<file>ui/qml/Controls/BlueButtonType.qml</file>
|
||||
<file>ui/qml/Controls/CheckBoxType.qml</file>
|
||||
|
@ -92,57 +115,40 @@
|
|||
<file>ui/qml/Controls/ShareConnectionButtonType.qml</file>
|
||||
<file>ui/qml/Controls/ShareConnectionContent.qml</file>
|
||||
<file>ui/qml/Controls/TextFieldType.qml</file>
|
||||
<file>ui/qml/Pages/PageBase.qml</file>
|
||||
<file>ui/qml/Config/GlobalConfig.qml</file>
|
||||
<file>ui/qml/Config/qmldir</file>
|
||||
<file>images/background_connected.png</file>
|
||||
<file>images/background_connected@2x.png</file>
|
||||
<file>ui/qml/Pages/Protocols/PageProtoCloak.qml</file>
|
||||
<file>ui/qml/Pages/Protocols/PageProtoOpenVPN.qml</file>
|
||||
<file>ui/qml/Pages/Protocols/PageProtoShadowSocks.qml</file>
|
||||
<file>ui/qml/Controls/BackButton.qml</file>
|
||||
<file>ui/qml/Pages/InstallSettings/InstallSettingsBase.qml</file>
|
||||
<file>ui/qml/Controls/Caption.qml</file>
|
||||
<file>ui/qml/Controls/Logo.qml</file>
|
||||
<file>ui/qml/Pages/InstallSettings/SelectContainer.qml</file>
|
||||
<file>ui/qml/Pages/Protocols/PageProtocolBase.qml</file>
|
||||
<file>images/delete.png</file>
|
||||
<file>ui/qml/Controls/RichLabelType.qml</file>
|
||||
<file>ui/qml/Controls/SvgImageType.qml</file>
|
||||
<file>ui/qml/Controls/FlickableType.qml</file>
|
||||
<file>ui/qml/Controls/UrlButtonType.qml</file>
|
||||
<file>ui/qml/Controls/TextAreaType.qml</file>
|
||||
<file>ui/qml/Controls/ContextMenu.qml</file>
|
||||
<file>ui/qml/Controls/FadeBehavior.qml</file>
|
||||
<file>ui/qml/Controls/VisibleBehavior.qml</file>
|
||||
<file>ui/qml/Controls/Caption.qml</file>
|
||||
<file>ui/qml/Controls/Logo.qml</file>
|
||||
<file>ui/qml/Controls/BackButton.qml</file>
|
||||
<file>ui/qml/Controls/ShareConnectionButtonCopyType.qml</file>
|
||||
<file>ui/qml/Controls/SvgButtonType.qml</file>
|
||||
<file>ui/qml/Config/GlobalConfig.qml</file>
|
||||
<file>ui/qml/Config/qmldir</file>
|
||||
<file>server_scripts/dns/configure_container.sh</file>
|
||||
<file>server_scripts/dns/Dockerfile</file>
|
||||
<file>server_scripts/dns/run_container.sh</file>
|
||||
<file>server_scripts/sftp/configure_container.sh</file>
|
||||
<file>server_scripts/sftp/Dockerfile</file>
|
||||
<file>server_scripts/sftp/run_container.sh</file>
|
||||
<file>ui/qml/Pages/Protocols/PageProtoSftp.qml</file>
|
||||
<file>ui/qml/Pages/Protocols/PageProtoTorWebSite.qml</file>
|
||||
<file>server_scripts/ipsec/configure_container.sh</file>
|
||||
<file>server_scripts/ipsec/Dockerfile</file>
|
||||
<file>server_scripts/ipsec/run_container.sh</file>
|
||||
<file>server_scripts/ipsec/start.sh</file>
|
||||
<file>ui/qml/Pages/Share/PageShareProtoCloak.qml</file>
|
||||
<file>ui/qml/Pages/Share/PageShareProtocolBase.qml</file>
|
||||
<file>ui/qml/Pages/Share/PageShareProtoOpenVPN.qml</file>
|
||||
<file>ui/qml/Pages/Share/PageShareProtoSftp.qml</file>
|
||||
<file>ui/qml/Pages/Share/PageShareProtoShadowSocks.qml</file>
|
||||
<file>ui/qml/Pages/Share/PageShareProtoTorWebSite.qml</file>
|
||||
<file>ui/qml/Controls/TextAreaType.qml</file>
|
||||
<file>ui/qml/Controls/ContextMenu.qml</file>
|
||||
<file>ui/qml/Pages/Share/PageShareProtoAmnezia.qml</file>
|
||||
<file>ui/qml/Controls/ShareConnectionButtonCopyType.qml</file>
|
||||
<file>ui/qml/Pages/Share/PageShareProtoWireGuard.qml</file>
|
||||
<file>server_scripts/ipsec/mobileconfig.plist</file>
|
||||
<file>ui/qml/Pages/Share/PageShareProtoIkev2.qml</file>
|
||||
<file>server_scripts/ipsec/strongswan.profile</file>
|
||||
<file>images/background_connected.png</file>
|
||||
<file>images/background_connected@2x.png</file>
|
||||
<file>images/delete.png</file>
|
||||
<file>images/animation.gif</file>
|
||||
<file>images/connected.png</file>
|
||||
<file>images/disconnected.png</file>
|
||||
<file>ui/qml/Pages/PageQrDecoder.qml</file>
|
||||
<file>ui/qml/Pages/PageAbout.qml</file>
|
||||
<file>ui/qml/Controls/RichLabelType.qml</file>
|
||||
<file>images/svg/gpp_good_black_24dp.svg</file>
|
||||
<file>ui/qml/Controls/SvgImageType.qml</file>
|
||||
<file>images/svg/gpp_maybe_black_24dp.svg</file>
|
||||
<file>images/svg/close_black_24dp.svg</file>
|
||||
<file>images/svg/delete_black_24dp.svg</file>
|
||||
|
@ -156,9 +162,6 @@
|
|||
<file>images/svg/vpn_key_black_24dp.svg</file>
|
||||
<file>images/svg/control_point_black_24dp.svg</file>
|
||||
<file>images/svg/settings_suggest_black_24dp.svg</file>
|
||||
<file>ui/qml/Controls/SvgButtonType.qml</file>
|
||||
<file>ui/qml/Pages/PageQrDecoderIos.qml</file>
|
||||
<file>server_scripts/website_tor/Dockerfile</file>
|
||||
<file>ui/qml/Pages/PageViewConfig.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
|
|
@ -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 <macos|ios|> [-d|--debug] [-n|--networkextension] [-a|--adjusttoken <adjust_token>]"
|
||||
print N "\t$0 <macos|ios|> [-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=</path/of/the/qt/bin/folder/for/macos>"
|
||||
print N "\tQT_IOS_BIN=</path/of/the/qt/bin/folder/for/ios>"
|
||||
print N "\tMVPN_IOS_ADJUST_TOKEN=<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 '' '/<string>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
|
||||
|
|
|
@ -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('3rd/OpenVPNAdapter/build/Release-iphoneos/OpenVPNAdapter.framework')
|
||||
build_file = embed_frameworks_build_phase.add_file_reference(framework_ref)
|
||||
|
||||
framework_ref = frameworks_group.new_file('iAd.framework')
|
||||
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')
|
||||
}
|
||||
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])
|
||||
|
@ -403,39 +306,9 @@ 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
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <QEventLoop>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QSharedPointer>
|
||||
#include <QTimer>
|
||||
#include "utils.h"
|
||||
#include <QRandomGenerator>
|
||||
|
@ -162,7 +163,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;
|
||||
}
|
||||
|
||||
|
@ -219,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<ReadPasswordJob>(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<WritePasswordJob>(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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,4 +23,5 @@ verb 1
|
|||
tls-server
|
||||
tls-version-min 1.2
|
||||
$OPENVPN_TLS_AUTH
|
||||
$OPENVPN_ADDITIONAL_SERVER_CONFIG
|
||||
EOF
|
||||
|
|
|
@ -21,6 +21,8 @@ block-outside-dns
|
|||
|
||||
remote $REMOTE_HOST $OPENVPN_PORT
|
||||
|
||||
$OPENVPN_ADDITIONAL_CLIENT_CONFIG
|
||||
|
||||
<ca>
|
||||
$OPENVPN_CA_CERT
|
||||
</ca>
|
||||
|
|
|
@ -23,6 +23,7 @@ verb 1
|
|||
tls-server
|
||||
tls-version-min 1.2
|
||||
$OPENVPN_TLS_AUTH
|
||||
$OPENVPN_ADDITIONAL_SERVER_CONFIG
|
||||
EOF
|
||||
|
||||
# Cloak config
|
||||
|
|
|
@ -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
|
||||
|
||||
<ca>
|
||||
$OPENVPN_CA_CERT
|
||||
</ca>
|
||||
|
|
|
@ -23,6 +23,7 @@ verb 1
|
|||
tls-server
|
||||
tls-version-min 1.2
|
||||
$OPENVPN_TLS_AUTH
|
||||
$OPENVPN_ADDITIONAL_SERVER_CONFIG
|
||||
EOF
|
||||
|
||||
# ShadowSocks config
|
||||
|
|
|
@ -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
|
||||
|
||||
<ca>
|
||||
$OPENVPN_CA_CERT
|
||||
</ca>
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ void ServerContainersLogic::onUpdatePage()
|
|||
ProtocolsModel *p_model = qobject_cast<ProtocolsModel *>(uiLogic()->protocolsModel());
|
||||
p_model->setSelectedServerIndex(uiLogic()->selectedServerIndex);
|
||||
|
||||
set_isManagedServer(m_settings->haveAuthData(uiLogic()->selectedServerIndex));
|
||||
emit updatePage();
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,8 @@ public:
|
|||
Q_INVOKABLE void onPushButtonRemoveClicked(DockerContainer c);
|
||||
Q_INVOKABLE void onPushButtonContinueClicked(DockerContainer c, int port, TransportProto tp);
|
||||
|
||||
AUTO_PROPERTY(bool, isManagedServer)
|
||||
|
||||
public:
|
||||
explicit ServerContainersLogic(UiLogic *uiLogic, QObject *parent = nullptr);
|
||||
~ServerContainersLogic() = default;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -10,6 +10,12 @@
|
|||
#include <core/servercontroller.h>
|
||||
#include <QTimer>
|
||||
|
||||
#if defined(Q_OS_ANDROID)
|
||||
#include <QAndroidJniObject>
|
||||
#include <QAndroidJniEnvironment>
|
||||
#include <QtAndroid>
|
||||
#endif
|
||||
|
||||
ServerSettingsLogic::ServerSettingsLogic(UiLogic *logic, QObject *parent):
|
||||
PageLogicBase(logic, parent),
|
||||
m_labelWaitInfoVisible{true},
|
||||
|
@ -31,11 +37,17 @@ void ServerSettingsLogic::onUpdatePage()
|
|||
set_pushButtonShareFullVisible(m_settings->haveAuthData(uiLogic()->selectedServerIndex));
|
||||
const QJsonObject &server = m_settings->server(uiLogic()->selectedServerIndex);
|
||||
const QString &port = server.value(config_key::port).toString();
|
||||
set_labelServerText(QString("%1@%2%3%4")
|
||||
.arg(server.value(config_key::userName).toString())
|
||||
.arg(server.value(config_key::hostName).toString())
|
||||
.arg(port.isEmpty() ? "" : ":")
|
||||
.arg(port));
|
||||
|
||||
const QString &userName = server.value(config_key::userName).toString();
|
||||
const QString &hostName = server.value(config_key::hostName).toString();
|
||||
QString name = QString("%1%2%3%4%5")
|
||||
.arg(userName)
|
||||
.arg(userName.isEmpty() ? "" : "@")
|
||||
.arg(hostName)
|
||||
.arg(port.isEmpty() ? "" : ":")
|
||||
.arg(port);
|
||||
|
||||
set_labelServerText(name);
|
||||
set_lineEditDescriptionText(server.value(config_key::description).toString());
|
||||
|
||||
DockerContainer selectedContainer = m_settings->defaultContainer(uiLogic()->selectedServerIndex);
|
||||
|
@ -126,8 +138,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<ShareConnectionLogic>()->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<jobject>() != nullptr) {
|
||||
QtAndroid::startActivity(intent.object<jobject>(), 1, receiver);
|
||||
}
|
||||
} else {
|
||||
uiLogic()->pageLogic<ShareConnectionLogic>()->updateSharingPage(uiLogic()->selectedServerIndex, DockerContainer::None);
|
||||
emit uiLogic()->goToShareProtocolPage(Proto::Any);
|
||||
}
|
||||
}
|
||||
#else
|
||||
uiLogic()->pageLogic<ShareConnectionLogic>()->updateSharingPage(uiLogic()->selectedServerIndex, DockerContainer::None);
|
||||
emit uiLogic()->goToShareProtocolPage(Proto::Any);
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -3,6 +3,10 @@
|
|||
|
||||
#include "PageLogicBase.h"
|
||||
|
||||
#if defined(Q_OS_ANDROID)
|
||||
#include <QAndroidActivityResultReceiver>
|
||||
#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
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
#include <QBuffer>
|
||||
#include <QImage>
|
||||
#include <QDataStream>
|
||||
//#include <QZXing>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include "QZXing.h"
|
||||
#include "QZXingImageProvider.h"
|
||||
#include "QZXingFilter.h"
|
||||
#include "qrcodegen.hpp"
|
||||
|
||||
#include "ShareConnectionLogic.h"
|
||||
|
||||
|
@ -30,6 +27,8 @@
|
|||
#include <math.h>
|
||||
#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<QString> ShareConnectionLogic::genQrCodeImageSeries(const QByteArray &data)
|
||||
{
|
||||
double k = 1500;
|
||||
double k = 850;
|
||||
|
||||
quint8 chunksCount = std::ceil(data.size() / k);
|
||||
QList<QString> chunks;
|
||||
|
@ -278,18 +280,15 @@ QList<QString> 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());
|
||||
}
|
||||
|
|
|
@ -49,8 +49,7 @@ public:
|
|||
void updateSharingPage(int serverIndex, DockerContainer container);
|
||||
QList<QString> genQrCodeImageSeries(const QByteArray &data);
|
||||
|
||||
QString imageToBase64(const QImage &image);
|
||||
|
||||
QString svgToBase64(const QString &image);
|
||||
|
||||
};
|
||||
#endif // SHARE_CONNECTION_LOGIC_H
|
||||
|
|
|
@ -11,9 +11,40 @@
|
|||
#include <QStandardPaths>
|
||||
|
||||
#ifdef Q_OS_ANDROID
|
||||
#include <QtAndroid>
|
||||
#include "platforms/android/android_controller.h"
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
enum class ConfigTypes {
|
||||
Amnezia,
|
||||
OpenVpn,
|
||||
WireGuard
|
||||
};
|
||||
|
||||
ConfigTypes 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";
|
||||
|
||||
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 ConfigTypes::OpenVpn;
|
||||
} else if (config.contains(wireguardConfigPatternSectionInterface) &&
|
||||
config.contains(wireguardConfigPatternSectionPeer))
|
||||
return ConfigTypes::WireGuard;
|
||||
return ConfigTypes::Amnezia;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
StartPageLogic::StartPageLogic(UiLogic *logic, QObject *parent):
|
||||
PageLogicBase(logic, parent),
|
||||
m_pushButtonConnectEnabled{true},
|
||||
|
@ -23,7 +54,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<void>("addFlags", "(I)V", FLAG_SECURE);
|
||||
}
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
void StartPageLogic::onUpdatePage()
|
||||
|
@ -125,16 +165,22 @@ 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 config file"),
|
||||
QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), "*.vpn *.ovpn *.conf");
|
||||
if (fileName.isEmpty()) return;
|
||||
|
||||
QFile file(fileName);
|
||||
file.open(QIODevice::ReadOnly);
|
||||
QByteArray data = file.readAll();
|
||||
|
||||
importConnectionFromCode(QString(data));
|
||||
auto configFormat = checkConfigFormat(QString(data));
|
||||
if (configFormat == ConfigTypes::OpenVpn) {
|
||||
importConnectionFromOpenVpnConfig(QString(data));
|
||||
} else if (configFormat == ConfigTypes::WireGuard) {
|
||||
importConnectionFromWireguardConfig(QString(data));
|
||||
} else {
|
||||
importConnectionFromCode(QString(data));
|
||||
}
|
||||
}
|
||||
|
||||
bool StartPageLogic::importConnection(const QJsonObject &profile)
|
||||
|
@ -156,14 +202,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;
|
||||
}
|
||||
|
||||
|
@ -204,3 +242,90 @@ 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());
|
||||
lastConfig[config_key::isThirdPartyConfig] = true;
|
||||
|
||||
QJsonObject containers;
|
||||
containers.insert(config_key::container, QJsonValue("amnezia-openvpn"));
|
||||
containers.insert(config_key::openvpn, QJsonValue(lastConfig));
|
||||
|
||||
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] = m_settings->nextAvailableServerName();
|
||||
|
||||
|
||||
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(1);
|
||||
}
|
||||
if (dnsMatch.hasNext()) {
|
||||
o[config_key::dns2] = dnsMatch.next().captured(1);
|
||||
}
|
||||
|
||||
o[config_key::hostName] = hostName;
|
||||
|
||||
return importConnection(o);
|
||||
}
|
||||
|
||||
bool StartPageLogic::importConnectionFromWireguardConfig(const QString &config)
|
||||
{
|
||||
QJsonObject lastConfig;
|
||||
lastConfig[config_key::config] = config;
|
||||
|
||||
const static QRegularExpression hostNameAndPortRegExp("Endpoint = (.*):([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] = QString(QJsonDocument(lastConfig).toJson());
|
||||
wireguardConfig[config_key::isThirdPartyConfig] = true;
|
||||
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] = m_settings->nextAvailableServerName();
|
||||
|
||||
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;
|
||||
|
||||
return importConnection(o);
|
||||
}
|
||||
|
|
|
@ -32,6 +32,8 @@ public:
|
|||
bool importConnection(const QJsonObject &profile);
|
||||
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);
|
||||
|
|
|
@ -13,6 +13,8 @@ void ViewConfigLogic::onUpdatePage()
|
|||
{
|
||||
set_configText(QJsonDocument(configJson()).toJson());
|
||||
|
||||
auto s = configJson()[config_key::isThirdPartyConfig].toBool();
|
||||
|
||||
m_openVpnLastConfigs = m_openVpnMalStrings =
|
||||
"<style> \
|
||||
div { line-height: 0.5; } \
|
||||
|
@ -24,28 +26,42 @@ void ViewConfigLogic::onUpdatePage()
|
|||
const QJsonArray &containers = configJson()[config_key::containers].toArray();
|
||||
int i = 0;
|
||||
for (const QJsonValue &v: containers) {
|
||||
QString cfg_json = v.toObject()[ProtocolProps::protoToString(Proto::OpenVpn)]
|
||||
.toObject()[config_key::last_config].toString();
|
||||
|
||||
QString openvpn_cfg = QJsonDocument::fromJson(cfg_json.toUtf8()).object()[config_key::config]
|
||||
.toString();
|
||||
|
||||
openvpn_cfg.replace("\r", "");
|
||||
|
||||
QStringList lines = openvpn_cfg.split("\n");
|
||||
for (const QString &l: lines) {
|
||||
i++;
|
||||
QRegularExpressionMatch match = m_re.match(l);
|
||||
if (dangerousTags.contains(match.captured(0))) {
|
||||
QString t = QString("<p><font color=\"red\">%1</font>").arg(l);
|
||||
m_openVpnLastConfigs.append(t + "\n");
|
||||
m_openVpnMalStrings.append(t);
|
||||
if (m_warningStringNumber == 3) m_warningStringNumber = i - 3;
|
||||
m_warningActive = true;
|
||||
qDebug() << "ViewConfigLogic : malicious scripts warning:" << l;
|
||||
auto containerName = v.toObject()[config_key::container].toString();
|
||||
QJsonObject containerConfig = v.toObject()[containerName.replace("amnezia-", "")].toObject();
|
||||
if (containerConfig[config_key::isThirdPartyConfig].toBool()) {
|
||||
auto lastConfig = containerConfig.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 lastConfigText;
|
||||
for (const QString &l: lines) {
|
||||
lastConfigText.append(l + "\n");
|
||||
}
|
||||
else {
|
||||
m_openVpnLastConfigs.append("<p>" + l + " \n");
|
||||
set_configText(lastConfigText);
|
||||
}
|
||||
|
||||
|
||||
if (v.toObject()[config_key::container].toString() == "amnezia-openvpn") {
|
||||
QString lastConfig = v.toObject()[ProtocolProps::protoToString(Proto::OpenVpn)]
|
||||
.toObject()[config_key::last_config].toString();
|
||||
|
||||
QString lastConfigJson = QJsonDocument::fromJson(lastConfig.toUtf8()).object()[config_key::config]
|
||||
.toString();
|
||||
|
||||
QStringList lines = lastConfigJson.replace("\r", "").split("\n");
|
||||
for (const QString &l: lines) {
|
||||
i++;
|
||||
QRegularExpressionMatch match = m_re.match(l);
|
||||
if (dangerousTags.contains(match.captured(0))) {
|
||||
QString t = QString("<p><font color=\"red\">%1</font>").arg(l);
|
||||
m_openVpnLastConfigs.append(t + "\n");
|
||||
m_openVpnMalStrings.append(t);
|
||||
if (m_warningStringNumber == 3) m_warningStringNumber = i - 3;
|
||||
m_warningActive = true;
|
||||
qDebug() << "ViewConfigLogic : malicious scripts warning:" << l;
|
||||
}
|
||||
else {
|
||||
m_openVpnLastConfigs.append("<p>" + l + " \n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -61,7 +77,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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) override;
|
||||
QJsonObject getProtocolConfigFromPage(QJsonObject oldConfig) override;
|
||||
|
||||
private:
|
||||
|
|
|
@ -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);
|
||||
|
@ -77,6 +87,17 @@ 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");
|
||||
QString openVpnLastConfigText;
|
||||
for (const QString &l: lines) {
|
||||
openVpnLastConfigText.append(l + "\n");
|
||||
}
|
||||
|
||||
set_openVpnLastConfigText(openVpnLastConfigText);
|
||||
set_isThirdPartyConfig(openvpnConfig.value(config_key::isThirdPartyConfig).isBool());
|
||||
}
|
||||
|
||||
void OpenVpnLogic::onPushButtonProtoOpenVpnSaveClicked()
|
||||
|
@ -142,5 +163,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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
@ -33,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();
|
||||
|
||||
|
|
30
client/ui/pages_logic/protocols/WireGuardLogic.cpp
Normal file
30
client/ui/pages_logic/protocols/WireGuardLogic.cpp
Normal file
|
@ -0,0 +1,30 @@
|
|||
#include "WireGuardLogic.h"
|
||||
#include "core/servercontroller.h"
|
||||
#include <functional>
|
||||
#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)
|
||||
{
|
||||
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(wireGuardConfig.value(config_key::isThirdPartyConfig).toBool());
|
||||
}
|
26
client/ui/pages_logic/protocols/WireGuardLogic.h
Normal file
26
client/ui/pages_logic/protocols/WireGuardLogic.h
Normal file
|
@ -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) override;
|
||||
|
||||
private:
|
||||
UiLogic *m_uiLogic;
|
||||
|
||||
};
|
||||
|
||||
#endif // WIREGUARDLOGIC_H
|
|
@ -8,6 +8,8 @@ Item {
|
|||
readonly property int screenWidth: 380
|
||||
readonly property int screenHeight: 640
|
||||
|
||||
readonly property int defaultMargin: 20
|
||||
|
||||
function isMobile() {
|
||||
if (Qt.platform.os == "android" ||
|
||||
Qt.platform.os == "ios") {
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.12
|
||||
|
||||
import "../Config"
|
||||
|
||||
BasicButtonType {
|
||||
id: root
|
||||
width: parent.width - 80
|
||||
height: 40
|
||||
width: parent.width - 2 * GC.defaultMargin
|
||||
implicitHeight: 40
|
||||
|
||||
background: Rectangle {
|
||||
anchors.fill: parent
|
||||
|
|
26
client/ui/qml/Controls/FlickableType.qml
Normal file
26
client/ui/qml/Controls/FlickableType.qml
Normal file
|
@ -0,0 +1,26 @@
|
|||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.12
|
||||
import "../Config"
|
||||
|
||||
Flickable {
|
||||
id: fl
|
||||
|
||||
clip: true
|
||||
width: parent.width
|
||||
|
||||
anchors.topMargin: GC.defaultMargin
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: GC.defaultMargin
|
||||
anchors.left: root.left
|
||||
anchors.leftMargin: GC.defaultMargin
|
||||
anchors.right: root.right
|
||||
anchors.rightMargin: 1
|
||||
|
||||
Keys.onUpPressed: scrollBar.decrease()
|
||||
Keys.onDownPressed: scrollBar.increase()
|
||||
|
||||
ScrollBar.vertical: ScrollBar {
|
||||
id: scrollBar
|
||||
policy: fl.height >= fl.contentHeight ? ScrollBar.AlwaysOff : ScrollBar.AlwaysOn
|
||||
}
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
import QtQuick 2.12
|
||||
import "../Config"
|
||||
|
||||
Text {
|
||||
id: root
|
||||
x: 40
|
||||
width: parent.width
|
||||
width: parent.width - 2 * GC.defaultMargin
|
||||
anchors.topMargin: 10
|
||||
|
||||
font.family: "Lato"
|
||||
|
|
|
@ -1,19 +1,20 @@
|
|||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.12
|
||||
import Qt.labs.platform 1.0
|
||||
import "../Config"
|
||||
|
||||
TextField {
|
||||
id: root
|
||||
property bool error: false
|
||||
|
||||
width: parent.width - 80
|
||||
width: parent.width - 2 * GC.defaultMargin
|
||||
height: 40
|
||||
anchors.topMargin: 5
|
||||
selectByMouse: true
|
||||
|
||||
selectionColor: "darkgray"
|
||||
font.pixelSize: 16
|
||||
color: "#333333"
|
||||
|
||||
background: Rectangle {
|
||||
implicitWidth: 200
|
||||
implicitHeight: 40
|
||||
|
|
25
client/ui/qml/Controls/UrlButtonType.qml
Normal file
25
client/ui/qml/Controls/UrlButtonType.qml
Normal file
|
@ -0,0 +1,25 @@
|
|||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.12
|
||||
|
||||
BasicButtonType {
|
||||
property alias label: lbl
|
||||
id: root
|
||||
antialiasing: true
|
||||
height: 21
|
||||
background: Item {}
|
||||
|
||||
contentItem: Text {
|
||||
id: lbl
|
||||
anchors.fill: parent
|
||||
font.family: "Lato"
|
||||
font.styleName: "normal"
|
||||
font.pixelSize: 18
|
||||
font.underline: true
|
||||
|
||||
text: root.text
|
||||
color: "#3045ee"
|
||||
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
}
|
|
@ -50,8 +50,7 @@ Drawer {
|
|||
|
||||
}
|
||||
|
||||
Flickable {
|
||||
clip: true
|
||||
FlickableType {
|
||||
anchors.fill: parent
|
||||
contentHeight: col.height
|
||||
|
||||
|
|
|
@ -19,20 +19,11 @@ PageBase {
|
|||
text: qsTr("Application Settings")
|
||||
}
|
||||
|
||||
Flickable {
|
||||
FlickableType {
|
||||
id: fl
|
||||
width: root.width
|
||||
anchors.top: caption.bottom
|
||||
anchors.topMargin: 20
|
||||
anchors.bottom: logo.top
|
||||
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
|
||||
|
@ -40,6 +31,7 @@ PageBase {
|
|||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 15
|
||||
|
||||
CheckBoxType {
|
||||
visible: !GC.isMobile()
|
||||
|
@ -79,7 +71,6 @@ PageBase {
|
|||
BlueButtonType {
|
||||
visible: !GC.isMobile()
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 41
|
||||
text: qsTr("Check for updates")
|
||||
onClicked: {
|
||||
Qt.openUrlExternally("https://github.com/amnezia-vpn/desktop-client/releases/latest")
|
||||
|
@ -98,7 +89,6 @@ PageBase {
|
|||
}
|
||||
BlueButtonType {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 41
|
||||
text: qsTr("Open logs folder")
|
||||
onClicked: {
|
||||
AppSettingsLogic.onPushButtonOpenLogsClicked()
|
||||
|
@ -108,7 +98,6 @@ PageBase {
|
|||
BlueButtonType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 10
|
||||
Layout.preferredHeight: 41
|
||||
text: qsTr("Export logs")
|
||||
onClicked: {
|
||||
AppSettingsLogic.onPushButtonExportLogsClicked()
|
||||
|
@ -118,7 +107,6 @@ PageBase {
|
|||
BlueButtonType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 10
|
||||
Layout.preferredHeight: 41
|
||||
|
||||
property string start_text: qsTr("Clear logs")
|
||||
property string end_text: qsTr("Cleared")
|
||||
|
|
|
@ -17,8 +17,4 @@ Item {
|
|||
|
||||
onActivated: pageActive = true
|
||||
onDeactivated: pageActive = false
|
||||
|
||||
// width: GC.screenWidth
|
||||
// height: GC.screenHeight
|
||||
|
||||
}
|
||||
|
|
|
@ -17,20 +17,12 @@ PageBase {
|
|||
z: -1
|
||||
}
|
||||
|
||||
Flickable {
|
||||
FlickableType {
|
||||
id: fl
|
||||
width: root.width
|
||||
anchors.top: back.bottom
|
||||
anchors.topMargin: 0
|
||||
anchors.bottom: root.bottom
|
||||
anchors.bottomMargin: 10
|
||||
anchors.left: root.left
|
||||
anchors.leftMargin: 30
|
||||
anchors.right: root.right
|
||||
anchors.rightMargin: 30
|
||||
|
||||
contentHeight: content.height
|
||||
clip: true
|
||||
|
||||
ColumnLayout {
|
||||
id: content
|
||||
|
@ -39,6 +31,7 @@ PageBase {
|
|||
anchors.topMargin: 10
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: GC.defaultMargin
|
||||
|
||||
spacing: 15
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.12
|
||||
import QtQuick.Layouts 1.15
|
||||
import PageEnum 1.0
|
||||
import "./"
|
||||
import "../Controls"
|
||||
|
@ -18,108 +19,97 @@ PageBase {
|
|||
text: qsTr("DNS Servers")
|
||||
}
|
||||
|
||||
CheckBoxType {
|
||||
id: cb_amnezia_dns
|
||||
FlickableType {
|
||||
id: fl
|
||||
anchors.top: caption.bottom
|
||||
x: 30
|
||||
width: parent.width - 60
|
||||
text: qsTr("Use AmneziaDNS service (recommended)")
|
||||
checked: NetworkSettingsLogic.checkBoxUseAmneziaDnsChecked
|
||||
onCheckedChanged: {
|
||||
NetworkSettingsLogic.checkBoxUseAmneziaDnsChecked = checked
|
||||
NetworkSettingsLogic.onCheckBoxUseAmneziaDnsToggled(checked)
|
||||
UiLogic.onUpdateAllPages()
|
||||
}
|
||||
}
|
||||
contentHeight: content.height
|
||||
|
||||
LabelType {
|
||||
id: lb_amnezia_dns
|
||||
x: 30
|
||||
anchors.top: cb_amnezia_dns.bottom
|
||||
width: parent.width - 60
|
||||
text: qsTr("Use AmneziaDNS container on your server, when it installed.\n
|
||||
ColumnLayout {
|
||||
id: content
|
||||
enabled: logic.pageEnabled
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 15
|
||||
|
||||
CheckBoxType {
|
||||
Layout.preferredWidth: parent.width
|
||||
text: qsTr("Use AmneziaDNS service (recommended)")
|
||||
checked: NetworkSettingsLogic.checkBoxUseAmneziaDnsChecked
|
||||
onCheckedChanged: {
|
||||
NetworkSettingsLogic.checkBoxUseAmneziaDnsChecked = checked
|
||||
NetworkSettingsLogic.onCheckBoxUseAmneziaDnsToggled(checked)
|
||||
UiLogic.onUpdateAllPages()
|
||||
}
|
||||
}
|
||||
|
||||
LabelType {
|
||||
Layout.preferredWidth: parent.width
|
||||
text: qsTr("Use AmneziaDNS container on your server, when it installed.\n
|
||||
Your AmneziaDNS server available only when it installed and VPN connected, it has internal IP address 172.29.172.254\n
|
||||
If AmneziaDNS service is not installed on the same server, or this option is unchecked, the following DNS servers will be used:")
|
||||
}
|
||||
}
|
||||
|
||||
LabelType {
|
||||
id: l1
|
||||
x: 30
|
||||
anchors.top: lb_amnezia_dns.bottom
|
||||
width: parent.width - 30
|
||||
height: 21
|
||||
text: qsTr("Primary DNS server")
|
||||
}
|
||||
TextFieldType {
|
||||
id: dns1
|
||||
x: 30
|
||||
anchors.top: l1.bottom
|
||||
width: parent.width - 90
|
||||
height: 40
|
||||
text: NetworkSettingsLogic.lineEditDns1Text
|
||||
onEditingFinished: {
|
||||
NetworkSettingsLogic.lineEditDns1Text = text
|
||||
NetworkSettingsLogic.onLineEditDns1EditFinished(text)
|
||||
UiLogic.onUpdateAllPages()
|
||||
}
|
||||
validator: RegExpValidator {
|
||||
regExp: NetworkSettingsLogic.ipAddressRegex
|
||||
}
|
||||
}
|
||||
SvgButtonType {
|
||||
id: resetDNS1
|
||||
anchors. left: dns1.right
|
||||
anchors.leftMargin: 10
|
||||
anchors.verticalCenter: dns1.verticalCenter
|
||||
width: 24
|
||||
height: 24
|
||||
icon.source: "qrc:/images/svg/refresh_black_24dp.svg"
|
||||
onClicked: {
|
||||
NetworkSettingsLogic.onPushButtonResetDns1Clicked()
|
||||
UiLogic.onUpdateAllPages()
|
||||
LabelType {
|
||||
Layout.topMargin: 15
|
||||
text: qsTr("Primary DNS server")
|
||||
}
|
||||
TextFieldType {
|
||||
height: 40
|
||||
implicitWidth: parent.width
|
||||
text: NetworkSettingsLogic.lineEditDns1Text
|
||||
onEditingFinished: {
|
||||
NetworkSettingsLogic.lineEditDns1Text = text
|
||||
NetworkSettingsLogic.onLineEditDns1EditFinished(text)
|
||||
UiLogic.onUpdateAllPages()
|
||||
}
|
||||
validator: RegExpValidator {
|
||||
regExp: NetworkSettingsLogic.ipAddressRegex
|
||||
}
|
||||
}
|
||||
|
||||
UrlButtonType {
|
||||
text: qsTr("Reset to default")
|
||||
label.horizontalAlignment: Text.AlignLeft
|
||||
label.verticalAlignment: Text.AlignTop
|
||||
label.font.pixelSize: 14
|
||||
icon.source: "qrc:/images/svg/refresh_black_24dp.svg"
|
||||
onClicked: {
|
||||
NetworkSettingsLogic.onPushButtonResetDns1Clicked()
|
||||
UiLogic.onUpdateAllPages()
|
||||
}
|
||||
}
|
||||
|
||||
LabelType {
|
||||
text: qsTr("Secondary DNS server")
|
||||
}
|
||||
TextFieldType {
|
||||
height: 40
|
||||
implicitWidth: parent.width
|
||||
text: NetworkSettingsLogic.lineEditDns2Text
|
||||
onEditingFinished: {
|
||||
NetworkSettingsLogic.lineEditDns2Text = text
|
||||
NetworkSettingsLogic.onLineEditDns2EditFinished(text)
|
||||
UiLogic.onUpdateAllPages()
|
||||
}
|
||||
validator: RegExpValidator {
|
||||
regExp: NetworkSettingsLogic.ipAddressRegex
|
||||
}
|
||||
}
|
||||
|
||||
UrlButtonType {
|
||||
text: qsTr("Reset to default")
|
||||
label.horizontalAlignment: Text.AlignLeft
|
||||
label.verticalAlignment: Text.AlignTop
|
||||
label.font.pixelSize: 14
|
||||
icon.source: "qrc:/images/svg/refresh_black_24dp.svg"
|
||||
onClicked: {
|
||||
NetworkSettingsLogic.onPushButtonResetDns2Clicked()
|
||||
UiLogic.onUpdateAllPages()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LabelType {
|
||||
id: l2
|
||||
x: 30
|
||||
anchors.top: dns1.bottom
|
||||
anchors.topMargin: 20
|
||||
width: parent.width - 60
|
||||
height: 21
|
||||
text: qsTr("Secondray DNS server")
|
||||
}
|
||||
TextFieldType {
|
||||
id: dns2
|
||||
x: 30
|
||||
anchors.top: l2.bottom
|
||||
width: parent.width - 90
|
||||
height: 40
|
||||
text: NetworkSettingsLogic.lineEditDns2Text
|
||||
onEditingFinished: {
|
||||
NetworkSettingsLogic.lineEditDns2Text = text
|
||||
NetworkSettingsLogic.onLineEditDns2EditFinished(text)
|
||||
UiLogic.onUpdateAllPages()
|
||||
}
|
||||
validator: RegExpValidator {
|
||||
regExp: NetworkSettingsLogic.ipAddressRegex
|
||||
}
|
||||
}
|
||||
SvgButtonType {
|
||||
id: resetDNS2
|
||||
anchors. left: dns2.right
|
||||
anchors.leftMargin: 10
|
||||
anchors.verticalCenter: dns2.verticalCenter
|
||||
width: 24
|
||||
height: 24
|
||||
icon.source: "qrc:/images/svg/refresh_black_24dp.svg"
|
||||
onClicked: {
|
||||
NetworkSettingsLogic.onPushButtonResetDns2Clicked()
|
||||
UiLogic.onUpdateAllPages()
|
||||
}
|
||||
}
|
||||
|
||||
Logo {
|
||||
anchors.bottom: parent.bottom
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ import "../Config"
|
|||
PageBase {
|
||||
id: root
|
||||
page: PageEnum.NewServer
|
||||
//logic: {}
|
||||
|
||||
BackButton {
|
||||
id: back_from_new_server
|
||||
|
@ -22,15 +21,15 @@ PageBase {
|
|||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
text: qsTr("If you want easily configure your server just run Wizard")
|
||||
width: parent.width - 80
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: caption.bottom
|
||||
anchors.topMargin: 30
|
||||
}
|
||||
BlueButtonType {
|
||||
id: pushButtonWizard
|
||||
text: qsTr("Run Setup Wizard")
|
||||
anchors.top: labelWizard.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: labelWizard.bottom
|
||||
anchors.topMargin: 10
|
||||
onClicked: {
|
||||
UiLogic.goToPage(PageEnum.Wizard);
|
||||
|
@ -41,14 +40,13 @@ PageBase {
|
|||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
text: qsTr("Press configure manually to choose VPN protocols you want to install")
|
||||
|
||||
width: parent.width - 80
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: pushButtonWizard.bottom
|
||||
anchors.topMargin: 40
|
||||
}
|
||||
|
||||
BlueButtonType {
|
||||
text: qsTr("Configure VPN protocols manually")
|
||||
text: qsTr("Configure")
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: labelManual.bottom
|
||||
anchors.topMargin: 10
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ PageBase {
|
|||
|
||||
BackButton {
|
||||
id: back
|
||||
onClicked: tb_c.currentIndex = -1
|
||||
}
|
||||
Caption {
|
||||
id: caption
|
||||
|
@ -174,11 +175,7 @@ PageBase {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Flickable {
|
||||
FlickableType {
|
||||
visible: container_selector.selectedIndex <= 0
|
||||
clip: true
|
||||
width: parent.width
|
||||
|
@ -224,7 +221,6 @@ PageBase {
|
|||
|
||||
ListView {
|
||||
id: tb_c
|
||||
x: 10
|
||||
width: parent.width - 10
|
||||
height: tb_c.contentItem.height
|
||||
currentIndex: -1
|
||||
|
@ -293,7 +289,7 @@ PageBase {
|
|||
|
||||
ImageButtonType {
|
||||
id: button_remove
|
||||
visible: index === tb_c.currentIndex
|
||||
visible: (index === tb_c.currentIndex) && ServerContainersLogic.isManagedServer
|
||||
Layout.alignment: Qt.AlignRight
|
||||
checkable: true
|
||||
icon.source: "qrc:/images/delete.png"
|
||||
|
@ -320,7 +316,7 @@ PageBase {
|
|||
|
||||
ImageButtonType {
|
||||
id: button_share
|
||||
visible: index === tb_c.currentIndex
|
||||
visible: (index === tb_c.currentIndex) && ServerContainersLogic.isManagedServer
|
||||
Layout.alignment: Qt.AlignRight
|
||||
icon.source: "qrc:/images/share.png"
|
||||
implicitWidth: 30
|
||||
|
@ -431,7 +427,7 @@ PageBase {
|
|||
|
||||
width: parent.width - 40
|
||||
height: 40
|
||||
text: qsTr("Install new protocols container")
|
||||
text: qsTr("Install new service")
|
||||
font.pixelSize: 16
|
||||
onClicked: container_selector.visible ? container_selector.close() : container_selector.open()
|
||||
|
||||
|
|
|
@ -35,18 +35,21 @@ PageBase {
|
|||
|
||||
ListView {
|
||||
id: listWidget_servers
|
||||
x: 20
|
||||
x: GC.defaultMargin
|
||||
anchors.top: caption.bottom
|
||||
anchors.topMargin: 15
|
||||
width: parent.width
|
||||
width: parent.width - GC.defaultMargin - 1
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 20
|
||||
model: ServerListLogic.serverListModel
|
||||
highlightRangeMode: ListView.ApplyRange
|
||||
highlightMoveVelocity: -1
|
||||
currentIndex: ServerListLogic.currServerIdx
|
||||
spacing: 5
|
||||
clip: true
|
||||
delegate: Item {
|
||||
height: 60
|
||||
width: root.width - 40
|
||||
width: listWidget_servers.width - 15
|
||||
MouseArea {
|
||||
id: ms
|
||||
anchors.fill: parent
|
||||
|
@ -55,10 +58,6 @@ PageBase {
|
|||
if (GC.isMobile()) {
|
||||
ServerListLogic.onServerListPushbuttonSettingsClicked(index)
|
||||
}
|
||||
else {
|
||||
listWidget_servers.currentIndex = index
|
||||
}
|
||||
|
||||
mouse.accepted = false
|
||||
}
|
||||
onEntered: {
|
||||
|
@ -94,14 +93,14 @@ PageBase {
|
|||
id: label_address
|
||||
x: 20
|
||||
y: 40
|
||||
width: 141
|
||||
width: listWidget_servers.width - 100
|
||||
height: 16
|
||||
text: address
|
||||
}
|
||||
Text {
|
||||
x: 10
|
||||
y: 10
|
||||
width: 181
|
||||
width: listWidget_servers.width - 100
|
||||
height: 21
|
||||
font.family: "Lato"
|
||||
font.styleName: "normal"
|
||||
|
@ -175,5 +174,9 @@ PageBase {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
ScrollBar.vertical: ScrollBar {
|
||||
policy: ScrollBar.AsNeeded
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.12
|
||||
import QtQuick.Layouts 1.15
|
||||
import PageEnum 1.0
|
||||
import "./"
|
||||
import "../Controls"
|
||||
|
@ -16,113 +17,106 @@ PageBase {
|
|||
id: back
|
||||
}
|
||||
Caption {
|
||||
id: caption
|
||||
text: qsTr("Server settings")
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
LabelType {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
y: 150
|
||||
width: 341
|
||||
height: 31
|
||||
font.pixelSize: 20
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: ServerSettingsLogic.labelCurrentVpnProtocolText
|
||||
}
|
||||
// LabelType {
|
||||
// anchors.horizontalCenter: parent.horizontalCenter
|
||||
// y: 120
|
||||
// width: 341
|
||||
// height: 31
|
||||
// font.pixelSize: 20
|
||||
// horizontalAlignment: Text.AlignHCenter
|
||||
// text: ServerSettingsLogic.labelServerText
|
||||
// }
|
||||
TextFieldType {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
y: 120
|
||||
width: 341
|
||||
height: 31
|
||||
font.pixelSize: 20
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: ServerSettingsLogic.labelServerText
|
||||
readOnly: true
|
||||
background: Item {}
|
||||
|
||||
FlickableType {
|
||||
id: fl
|
||||
anchors.top: caption.bottom
|
||||
anchors.bottom: logo.top
|
||||
contentHeight: content.height
|
||||
|
||||
ColumnLayout {
|
||||
id: content
|
||||
enabled: logic.pageEnabled
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 15
|
||||
|
||||
LabelType {
|
||||
Layout.fillWidth: true
|
||||
font.pixelSize: 20
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: ServerSettingsLogic.labelCurrentVpnProtocolText
|
||||
}
|
||||
|
||||
TextFieldType {
|
||||
Layout.fillWidth: true
|
||||
font.pixelSize: 20
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: ServerSettingsLogic.labelServerText
|
||||
readOnly: true
|
||||
background: Item {}
|
||||
}
|
||||
|
||||
LabelType {
|
||||
Layout.fillWidth: true
|
||||
text: ServerSettingsLogic.labelWaitInfoText
|
||||
visible: ServerSettingsLogic.labelWaitInfoVisible
|
||||
}
|
||||
TextFieldType {
|
||||
Layout.fillWidth: true
|
||||
text: ServerSettingsLogic.lineEditDescriptionText
|
||||
onEditingFinished: {
|
||||
ServerSettingsLogic.lineEditDescriptionText = text
|
||||
ServerSettingsLogic.onLineEditDescriptionEditingFinished()
|
||||
}
|
||||
}
|
||||
|
||||
BlueButtonType {
|
||||
text: qsTr("Protocols and Services")
|
||||
Layout.topMargin: 20
|
||||
Layout.fillWidth: true
|
||||
onClicked: {
|
||||
UiLogic.goToPage(PageEnum.ServerContainers)
|
||||
}
|
||||
}
|
||||
BlueButtonType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 10
|
||||
text: qsTr("Share Server (FULL ACCESS)")
|
||||
visible: ServerSettingsLogic.pushButtonShareFullVisible
|
||||
onClicked: {
|
||||
ServerSettingsLogic.onPushButtonShareFullClicked()
|
||||
}
|
||||
}
|
||||
|
||||
BlueButtonType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 60
|
||||
text: ServerSettingsLogic.pushButtonClearText
|
||||
visible: ServerSettingsLogic.pushButtonClearVisible
|
||||
onClicked: {
|
||||
ServerSettingsLogic.onPushButtonClearServer()
|
||||
}
|
||||
}
|
||||
BlueButtonType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 10
|
||||
text: ServerSettingsLogic.pushButtonClearClientCacheText
|
||||
visible: ServerSettingsLogic.pushButtonClearClientCacheVisible
|
||||
onClicked: {
|
||||
ServerSettingsLogic.onPushButtonClearClientCacheClicked()
|
||||
}
|
||||
}
|
||||
BlueButtonType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 10
|
||||
text: qsTr("Forget this server")
|
||||
onClicked: {
|
||||
ServerSettingsLogic.onPushButtonForgetServer()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
LabelType {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
y: 530
|
||||
width: 301
|
||||
height: 41
|
||||
text: ServerSettingsLogic.labelWaitInfoText
|
||||
visible: ServerSettingsLogic.labelWaitInfoVisible
|
||||
}
|
||||
TextFieldType {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
y: 80
|
||||
width: 251
|
||||
height: 31
|
||||
text: ServerSettingsLogic.lineEditDescriptionText
|
||||
onEditingFinished: {
|
||||
ServerSettingsLogic.lineEditDescriptionText = text
|
||||
ServerSettingsLogic.onLineEditDescriptionEditingFinished()
|
||||
}
|
||||
}
|
||||
BlueButtonType {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
y: 410
|
||||
width: parent.width - 40
|
||||
height: 40
|
||||
text: ServerSettingsLogic.pushButtonClearText
|
||||
visible: ServerSettingsLogic.pushButtonClearVisible
|
||||
onClicked: {
|
||||
ServerSettingsLogic.onPushButtonClearServer()
|
||||
}
|
||||
}
|
||||
BlueButtonType {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
y: 350
|
||||
width: parent.width - 40
|
||||
height: 40
|
||||
text: ServerSettingsLogic.pushButtonClearClientCacheText
|
||||
visible: ServerSettingsLogic.pushButtonClearClientCacheVisible
|
||||
onClicked: {
|
||||
ServerSettingsLogic.onPushButtonClearClientCacheClicked()
|
||||
}
|
||||
}
|
||||
BlueButtonType {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
y: 470
|
||||
width: parent.width - 40
|
||||
height: 40
|
||||
text: qsTr("Forget this server")
|
||||
onClicked: {
|
||||
ServerSettingsLogic.onPushButtonForgetServer()
|
||||
}
|
||||
}
|
||||
BlueButtonType {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
y: 210
|
||||
width: parent.width - 40
|
||||
height: 40
|
||||
text: qsTr("Protocols and Services")
|
||||
onClicked: {
|
||||
UiLogic.goToPage(PageEnum.ServerContainers)
|
||||
}
|
||||
}
|
||||
BlueButtonType {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
y: 260
|
||||
width: parent.width - 40
|
||||
height: 40
|
||||
text: qsTr("Share Server (FULL ACCESS)")
|
||||
visible: ServerSettingsLogic.pushButtonShareFullVisible
|
||||
onClicked: {
|
||||
ServerSettingsLogic.onPushButtonShareFullClicked()
|
||||
}
|
||||
}
|
||||
|
||||
Logo {
|
||||
id : logo
|
||||
anchors.bottom: parent.bottom
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,20 +19,11 @@ PageBase {
|
|||
text: qsTr("Setup your server to use VPN")
|
||||
}
|
||||
|
||||
Flickable {
|
||||
FlickableType {
|
||||
id: fl
|
||||
width: root.width
|
||||
anchors.top: caption.bottom
|
||||
anchors.topMargin: 20
|
||||
anchors.bottom: root.bottom
|
||||
anchors.bottomMargin: 20
|
||||
anchors.left: root.left
|
||||
anchors.leftMargin: 30
|
||||
anchors.right: root.right
|
||||
anchors.rightMargin: 30
|
||||
|
||||
anchors.bottom: next_button.top
|
||||
contentHeight: content.height
|
||||
clip: true
|
||||
|
||||
ColumnLayout {
|
||||
id: content
|
||||
|
@ -40,6 +31,7 @@ PageBase {
|
|||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 15
|
||||
|
||||
RadioButtonType {
|
||||
id: radioButton_setup_wizard_high
|
||||
|
@ -93,21 +85,23 @@ OpenVPN over ShadowSocks profile will be installed.\n")
|
|||
text: qsTr("I want to improve my privacy on the internet.
|
||||
OpenVPN profile will be installed.\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BlueButtonType {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 41
|
||||
text: qsTr("Next")
|
||||
onClicked: {
|
||||
if (radioButton_setup_wizard_high.checked) {
|
||||
UiLogic.goToPage(PageEnum.WizardHigh, false);
|
||||
} else if (radioButton_setup_wizard_medium.checked) {
|
||||
UiLogic.goToPage(PageEnum.WizardMedium, false);
|
||||
} else if (radioButton_setup_wizard_low.checked) {
|
||||
UiLogic.goToPage(PageEnum.WizardLow, false);
|
||||
}
|
||||
}
|
||||
BlueButtonType {
|
||||
id: next_button
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: GC.defaultMargin
|
||||
x: GC.defaultMargin
|
||||
width: parent.width - 2 * GC.defaultMargin
|
||||
text: qsTr("Next")
|
||||
onClicked: {
|
||||
if (radioButton_setup_wizard_high.checked) {
|
||||
UiLogic.goToPage(PageEnum.WizardHigh, false);
|
||||
} else if (radioButton_setup_wizard_medium.checked) {
|
||||
UiLogic.goToPage(PageEnum.WizardMedium, false);
|
||||
} else if (radioButton_setup_wizard_low.checked) {
|
||||
UiLogic.goToPage(PageEnum.WizardLow, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,20 +19,11 @@ PageBase {
|
|||
text: qsTr("Setup Wizard")
|
||||
}
|
||||
|
||||
Flickable {
|
||||
FlickableType {
|
||||
id: fl
|
||||
width: root.width
|
||||
anchors.top: caption.bottom
|
||||
anchors.topMargin: 20
|
||||
anchors.bottom: root.bottom
|
||||
anchors.bottomMargin: 20
|
||||
anchors.left: root.left
|
||||
anchors.leftMargin: 30
|
||||
anchors.right: root.right
|
||||
anchors.rightMargin: 30
|
||||
|
||||
anchors.bottom: next_button.top
|
||||
contentHeight: content.height
|
||||
clip: true
|
||||
|
||||
ColumnLayout {
|
||||
id: content
|
||||
|
@ -40,6 +31,7 @@ PageBase {
|
|||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 15
|
||||
|
||||
LabelType {
|
||||
Layout.fillWidth: true
|
||||
|
@ -83,22 +75,22 @@ You SHOULD set this website address to some foreign website which is not blocked
|
|||
|
||||
This protocol support exporting connection profiles to mobile devices by exporting ShadowSocks and Cloak configs (you should launch the 3rd party open source VPN client - ShadowSocks VPN and install Cloak plugin).")
|
||||
}
|
||||
BlueButtonType {
|
||||
id: next_button
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 15
|
||||
Layout.preferredHeight: 41
|
||||
text: qsTr("Next")
|
||||
onClicked: {
|
||||
let domain = website_masking.text;
|
||||
if (!domain || !domain.includes(".")) {
|
||||
return
|
||||
}
|
||||
UiLogic.goToPage(PageEnum.WizardVpnMode, false)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
BlueButtonType {
|
||||
id: next_button
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: GC.defaultMargin
|
||||
x: GC.defaultMargin
|
||||
width: parent.width - 2 * GC.defaultMargin
|
||||
text: qsTr("Next")
|
||||
onClicked: {
|
||||
let domain = website_masking.text;
|
||||
if (!domain || !domain.includes(".")) {
|
||||
return
|
||||
}
|
||||
UiLogic.goToPage(PageEnum.WizardVpnMode, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,20 +19,11 @@ PageBase {
|
|||
text: qsTr("Setup Wizard")
|
||||
}
|
||||
|
||||
Flickable {
|
||||
FlickableType {
|
||||
id: fl
|
||||
width: root.width
|
||||
anchors.top: caption.bottom
|
||||
anchors.topMargin: 20
|
||||
anchors.bottom: root.bottom
|
||||
anchors.bottomMargin: 20
|
||||
anchors.left: root.left
|
||||
anchors.leftMargin: 30
|
||||
anchors.right: root.right
|
||||
anchors.rightMargin: 30
|
||||
|
||||
anchors.bottom: next_button.top
|
||||
contentHeight: content.height
|
||||
clip: true
|
||||
|
||||
ColumnLayout {
|
||||
id: content
|
||||
|
@ -40,6 +31,7 @@ PageBase {
|
|||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 15
|
||||
|
||||
LabelType {
|
||||
Layout.fillWidth: true
|
||||
|
@ -56,16 +48,18 @@ We recommend not to use messaging applications for sending the connection profil
|
|||
text: qsTr('OpenVPN profile will be installed')
|
||||
verticalAlignment: Text.AlignBottom
|
||||
}
|
||||
BlueButtonType {
|
||||
id: next_button
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 15
|
||||
Layout.preferredHeight: 41
|
||||
text: qsTr("Start configuring")
|
||||
onClicked: {
|
||||
WizardLogic.onPushButtonLowFinishClicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BlueButtonType {
|
||||
id: next_button
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: GC.defaultMargin
|
||||
x: GC.defaultMargin
|
||||
width: parent.width - 2 * GC.defaultMargin
|
||||
text: qsTr("Start configuring")
|
||||
onClicked: {
|
||||
WizardLogic.onPushButtonLowFinishClicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,20 +19,11 @@ PageBase {
|
|||
text: qsTr("Setup Wizard")
|
||||
}
|
||||
|
||||
Flickable {
|
||||
FlickableType {
|
||||
id: fl
|
||||
width: root.width
|
||||
anchors.top: caption.bottom
|
||||
anchors.topMargin: 20
|
||||
anchors.bottom: root.bottom
|
||||
anchors.bottomMargin: 20
|
||||
anchors.left: root.left
|
||||
anchors.leftMargin: 30
|
||||
anchors.right: root.right
|
||||
anchors.rightMargin: 30
|
||||
|
||||
anchors.bottom: next_button.top
|
||||
contentHeight: content.height
|
||||
clip: true
|
||||
|
||||
ColumnLayout {
|
||||
id: content
|
||||
|
@ -40,6 +31,7 @@ PageBase {
|
|||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 15
|
||||
|
||||
LabelType {
|
||||
Layout.fillWidth: true
|
||||
|
@ -51,16 +43,18 @@ PageBase {
|
|||
Layout.topMargin: 15
|
||||
text: qsTr('OpenVPN over ShadowSocks profile will be installed')
|
||||
}
|
||||
BlueButtonType {
|
||||
id: next_button
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 15
|
||||
Layout.preferredHeight: 41
|
||||
text: qsTr("Next")
|
||||
onClicked: {
|
||||
UiLogic.goToPage(PageEnum.WizardVpnMode, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BlueButtonType {
|
||||
id: next_button
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: GC.defaultMargin
|
||||
x: GC.defaultMargin
|
||||
width: parent.width - 2 * GC.defaultMargin
|
||||
text: qsTr("Next")
|
||||
onClicked: {
|
||||
UiLogic.goToPage(PageEnum.WizardVpnMode, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,20 +19,11 @@ PageBase {
|
|||
text: qsTr("Setup Wizard")
|
||||
}
|
||||
|
||||
Flickable {
|
||||
FlickableType {
|
||||
id: fl
|
||||
width: root.width
|
||||
anchors.top: caption.bottom
|
||||
anchors.topMargin: 20
|
||||
anchors.bottom: root.bottom
|
||||
anchors.bottomMargin: 20
|
||||
anchors.left: root.left
|
||||
anchors.leftMargin: 30
|
||||
anchors.right: root.right
|
||||
anchors.rightMargin: 30
|
||||
|
||||
anchors.bottom: vpn_mode_finish.top
|
||||
contentHeight: content.height
|
||||
clip: true
|
||||
|
||||
ColumnLayout {
|
||||
id: content
|
||||
|
@ -40,6 +31,7 @@ PageBase {
|
|||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 15
|
||||
|
||||
LabelType {
|
||||
Layout.fillWidth: true
|
||||
|
@ -56,17 +48,18 @@ You can enable VPN mode "For selected sites" and add blocked sites you need to v
|
|||
WizardLogic.checkBoxVpnModeChecked = checked
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BlueButtonType {
|
||||
id: vpn_mode_finish
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 15
|
||||
Layout.preferredHeight: 41
|
||||
text: qsTr("Start configuring")
|
||||
onClicked: {
|
||||
WizardLogic.onPushButtonVpnModeFinishClicked()
|
||||
}
|
||||
}
|
||||
BlueButtonType {
|
||||
id: vpn_mode_finish
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: GC.defaultMargin
|
||||
x: GC.defaultMargin
|
||||
width: parent.width - 2 * GC.defaultMargin
|
||||
text: qsTr("Start configuring")
|
||||
onClicked: {
|
||||
WizardLogic.onPushButtonVpnModeFinishClicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,12 +28,11 @@ PageBase {
|
|||
}
|
||||
|
||||
|
||||
Flickable {
|
||||
FlickableType {
|
||||
clip: true
|
||||
width: parent.width
|
||||
anchors.top: caption.bottom
|
||||
anchors.bottom: root.bottom
|
||||
contentHeight: col.height
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
|
||||
Column {
|
||||
id: col
|
||||
|
@ -55,7 +54,6 @@ PageBase {
|
|||
|
||||
|
||||
ShareConnectionContent {
|
||||
x: 10
|
||||
text: qsTr("Share for Amnezia")
|
||||
height: 40
|
||||
width: tb_c.width - 10
|
||||
|
@ -64,7 +62,6 @@ PageBase {
|
|||
|
||||
ListView {
|
||||
id: tb_c
|
||||
x: 10
|
||||
width: parent.width - 10
|
||||
height: tb_c.contentItem.height
|
||||
currentIndex: -1
|
||||
|
|
|
@ -29,12 +29,12 @@ PageBase {
|
|||
|
||||
BasicButtonType {
|
||||
id: start_switch_page
|
||||
width: parent.width - 2 * GC.defaultMargin
|
||||
implicitHeight: 40
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: logo.top
|
||||
anchors.bottomMargin: 10
|
||||
|
||||
width: parent.width - 80
|
||||
height: 40
|
||||
anchors.topMargin: 20
|
||||
|
||||
text: qsTr("Set up your own server")
|
||||
|
@ -71,7 +71,6 @@ PageBase {
|
|||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
antialiasing: true
|
||||
|
||||
}
|
||||
|
||||
Item {
|
||||
|
@ -87,6 +86,7 @@ PageBase {
|
|||
id: label_connection_code
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 20
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: qsTr("Connection code")
|
||||
}
|
||||
TextFieldType {
|
||||
|
@ -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
|
||||
|
@ -285,7 +286,8 @@ PageBase {
|
|||
}
|
||||
enabled: StartPageLogic.pushButtonConnectEnabled
|
||||
}
|
||||
BasicButtonType {
|
||||
|
||||
UrlButtonType {
|
||||
id: new_sever_connect_key
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: new_sever_connect.bottom
|
||||
|
@ -293,21 +295,8 @@ PageBase {
|
|||
width: 281
|
||||
height: 21
|
||||
text: qsTr("Connect using SSH key")
|
||||
background: Item {
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
contentItem: Text {
|
||||
anchors.fill: parent
|
||||
font.family: "Lato"
|
||||
font.styleName: "normal"
|
||||
font.pixelSize: 16
|
||||
color: "#15CDCB";
|
||||
text: new_sever_connect_key.text
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
antialiasing: true
|
||||
label.font.pixelSize: 16
|
||||
checkable: true
|
||||
checked: StartPageLogic.pushButtonConnectKeyChecked
|
||||
onCheckedChanged: {
|
||||
|
|
|
@ -30,27 +30,12 @@ PageBase {
|
|||
font.pixelSize: 12
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
UrlButtonType {
|
||||
y: 10
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
height: 21
|
||||
background: Item {}
|
||||
|
||||
|
||||
contentItem: Text {
|
||||
anchors.fill: parent
|
||||
font.family: "Lato"
|
||||
font.styleName: "normal"
|
||||
font.pixelSize: 18
|
||||
font.underline: true
|
||||
|
||||
text: qsTr("Donate")
|
||||
color: "#D4D4D4"
|
||||
|
||||
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
label.color: "#D4D4D4"
|
||||
label.text: qsTr("Donate")
|
||||
|
||||
onClicked: {
|
||||
UiLogic.goToPage(PageEnum.About)
|
||||
|
|
|
@ -20,20 +20,10 @@ PageBase {
|
|||
text: qsTr("Check config")
|
||||
}
|
||||
|
||||
Flickable {
|
||||
FlickableType {
|
||||
id: fl
|
||||
width: root.width
|
||||
anchors.top: caption.bottom
|
||||
anchors.topMargin: 20
|
||||
anchors.bottom: root.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
|
||||
|
@ -41,6 +31,7 @@ PageBase {
|
|||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 15
|
||||
|
||||
TextAreaType {
|
||||
id: ta_config
|
||||
|
@ -124,7 +115,7 @@ AmneziaVPN detected this profile may contain malicious scripts. Please, carefull
|
|||
|
||||
BasicButtonType {
|
||||
Layout.preferredWidth: (content.width - parent.spacing) /2
|
||||
Layout.preferredHeight: 41
|
||||
Layout.preferredHeight: 40
|
||||
font.pixelSize: btn_import.font.pixelSize
|
||||
text: qsTr("Cancel")
|
||||
onClicked: {
|
||||
|
@ -135,7 +126,6 @@ AmneziaVPN detected this profile may contain malicious scripts. Please, carefull
|
|||
BlueButtonType {
|
||||
id: btn_import
|
||||
Layout.preferredWidth: (content.width - parent.spacing) /2
|
||||
Layout.preferredHeight: 41
|
||||
text: qsTr("Import config")
|
||||
onClicked: {
|
||||
logic.importConfig()
|
||||
|
|
|
@ -19,381 +19,398 @@ PageProtocolBase {
|
|||
text: qsTr("OpenVPN Settings")
|
||||
}
|
||||
|
||||
Flickable {
|
||||
FlickableType {
|
||||
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
|
||||
enabled: logic.pageEnabled
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: GC.defaultMargin - 1
|
||||
|
||||
ColumnLayout {
|
||||
visible: !logic.isThirdPartyConfig
|
||||
enabled: logic.pageEnabled
|
||||
|
||||
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
|
||||
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: parent.width
|
||||
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: {
|
||||
logic.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"
|
||||
|
||||
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 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
|
||||
}
|
||||
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"
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
60
client/ui/qml/Pages/Protocols/PageProtoWireGuard.qml
Normal file
60
client/ui/qml/Pages/Protocols/PageProtoWireGuard.qml
Normal file
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -18,20 +18,10 @@ PageShareProtocolBase {
|
|||
text: qsTr("Share for Amnezia")
|
||||
}
|
||||
|
||||
Flickable {
|
||||
FlickableType {
|
||||
id: fl
|
||||
width: root.width
|
||||
anchors.top: caption.bottom
|
||||
anchors.topMargin: 20
|
||||
anchors.bottom: root.bottom
|
||||
anchors.bottomMargin: 20
|
||||
anchors.left: root.left
|
||||
anchors.leftMargin: 30
|
||||
anchors.right: root.right
|
||||
anchors.rightMargin: 30
|
||||
|
||||
contentHeight: content.height + 20
|
||||
clip: true
|
||||
|
||||
Behavior on contentY{
|
||||
NumberAnimation {
|
||||
|
@ -46,6 +36,7 @@ PageShareProtocolBase {
|
|||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 15
|
||||
|
||||
Text {
|
||||
id: lb_desc
|
||||
|
@ -112,7 +103,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
|
||||
|
||||
|
|
|
@ -18,20 +18,10 @@ PageShareProtocolBase {
|
|||
text: qsTr("Share Cloak Settings")
|
||||
}
|
||||
|
||||
Flickable {
|
||||
FlickableType {
|
||||
id: fl
|
||||
width: root.width
|
||||
anchors.top: caption.bottom
|
||||
anchors.topMargin: 20
|
||||
anchors.bottom: root.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
|
||||
|
@ -39,7 +29,7 @@ PageShareProtocolBase {
|
|||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.rightMargin: 15
|
||||
|
||||
LabelType {
|
||||
id: lb_desc
|
||||
|
@ -94,7 +84,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
|
||||
|
||||
|
|
|
@ -42,20 +42,10 @@ PageShareProtocolBase {
|
|||
visible: false
|
||||
}
|
||||
|
||||
Flickable {
|
||||
FlickableType {
|
||||
id: fl
|
||||
width: root.width
|
||||
anchors.top: caption.bottom
|
||||
anchors.topMargin: 20
|
||||
anchors.bottom: root.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
|
||||
|
@ -63,6 +53,7 @@ PageShareProtocolBase {
|
|||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 15
|
||||
|
||||
// LabelType {
|
||||
// id: lb_desc
|
||||
|
|
|
@ -18,20 +18,10 @@ PageShareProtocolBase {
|
|||
text: qsTr("Share OpenVPN Settings")
|
||||
}
|
||||
|
||||
Flickable {
|
||||
FlickableType {
|
||||
id: fl
|
||||
width: root.width
|
||||
anchors.top: caption.bottom
|
||||
anchors.topMargin: 20
|
||||
anchors.bottom: root.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
|
||||
|
@ -39,7 +29,7 @@ PageShareProtocolBase {
|
|||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.rightMargin: 15
|
||||
|
||||
LabelType {
|
||||
id: lb_desc
|
||||
|
@ -93,7 +83,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
|
||||
|
||||
|
|
|
@ -18,20 +18,10 @@ PageShareProtocolBase {
|
|||
text: qsTr("Share ShadowSocks Settings")
|
||||
}
|
||||
|
||||
Flickable {
|
||||
FlickableType {
|
||||
id: fl
|
||||
width: root.width
|
||||
anchors.top: caption.bottom
|
||||
anchors.topMargin: 20
|
||||
anchors.bottom: root.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
|
||||
|
@ -39,6 +29,7 @@ PageShareProtocolBase {
|
|||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 15
|
||||
|
||||
LabelType {
|
||||
id: lb_desc
|
||||
|
|
|
@ -18,20 +18,10 @@ PageShareProtocolBase {
|
|||
text: qsTr("Share WireGuard Settings")
|
||||
}
|
||||
|
||||
Flickable {
|
||||
FlickableType {
|
||||
id: fl
|
||||
width: root.width
|
||||
anchors.top: caption.bottom
|
||||
anchors.topMargin: 20
|
||||
anchors.bottom: root.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
|
||||
|
@ -39,6 +29,7 @@ PageShareProtocolBase {
|
|||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 15
|
||||
|
||||
LabelType {
|
||||
id: lb_desc
|
||||
|
@ -91,7 +82,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
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue