mirror of
https://github.com/Qv2ray/Qv2ray.git
synced 2025-05-20 10:50:23 +08:00
commit
56a6dc36bc
117
.github/workflows/Linux.yml
vendored
117
.github/workflows/Linux.yml
vendored
@ -1,117 +0,0 @@
|
||||
name: Qv2ray Push Build for Linux
|
||||
|
||||
on:
|
||||
push:
|
||||
release:
|
||||
types: [prereleased]
|
||||
|
||||
jobs:
|
||||
# =================================================================================== LINUX
|
||||
Linux:
|
||||
name: Push build for Linux
|
||||
runs-on: ubuntu-16.04
|
||||
steps:
|
||||
- name: Checking out sources
|
||||
uses: actions/checkout@master
|
||||
- name: Restoring submodules
|
||||
run: git submodule update --init
|
||||
# --------------------------------------------------------
|
||||
- name: Installing Qt
|
||||
uses: jurplel/install-qt-action@v2
|
||||
with:
|
||||
modules: qtcharts
|
||||
# --------------------------------------------------------
|
||||
- name: Install Packages
|
||||
run: |
|
||||
sudo add-apt-repository ppa:webispy/grpc
|
||||
sudo add-apt-repository ppa:carsten-uppenbrink-net/openssl
|
||||
sudo apt update
|
||||
sudo apt install -y libgl-dev openssl libx11-dev libxkbcommon-x11-dev libgrpc++-dev libprotobuf-dev protobuf-compiler protobuf-c-compiler protobuf-compiler-grpc
|
||||
# --------------------------------------------------------
|
||||
- name: Extracting gRPC and protobuf libs and headers
|
||||
run: tools/grpc_gen.sh
|
||||
# --------------------------------------------------------
|
||||
- name: Download libqvb static library.
|
||||
uses: Legion2/download-release-action@v2.1.0
|
||||
with:
|
||||
repository: Qv2ray/QvRPCBridge
|
||||
tag: 'latest'
|
||||
path: libs
|
||||
file: libqvb-linux64.a
|
||||
# -------------------------------------------------------- libqvb build
|
||||
- name: libqvb - Build Qv2ray
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
qmake .. CONFIG+=no_increase_build_number CONFIG+=with_new_backend PREFIX=/usr
|
||||
make -j2
|
||||
- name: libqvb - Generating filesystem structure for AppImage
|
||||
run: |
|
||||
cd build
|
||||
make install INSTALL_ROOT=AppDir
|
||||
cd AppDir
|
||||
mkdir -p ./usr/lib/
|
||||
cp /usr/lib/x86_64-linux-gnu/libssl.so.1.1 /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 ./usr/lib/
|
||||
- name: libqvb - Building AppImage using linuxdeployqt
|
||||
run: |
|
||||
cd build
|
||||
wget https://github.com/probonopd/linuxdeployqt/releases/download/6/linuxdeployqt-6-x86_64.AppImage
|
||||
chmod +x ./linuxdeployqt-6-x86_64.AppImage
|
||||
./linuxdeployqt-6-x86_64.AppImage --appimage-extract
|
||||
cd AppDir
|
||||
../squashfs-root/AppRun usr/share/applications/qv2ray.desktop -appimage -no-strip -always-overwrite
|
||||
mv ./Qv2ray*.AppImage ./Qv2ray.AppImage
|
||||
- name: libqvb - Uploading artifact
|
||||
uses: actions/upload-artifact@master
|
||||
with:
|
||||
name: Qv2ray.linux-libqvb.AppImage
|
||||
path: build/AppDir/Qv2ray.AppImage
|
||||
- name: libqvb - Upload binaries to release
|
||||
if: github.event_name == 'release'
|
||||
uses: svenstaro/upload-release-action@v1-release
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: build/AppDir/Qv2ray.AppImage
|
||||
asset_name: Qv2ray-${{ github.ref }}-linux-libqvb.AppImage
|
||||
tag: ${{ github.ref }}
|
||||
overwrite: true
|
||||
# -------------------------------------------------------- cleanup build
|
||||
- name: cleanup -- clean up build directory to build against legacy gRPC
|
||||
run: rm -rvf ./build
|
||||
# -------------------------------------------------------- gRPC build
|
||||
- name: gRPC - Build Qv2ray
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
qmake .. CONFIG+=no_increase_build_number PREFIX=/usr
|
||||
make -j2
|
||||
- name: gRPC - Generating filesystem structure for AppImage
|
||||
run: |
|
||||
cd build
|
||||
make install INSTALL_ROOT=AppDir
|
||||
cd AppDir
|
||||
mkdir -p ./usr/lib/
|
||||
cp /usr/lib/x86_64-linux-gnu/libssl.so.1.1 /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 ./usr/lib/
|
||||
- name: gRPC - Building AppImage using linuxdeployqt
|
||||
run: |
|
||||
cd build
|
||||
wget https://github.com/probonopd/linuxdeployqt/releases/download/6/linuxdeployqt-6-x86_64.AppImage
|
||||
chmod +x ./linuxdeployqt-6-x86_64.AppImage
|
||||
./linuxdeployqt-6-x86_64.AppImage --appimage-extract
|
||||
cd AppDir
|
||||
../squashfs-root/AppRun usr/share/applications/qv2ray.desktop -appimage -no-strip -always-overwrite
|
||||
mv ./Qv2ray*.AppImage ./Qv2ray.AppImage
|
||||
- name: gRPC - Uploading artifact
|
||||
uses: actions/upload-artifact@master
|
||||
with:
|
||||
name: Qv2ray.linux-gRPC.AppImage
|
||||
path: build/AppDir/Qv2ray.AppImage
|
||||
- name: gRPC - Upload binaries to release
|
||||
if: github.event_name == 'release'
|
||||
uses: svenstaro/upload-release-action@v1-release
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: build/AppDir/Qv2ray.AppImage
|
||||
asset_name: Qv2ray-${{ github.ref }}-linux.gRPC.AppImage
|
||||
tag: ${{ github.ref }}
|
||||
overwrite: true
|
111
.github/workflows/Windows.yml
vendored
111
.github/workflows/Windows.yml
vendored
@ -1,111 +0,0 @@
|
||||
name: Qv2ray Push Build for Windows
|
||||
|
||||
on:
|
||||
push:
|
||||
release:
|
||||
types: [prereleased]
|
||||
|
||||
jobs:
|
||||
# ======================================================================================== WINDOWS
|
||||
Windows:
|
||||
name: Push build for win64
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checking out Qv2ray sources
|
||||
uses: actions/checkout@master
|
||||
- name: Restoring submodules
|
||||
run: git submodule update --init
|
||||
# --------------------------------------------------------
|
||||
- name: Installing Qt
|
||||
uses: jurplel/install-qt-action@v2
|
||||
with:
|
||||
arch: win64_mingw73
|
||||
modules: qtcharts
|
||||
# --------------------------------------------------------
|
||||
- name: Extracting gRPC and protobuf libs and headers
|
||||
run: tools\grpc_gen.bat
|
||||
# --------------------------------------------------------
|
||||
- name: Download libqvb static library.
|
||||
run: curl -o .\libs\libqvb-win64.a -L https://github.com/Qv2ray/QvRPCBridge/releases/download/v1.1/libqvb-win64.a
|
||||
# -------------------------------------------------------- libqvb
|
||||
- name: libqvb - Building Qv2ray
|
||||
shell: cmd
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
qmake .. CONFIG+=no_increase_build_number CONFIG+=with_new_backend
|
||||
mingw32-make -j2
|
||||
- name: libqvb - Copying DLLs to build output folders
|
||||
shell: cmd
|
||||
run: .github\workflows\copy_DLLs.bat
|
||||
- name: libqvb - Deploy Qt
|
||||
shell: cmd
|
||||
run: |
|
||||
cd build
|
||||
cd release
|
||||
del *.cpp *.h *.o *.qrc *.qm
|
||||
windeployqt ./qv2ray.exe --compiler-runtime
|
||||
- name: libqvb - Create 7z Release
|
||||
uses: DuckSoft/create-7z-action@v1.0
|
||||
with:
|
||||
# file/folder path to compress
|
||||
pathSource: ./build/release/
|
||||
# 7z archive path to write
|
||||
pathTarget: ./release.7z
|
||||
- name: libqvb - Uploading artifact
|
||||
uses: actions/upload-artifact@master
|
||||
with:
|
||||
name: Qv2ray.Win64-libqvb.7z
|
||||
path: release.7z
|
||||
- name: libqvb - Upload binaries to release
|
||||
uses: svenstaro/upload-release-action@v1-release
|
||||
if: github.event_name == 'release'
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: release.7z
|
||||
asset_name: Qv2ray-${{ github.ref }}-win64-libqvb.7z
|
||||
tag: ${{ github.ref }}
|
||||
overwrite: true
|
||||
# -------------------------------------------------------- cleanup
|
||||
- name: cleanup -- clean up build directory to build against legacy gRPC
|
||||
shell: cmd
|
||||
run: echo y|rmdir /S build
|
||||
# -------------------------------------------------------- gRPC
|
||||
- name: gRPC - Building Qv2ray
|
||||
shell: cmd
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
qmake .. CONFIG+=no_increase_build_number CONFIG+=with_new_backend
|
||||
mingw32-make -j2
|
||||
- name: gRPC - Copying DLLs to build output folders
|
||||
shell: cmd
|
||||
run: .github\workflows\copy_DLLs.bat
|
||||
- name: gRPC - Deploy Qt
|
||||
shell: cmd
|
||||
run: |
|
||||
cd build
|
||||
cd release
|
||||
del *.cpp *.h *.o *.qrc *.qm
|
||||
windeployqt ./qv2ray.exe --compiler-runtime
|
||||
- name: gRPC - Create 7z Release
|
||||
uses: DuckSoft/create-7z-action@v1.0
|
||||
with:
|
||||
# file/folder path to compress
|
||||
pathSource: ./build/release/
|
||||
# 7z archive path to write
|
||||
pathTarget: ./release.7z
|
||||
- name: gRPC - Uploading artifact
|
||||
uses: actions/upload-artifact@master
|
||||
with:
|
||||
name: Qv2ray.Win64-gRPC.7z
|
||||
path: release.7z
|
||||
- name: gRPC - Upload binaries to release
|
||||
uses: svenstaro/upload-release-action@v1-release
|
||||
if: github.event_name == 'release'
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: release.7z
|
||||
asset_name: Qv2ray-${{ github.ref }}-win64-gRPC.7z
|
||||
tag: ${{ github.ref }}
|
||||
overwrite: true
|
19
.github/workflows/aur.yml
vendored
Normal file
19
.github/workflows/aur.yml
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
name: AUR Test Build
|
||||
on:
|
||||
push:
|
||||
paths-ignore: [README.md]
|
||||
release: {}
|
||||
schedule:
|
||||
- cron: "0 19 * * *"
|
||||
|
||||
jobs:
|
||||
aur:
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
repo: [qv2ray, qv2ray-dev-git]
|
||||
runs-on: [ubuntu-latest]
|
||||
steps:
|
||||
- uses: DuckSoft/build-aur-action@v1.0
|
||||
with:
|
||||
repo-name: ${{ matrix.repo }}
|
178
.github/workflows/build-qv2ray.yml
vendored
Normal file
178
.github/workflows/build-qv2ray.yml
vendored
Normal file
@ -0,0 +1,178 @@
|
||||
name: Qv2ray build matrix
|
||||
|
||||
on:
|
||||
push:
|
||||
release:
|
||||
types: [prereleased]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
qt_version: [5.12.6, 5.13.2, 5.14.1]
|
||||
platform: [ubuntu-16.04, macos-latest, windows-latest]
|
||||
arch: [x86, x64]
|
||||
exclude:
|
||||
- platform: ubuntu-16.04
|
||||
arch: x86
|
||||
- platform: macos-latest
|
||||
arch: x86
|
||||
fail-fast: false
|
||||
|
||||
runs-on: ${{ matrix.platform }}
|
||||
|
||||
steps:
|
||||
- name: Get the version
|
||||
id: get_version
|
||||
shell: bash
|
||||
run: echo ::set-output name=VERSION::$(echo $GITHUB_REF | cut -d / -f 3)
|
||||
- name: Checking out sources
|
||||
uses: actions/checkout@master
|
||||
- name: Restoring submodules
|
||||
run: git submodule update --init
|
||||
# --------------------------------------------------------
|
||||
- name: Install MSVC compiler
|
||||
if: matrix.platform == 'windows-latest'
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
with:
|
||||
# 14.1 is for vs2017, 14.2 is vs2019, following the upstream vcpkg build from Qv2ray-deps repo
|
||||
toolset: 14.2
|
||||
arch: ${{ matrix.arch }}
|
||||
- name: Installing Qt - ${{ matrix.arch }}
|
||||
uses: Qv2ray/install-qt-action@master
|
||||
with:
|
||||
version: ${{ matrix.qt_version }}
|
||||
arch: ${{ matrix.arch }}
|
||||
# --------------------------------------------------------
|
||||
- name: Linux - Install Packages
|
||||
if: matrix.platform == 'ubuntu-16.04'
|
||||
run: |
|
||||
sudo add-apt-repository ppa:ubuntu-toolchain-r/test
|
||||
sudo add-apt-repository ppa:webispy/grpc
|
||||
sudo add-apt-repository ppa:carsten-uppenbrink-net/openssl
|
||||
sudo apt update
|
||||
sudo apt install -y gcc-7 libgl-dev openssl libx11-dev libxkbcommon-x11-dev libprotobuf-dev protobuf-compiler protobuf-c-compiler
|
||||
|
||||
- name: macOS - Install Packages
|
||||
if: matrix.platform == 'macos-latest'
|
||||
run: brew install protobuf
|
||||
# --------------------------------------------------------
|
||||
- name: Cross-platform - Download libraries
|
||||
shell: bash
|
||||
run: |
|
||||
curl -o ./libs/libqvb-linux64.a -L https://github.com/Qv2ray/QvRPCBridge/releases/download/v1.1/libqvb-linux64.a
|
||||
curl -o ./libs/libqvb-darwin.a -L https://github.com/Qv2ray/QvRPCBridge/releases/download/v1.1/libqvb-darwin.a
|
||||
curl -o ./libs/Qv2ray-deps-${{ matrix.arch }}-windows.7z -L https://github.com/Qv2ray/Qv2ray-deps/releases/download/release/Qv2ray-deps-${{ matrix.arch }}-windows.7z
|
||||
- name: Win-${{ matrix.arch }} - ${{ matrix.qt_version }} - Extract Windows Dependencies
|
||||
if: matrix.platform == 'windows-latest'
|
||||
uses: DuckSoft/extract-7z-action@v1.0
|
||||
with:
|
||||
pathSource: ./libs/Qv2ray-deps-${{ matrix.arch }}-windows.7z
|
||||
pathTarget: ./libs
|
||||
# -------------------------------------------------------- Generate MakeFile
|
||||
- name: Cross-platform - ${{ matrix.qt_version }} - Generate Dependencies and Makefile
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
export _QV2RAY_BUILD_INFO_="Qv2ray built from Github Action"
|
||||
export _QV2RAY_BUILD_EXTRA_INFO_="qt${{ matrix.qt_version }}-${{ github.sha }}"
|
||||
qmake .. CONFIG+=no_increase_build_number PREFIX=/usr
|
||||
# -------------------------------------------------------- Build
|
||||
- name: Unix - ${{ matrix.qt_version }} - Build Qv2ray
|
||||
if: matrix.platform != 'windows-latest'
|
||||
run: |
|
||||
cd build
|
||||
make -j2 $([ "${{ matrix.platform }}" == "ubuntu-16.04" ] && echo "CC=gcc-7 CXX=g++-7" LINK="g++-7" || echo "")
|
||||
- name: Win-${{ matrix.arch }} - ${{ matrix.qt_version }} - Build Qv2ray
|
||||
if: matrix.platform == 'windows-latest'
|
||||
run: |
|
||||
cd build
|
||||
nmake
|
||||
# -------------------------------------------------------- Deployments
|
||||
- name: Linux - ${{ matrix.qt_version }} - Generating AppImage
|
||||
if: matrix.platform == 'ubuntu-16.04'
|
||||
run: |
|
||||
cd build
|
||||
wget https://github.com/probonopd/linuxdeployqt/releases/download/6/linuxdeployqt-6-x86_64.AppImage
|
||||
chmod +x ./linuxdeployqt-6-x86_64.AppImage
|
||||
./linuxdeployqt-6-x86_64.AppImage --appimage-extract
|
||||
make install INSTALL_ROOT=AppDir
|
||||
cd AppDir
|
||||
mkdir -p ./usr/lib/
|
||||
cp /usr/lib/x86_64-linux-gnu/libssl.so.1.1 /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 ./usr/lib/
|
||||
../squashfs-root/AppRun usr/share/applications/qv2ray.desktop -appimage -no-strip -always-overwrite
|
||||
mv ./Qv2ray*.AppImage ./Qv2ray.AppImage
|
||||
- name: Linux - ${{ matrix.qt_version }} - Uploading artifact
|
||||
if: matrix.platform == 'ubuntu-16.04'
|
||||
uses: actions/upload-artifact@master
|
||||
with:
|
||||
name: Qv2ray-${{ github.sha }}.linux-${{ matrix.arch }}.qt${{ matrix.qt_version }}.AppImage
|
||||
path: build/AppDir/Qv2ray.AppImage
|
||||
- name: Linux - ${{ matrix.qt_version }} - Upload binaries to release
|
||||
uses: svenstaro/upload-release-action@v1-release
|
||||
if: github.event_name == 'release' && matrix.platform == 'ubuntu-16.04'
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: build/AppDir/Qv2ray.AppImage
|
||||
asset_name: Qv2ray.${{ steps.get_version.outputs.VERSION }}.linux-${{ matrix.arch }}.qt${{ matrix.qt_version }}.AppImage
|
||||
tag: ${{ github.ref }}
|
||||
overwrite: true
|
||||
# ==
|
||||
- name: macOS - ${{ matrix.qt_version }} - Making release tarball
|
||||
if: matrix.platform == 'macos-latest'
|
||||
run: |
|
||||
cd build
|
||||
cd qv2ray.app
|
||||
macdeployqt ./
|
||||
cd ..
|
||||
tar czf Qv2ray.app.tar.gz qv2ray.app
|
||||
- name: macOS - ${{ matrix.qt_version }} - Uploading Artifact
|
||||
if: matrix.platform == 'macos-latest'
|
||||
uses: actions/upload-artifact@master
|
||||
with:
|
||||
name: Qv2ray-${{ github.sha }}.macOS-${{ matrix.arch }}.qt${{ matrix.qt_version }}.zip
|
||||
path: build/Qv2ray.app.tar.gz
|
||||
- name: macOS - ${{ matrix.qt_version }} - Upload binaries to release
|
||||
uses: svenstaro/upload-release-action@v1-release
|
||||
if: github.event_name == 'release' && matrix.platform == 'macos-latest'
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: build/Qv2ray.app.tar.gz
|
||||
asset_name: Qv2ray.${{ steps.get_version.outputs.VERSION }}.macOS-${{ matrix.arch }}.qt${{ matrix.qt_version }}.tar.gz
|
||||
tag: ${{ github.ref }}
|
||||
overwrite: true
|
||||
# ==
|
||||
- name: Win-${{ matrix.arch }} - ${{ matrix.qt_version }} - Copying DLLs to build output folders
|
||||
if: matrix.platform == 'windows-latest'
|
||||
run: .\.github\workflows\copy_DLLs.bat
|
||||
- name: Win-${{ matrix.arch }} - ${{ matrix.qt_version }} - Running windeployqt
|
||||
if: matrix.platform == 'windows-latest'
|
||||
shell: cmd
|
||||
run: |
|
||||
cd build
|
||||
cd release
|
||||
del *.cpp *.h *.o *.qrc *.qm *.hpp *.obj
|
||||
set VCINSTALLDIR=C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\
|
||||
windeployqt ./qv2ray.exe --compiler-runtime --verbose 2
|
||||
- name: Win-${{ matrix.arch }} - ${{ matrix.qt_version }} - Create 7z Release
|
||||
if: matrix.platform == 'windows-latest'
|
||||
uses: DuckSoft/create-7z-action@v1.0
|
||||
with:
|
||||
pathSource: ./build/release/
|
||||
pathTarget: ./release.7z
|
||||
- name: Win-${{ matrix.arch }} - ${{ matrix.qt_version }} - Uploading artifact
|
||||
if: matrix.platform == 'windows-latest'
|
||||
uses: actions/upload-artifact@master
|
||||
with:
|
||||
name: Qv2ray-${{ github.sha }}.Windows-${{ matrix.arch }}.qt${{ matrix.qt_version }}.7z
|
||||
path: release.7z
|
||||
- name: Win-${{ matrix.arch }} - ${{ matrix.qt_version }} - Upload binaries to release
|
||||
uses: svenstaro/upload-release-action@v1-release
|
||||
if: github.event_name == 'release' && matrix.platform == 'windows-latest'
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: release.7z
|
||||
asset_name: Qv2ray.${{ steps.get_version.outputs.VERSION }}.Windows-${{ matrix.arch }}.qt${{ matrix.qt_version }}.7z
|
||||
tag: ${{ github.ref }}
|
||||
overwrite: true
|
6
.github/workflows/copy_DLLs.bat
vendored
6
.github/workflows/copy_DLLs.bat
vendored
@ -1,5 +1,5 @@
|
||||
@echo off
|
||||
echo Copying Windows Necessary DLLs
|
||||
forfiles /s /p %GITHUB_WORKSPACE%\libs\ /m "*.dll" /c "cmd.exe /c copy @file %GITHUB_WORKSPACE%\build\release\"
|
||||
forfiles /s /p %GITHUB_WORKSPACE%\libs\ /m "*.dll" /c "cmd.exe /c copy @file %GITHUB_WORKSPACE%\build\debug\"
|
||||
echo Copying DLLs for Windows
|
||||
forfiles /s /p %GITHUB_WORKSPACE%\libs\x86-windows\ /m "*.dll" /c "cmd.exe /c copy @file %GITHUB_WORKSPACE%\build\release\"
|
||||
forfiles /s /p %GITHUB_WORKSPACE%\libs\x64-windows\ /m "*.dll" /c "cmd.exe /c copy @file %GITHUB_WORKSPACE%\build\release\"
|
||||
exit 0
|
||||
|
87
.github/workflows/macOS.yml
vendored
87
.github/workflows/macOS.yml
vendored
@ -1,87 +0,0 @@
|
||||
name: Qv2ray Push Build for macOS
|
||||
|
||||
on:
|
||||
push:
|
||||
release:
|
||||
types: [prereleased]
|
||||
|
||||
jobs:
|
||||
macOS:
|
||||
name: Push build for macOS
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Checking out sources
|
||||
uses: actions/checkout@master
|
||||
- name: Restoring submodules
|
||||
run: git submodule update --init
|
||||
# --------------------------------------------------------
|
||||
- name: Installing Qt
|
||||
uses: jurplel/install-qt-action@v2
|
||||
with:
|
||||
modules: qtcharts
|
||||
# --------------------------------------------------------
|
||||
- name: Extracting gRPC and protobuf libs and headers
|
||||
run: tools/deps_macOS.sh
|
||||
# --------------------------------------------------------
|
||||
- name: Download libqvb static library.
|
||||
run: curl -o ./libs/libqvb-darwin.a -L https://github.com/Qv2ray/QvRPCBridge/releases/download/v1.1/libqvb-darwin.a
|
||||
# -------------------------------------------------------- == build against libqvb
|
||||
- name: libqvb - Build Qv2ray
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
qmake .. CONFIG+=no_increase_build_number CONFIG+=with_new_backend
|
||||
make -j2
|
||||
- name: libqvb - Making release tarball
|
||||
run: |
|
||||
cd build
|
||||
cd qv2ray.app
|
||||
macdeployqt ./
|
||||
cd ..
|
||||
tar czf Qv2ray.app.tar.gz qv2ray.app
|
||||
- name: libqvb - Uploading Artifact
|
||||
uses: actions/upload-artifact@master
|
||||
with:
|
||||
name: Qv2ray.macOS.app-libqvb.zip
|
||||
path: build/Qv2ray.app.tar.gz
|
||||
- name: libqvb - Upload binaries to release
|
||||
uses: svenstaro/upload-release-action@v1-release
|
||||
if: github.event_name == 'release'
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: build/Qv2ray.app.tar.gz
|
||||
asset_name: Qv2ray-${{ github.ref }}-macOS-libqvb.tar.gz
|
||||
tag: ${{ github.ref }}
|
||||
overwrite: true
|
||||
# -------------------------------------------------------- == cleanup
|
||||
- name: cleanup -- clean up build directory to build against legacy gRPC
|
||||
run: rm -rvf ./build
|
||||
# -------------------------------------------------------- == build against gRPC
|
||||
- name: gRPC - Build Qv2ray
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
qmake .. CONFIG+=no_increase_build_number
|
||||
make -j2
|
||||
- name: gRPC - Making release tarball
|
||||
run: |
|
||||
cd build
|
||||
cd qv2ray.app
|
||||
macdeployqt ./
|
||||
cd ..
|
||||
tar czf Qv2ray.app.tar.gz qv2ray.app
|
||||
- name: gRPC - Uploading Artifact
|
||||
uses: actions/upload-artifact@master
|
||||
with:
|
||||
name: Qv2ray.macOS.app-gRPC.zip
|
||||
path: build/Qv2ray.app.tar.gz
|
||||
- name: gRPC - Upload binaries to release
|
||||
uses: svenstaro/upload-release-action@v1-release
|
||||
if: github.event_name == 'release'
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: build/Qv2ray.app.tar.gz
|
||||
asset_name: Qv2ray-${{ github.ref }}-macOS-gRPC.tar.gz
|
||||
tag: ${{ github.ref }}
|
||||
overwrite: true
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -7,6 +7,8 @@
|
||||
SourceTrail/
|
||||
|
||||
libs/gen/
|
||||
libs/x64-windows/
|
||||
libs/x86-windows/
|
||||
libs/libqvb-*
|
||||
|
||||
build/
|
||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -13,9 +13,6 @@
|
||||
[submodule "3rdparty/qzxing"]
|
||||
path = 3rdparty/qzxing
|
||||
url = https://github.com/ftylitak/qzxing
|
||||
[submodule "libs/gRPC-win32"]
|
||||
path = libs/gRPC-win32
|
||||
url = https://github.com/Qv2ray/gRPC-Windows-Packages
|
||||
[submodule "libs/libqvb"]
|
||||
path = libs/libqvb
|
||||
url = https://github.com/Qv2ray/QvRPCBridge
|
||||
|
37
.travis.yml
Normal file
37
.travis.yml
Normal file
@ -0,0 +1,37 @@
|
||||
language: bash
|
||||
dist: bionic
|
||||
|
||||
git:
|
||||
depth: false
|
||||
|
||||
branches:
|
||||
only:
|
||||
- dev
|
||||
|
||||
env:
|
||||
global:
|
||||
- LC_ALL: C.UTF-8
|
||||
- LANG: C.UTF-8
|
||||
|
||||
addons:
|
||||
snaps:
|
||||
- name: snapcraft
|
||||
channel: stable
|
||||
confinement: classic
|
||||
- name: lxd
|
||||
channel: stable
|
||||
|
||||
script:
|
||||
- sudo /snap/bin/lxd.migrate -yes
|
||||
- sudo /snap/bin/lxd waitready
|
||||
- sudo /snap/bin/lxd init --auto
|
||||
- sudo snapcraft --use-lxd
|
||||
|
||||
after_failure:
|
||||
- sudo journalctl -u snapd
|
||||
|
||||
deploy:
|
||||
provider: snap
|
||||
snap: qv2ray_*.snap
|
||||
channel: edge
|
||||
skip_cleanup: true
|
2
3rdparty/SingleApplication
vendored
2
3rdparty/SingleApplication
vendored
@ -1 +1 @@
|
||||
Subproject commit 3d152b03d872b99d7f36e8463ac253e41194a17a
|
||||
Subproject commit 0f6695e2a9d8fdaa336e7ad941855c46c61f218a
|
2
3rdparty/qzxing
vendored
2
3rdparty/qzxing
vendored
@ -1 +1 @@
|
||||
Subproject commit 2a58c5032b2180f2cce95e1db106cbaa4ecaed02
|
||||
Subproject commit f0a78867d697fd271528007c7b4c67e8fce75f90
|
2
3rdparty/x2struct
vendored
2
3rdparty/x2struct
vendored
@ -1 +1 @@
|
||||
Subproject commit 09d790b83155336acb73730ac241fdc4256a85bd
|
||||
Subproject commit 4539764671509655370b2ff4fae90bfd8a12df40
|
@ -1 +0,0 @@
|
||||
3100
|
419
Qv2ray.pro
419
Qv2ray.pro
@ -4,140 +4,17 @@
|
||||
#
|
||||
#-------------------------------------------------
|
||||
|
||||
QT += core gui widgets network charts
|
||||
QT += core gui widgets network
|
||||
|
||||
TARGET = qv2ray
|
||||
TEMPLATE = app
|
||||
|
||||
# Now read build number file.
|
||||
_BUILD_NUMBER=$$cat(Build.Counter)
|
||||
VERSION = 2.0.1.$$_BUILD_NUMBER
|
||||
QV2RAY_VERSION=$$cat($$PWD/makespec/VERSION)
|
||||
QV2RAY_BUILD_VERSION=$$cat($$PWD/makespec/BUILDVERSION)
|
||||
|
||||
no_increase_build_number {
|
||||
message("Build.Counter will not be increased")
|
||||
} else {
|
||||
_BUILD_NUMBER = $$num_add($$_BUILD_NUMBER, 1)
|
||||
write_file("Build.Counter", _BUILD_NUMBER)
|
||||
}
|
||||
VERSION = $${QV2RAY_VERSION}.$${QV2RAY_BUILD_VERSION}
|
||||
|
||||
# Unix (Actually Linux only) prefix config.
|
||||
isEmpty(PREFIX) {
|
||||
PREFIX=/usr/local
|
||||
}
|
||||
message("Qv2ray installation PREFIX="$$PREFIX)
|
||||
|
||||
DEFINES += QT_DEPRECATED_WARNINGS QV2RAY_VERSION_STRING=\"\\\"v$${VERSION}\\\"\" QAPPLICATION_CLASS=QApplication
|
||||
|
||||
# Don't merge those configs with below.
|
||||
CONFIG += enable_decoder_qr_code enable_encoder_qr_code qt c++11 openssl-linked
|
||||
|
||||
include(3rdparty/qzxing/src/QZXing-components.pri)
|
||||
include(3rdparty/SingleApplication/singleapplication.pri)
|
||||
include(3rdparty/QNodeEditor/QNodeEditor.pri)
|
||||
include(3rdparty/x2struct/x2struct.pri)
|
||||
|
||||
# Main config
|
||||
CONFIG += lrelease embed_translations
|
||||
|
||||
# Win32 support.
|
||||
win32:CONFIG += win
|
||||
win64:CONFIG += win
|
||||
|
||||
SOURCES += \
|
||||
src/components/QvComponentsHandler.cpp \
|
||||
src/components/QvCore/QvCommandLineArgs.cpp \
|
||||
src/components/QvKernelInteractions.cpp \
|
||||
src/components/QvLaunchAtLoginConfigurator.cpp \
|
||||
src/components/QvPACHandler.cpp \
|
||||
src/components/QvSystemProxyConfigurator.cpp \
|
||||
src/components/QvTCPing.cpp \
|
||||
src/main.cpp \
|
||||
src/components/QvGFWPACConverter.cpp \
|
||||
src/components/QvHTTPRequestHelper.cpp \
|
||||
src/components/QvLogHighlighter.cpp \
|
||||
src/QvCoreConfigOperations.cpp \
|
||||
src/QvConfigUpgrade.cpp \
|
||||
src/QvCoreConfigOperations_Convertion.cpp \
|
||||
src/QvCoreConfigOperations_Generation.cpp \
|
||||
src/QvUtils.cpp \
|
||||
src/ui/routeNodeModels/QvInboundNodeModel.cpp \
|
||||
src/ui/routeNodeModels/QvOutboundNodeModel.cpp \
|
||||
src/ui/routeNodeModels/QvRuleNodeModel.cpp \
|
||||
src/ui/w_MainWindow_extra.cpp \
|
||||
src/ui/w_PreferencesWindow.cpp \
|
||||
src/ui/w_RoutesEditor_extra.cpp \
|
||||
src/utils/QvHelpers.cpp \
|
||||
src/utils/QJsonModel.cpp \
|
||||
src/ui/w_ExportConfig.cpp \
|
||||
src/ui/w_InboundEditor.cpp \
|
||||
src/ui/w_OutboundEditor.cpp \
|
||||
src/ui/w_RoutesEditor.cpp \
|
||||
src/ui/w_SubscriptionEditor.cpp \
|
||||
src/ui/w_JsonEditor.cpp \
|
||||
src/ui/w_MainWindow.cpp \
|
||||
src/ui/w_ImportConfig.cpp \
|
||||
src/ui/w_ScreenShot_Core.cpp \
|
||||
src/ui/NetSpeedBar/QvNetSpeedBar.cpp
|
||||
|
||||
INCLUDEPATH += \
|
||||
3rdparty/ \
|
||||
src/ \
|
||||
src/components \
|
||||
src/ui/ \
|
||||
src/utils/ \
|
||||
libs/gen/
|
||||
|
||||
HEADERS += \
|
||||
src/Qv2rayBase.hpp \
|
||||
src/Qv2rayFeatures.hpp \
|
||||
src/QvCoreConfigObjects.hpp \
|
||||
src/QvCoreConfigOperations.hpp \
|
||||
src/QvUtils.hpp \
|
||||
src/components/QvComponentsHandler.hpp \
|
||||
src/components/QvCore/QvCommandLineArgs.hpp \
|
||||
src/components/QvHTTPRequestHelper.hpp \
|
||||
src/components/QvKernelInteractions.hpp \
|
||||
src/components/QvLaunchAtLoginConfigurator.hpp \
|
||||
src/components/QvLogHighlighter.hpp \
|
||||
src/components/QvNetSpeedPlugin.hpp \
|
||||
src/components/QvPACHandler.hpp \
|
||||
src/components/QvSystemProxyConfigurator.hpp \
|
||||
src/components/QvTCPing.hpp \
|
||||
src/ui/routeNodeModels/QvInboundNodeModel.hpp \
|
||||
src/ui/routeNodeModels/QvNodeModelsBase.hpp \
|
||||
src/ui/routeNodeModels/QvOutboundNodeModel.hpp \
|
||||
src/ui/routeNodeModels/QvRuleNodeModel.hpp \
|
||||
src/ui/w_ExportConfig.hpp \
|
||||
src/ui/w_ImportConfig.hpp \
|
||||
src/ui/w_InboundEditor.hpp \
|
||||
src/ui/w_JsonEditor.hpp \
|
||||
src/ui/w_MainWindow.hpp \
|
||||
src/ui/w_OutboundEditor.hpp \
|
||||
src/ui/w_PreferencesWindow.hpp \
|
||||
src/ui/w_RoutesEditor.hpp \
|
||||
src/ui/w_SubscriptionEditor.hpp \
|
||||
src/ui/w_ScreenShot_Core.hpp \
|
||||
src/utils/QvHelpers.hpp \
|
||||
src/utils/QvTinyLog.hpp \
|
||||
src/utils/QJsonModel.hpp \
|
||||
src/utils/QJsonObjectInsertMacros.h
|
||||
|
||||
FORMS += \
|
||||
src/ui/w_ExportConfig.ui \
|
||||
src/ui/w_ImportConfig.ui \
|
||||
src/ui/w_InboundEditor.ui \
|
||||
src/ui/w_JsonEditor.ui \
|
||||
src/ui/w_MainWindow.ui \
|
||||
src/ui/w_OutboundEditor.ui \
|
||||
src/ui/w_PreferencesWindow.ui \
|
||||
src/ui/w_RoutesEditor.ui \
|
||||
src/ui/w_ScreenShot_Core.ui \
|
||||
src/ui/w_SubscriptionEditor.ui
|
||||
|
||||
RESOURCES += \
|
||||
resources.qrc
|
||||
|
||||
# Fine......
|
||||
message(" ")
|
||||
message("Qv2ray Version: $${VERSION}")
|
||||
message("|-------------------------------------------------|")
|
||||
@ -149,183 +26,143 @@ message("| permitted by local law. |")
|
||||
message("| |")
|
||||
message("| See: https://www.gnu.org/licenses/gpl-3.0.html |")
|
||||
message("|-------------------------------------------------|")
|
||||
message("| Project Homepage: https://github.com/Qv2ray |")
|
||||
message("| Welcome to contribute! |")
|
||||
message("|-------------------------------------------------|")
|
||||
message(" ")
|
||||
|
||||
|
||||
RC_ICONS += ./assets/icons/qv2ray.ico
|
||||
ICON = ./assets/icons/qv2ray.icns
|
||||
|
||||
with_new_backend {
|
||||
!exists($$PWD/libs/libqvb/build/libqvb.h) {
|
||||
message(" ")
|
||||
message("Cannot continue: ")
|
||||
message(" --> Qv2ray is configured to use custom backend, but: ")
|
||||
message(" libs/libqvb/build/libqvb.h is missing. ")
|
||||
error("! ABORTING THE BUILD !")
|
||||
message(" ")
|
||||
}
|
||||
|
||||
message("Qv2ray will use custom API backend.")
|
||||
message(" --> Adding libqvb header.")
|
||||
HEADERS += libs/libqvb/build/libqvb.h
|
||||
|
||||
# ==-- OS Specific configurations for libqvb --==
|
||||
win {
|
||||
message(" --> Linking libqvb static library, for Windows platform.")
|
||||
LIBS += -L$$PWD/libs/ -lqvb-win64
|
||||
}
|
||||
unix:!macx {
|
||||
message(" --> Linking libqvb static library, for Linux platform.")
|
||||
LIBS += -L$$PWD/libs/ -lqvb-linux64
|
||||
}
|
||||
macx {
|
||||
message(" --> Linking libqvb static library and Security framework, for macOS platform.")
|
||||
LIBS += -L$$PWD/libs/ -lqvb-darwin
|
||||
LIBS += -framework Security
|
||||
}
|
||||
} else {
|
||||
DEFINES += WITH_LIB_GRPCPP
|
||||
message("Qv2ray will use libgRPC as API backend")
|
||||
|
||||
# ------------------------------------------ Begin checking gRPC and protobuf headers.
|
||||
!exists($$PWD/libs/gen/v2ray_api_commands.grpc.pb.h) || !exists($$PWD/libs/gen/v2ray_api_commands.grpc.pb.cc) || !exists($$PWD/libs/gen/v2ray_api_commands.pb.h) || !exists($$PWD/libs/gen/v2ray_api_commands.pb.cc) {
|
||||
message(" ")
|
||||
message("-----------------------------------------------")
|
||||
message("Cannot continue: ")
|
||||
message(" --> Qv2ray is not properly configured yet: ")
|
||||
message(" gRPC and protobuf headers for v2ray API is missing.")
|
||||
message(" --> Please run gen_grpc.sh gen_grpc.bat or deps_macOS.sh located in tools/")
|
||||
message(" --> Or consider reading the build wiki: https://github.com/lhy0403/Qv2ray/wiki/Manually-Build-Qv2ray")
|
||||
message("-----------------------------------------------")
|
||||
message(" ")
|
||||
warning("IF YOU THINK IT'S A MISTAKE, PLEASE OPEN AN ISSUE")
|
||||
error("! ABORTING THE BUILD !")
|
||||
message(" ")
|
||||
}
|
||||
|
||||
SOURCES += libs/gen/v2ray_api_commands.pb.cc \
|
||||
libs/gen/v2ray_api_commands.grpc.pb.cc
|
||||
|
||||
HEADERS += libs/gen/v2ray_api_commands.pb.h \
|
||||
libs/gen/v2ray_api_commands.grpc.pb.h
|
||||
|
||||
# ==-- OS Specific configurations for libgRPC and libprotobuf --==
|
||||
win {
|
||||
# A hack for protobuf header.
|
||||
message(" --> Applying a hack for protobuf header")
|
||||
DEFINES += _WIN32_WINNT=0x600
|
||||
|
||||
message(" --> Linking against gRPC and protobuf library.")
|
||||
DEPENDPATH += $$PWD/libs/gRPC-win32/include
|
||||
INCLUDEPATH += $$PWD/libs/gRPC-win32/include
|
||||
LIBS += -L$$PWD/libs/gRPC-win32/lib/ \
|
||||
-llibprotobuf.dll \
|
||||
-llibgrpc++.dll
|
||||
}
|
||||
unix {
|
||||
# For gRPC and protobuf in linux and macOS
|
||||
message(" --> Linking against gRPC and protobuf library.")
|
||||
LIBS += -L/usr/local/lib -lgrpc++ -lprotobuf -lgrpc
|
||||
}
|
||||
macx {
|
||||
message(" --> Linking libgpr and libupb.")
|
||||
LIBS += -lgpr -lupb
|
||||
}
|
||||
# Distinguish debug and release builds.
|
||||
CONFIG(release, debug|release) {
|
||||
CONFIG+=Qv2ray_release no_increase_build_number
|
||||
}
|
||||
CONFIG(debug, debug|release) {
|
||||
CONFIG+=Qv2ray_debug
|
||||
}
|
||||
|
||||
message(" ")
|
||||
# ------------------------------------------ Begin to detect language files.
|
||||
message("Looking for language support.")
|
||||
QM_FILES_RESOURCE_PREFIX = "translations"
|
||||
for(var, $$list($$files("translations/*.ts", true))) {
|
||||
LOCALE_FILENAME = $$basename(var)
|
||||
message(" --> Found:" $$LOCALE_FILENAME)
|
||||
!equals(LOCALE_FILENAME, "en_US.ts") {
|
||||
# ONLY USED IN LRELEASE CONTEXT
|
||||
# en_US is not EXTRA...
|
||||
EXTRA_TRANSLATIONS += translations/$$LOCALE_FILENAME
|
||||
}
|
||||
}
|
||||
message("Qv2ray will build with" $${replace(EXTRA_TRANSLATIONS, "translations/", "")})
|
||||
TRANSLATIONS += translations/en_US.ts
|
||||
# Qv2ray basic configuration
|
||||
CONFIG += qt c++17 openssl-linked
|
||||
include($$PWD/makespec/00-deps.pri)
|
||||
|
||||
message(" ")
|
||||
QMAKE_CXXFLAGS += -Wno-missing-field-initializers -Wno-unused-parameter -Wno-unused-variable
|
||||
# lrelease will not work when adding BEFORE 00-deps.pri
|
||||
CONFIG += lrelease embed_translations
|
||||
DEFINES += QT_DEPRECATED_WARNINGS QV2RAY_VERSION_STRING=\"\\\"v$${VERSION}\\\"\" QAPPLICATION_CLASS=QApplication
|
||||
|
||||
message("Adding QHttpServer Support")
|
||||
message(" --> Adding qhttpserver")
|
||||
HEADERS += \
|
||||
$$PWD/3rdparty/qhttpserver/src/qhttpconnection.h \
|
||||
$$PWD/3rdparty/qhttpserver/src/qhttprequest.h \
|
||||
$$PWD/3rdparty/qhttpserver/src/qhttpresponse.h \
|
||||
$$PWD/3rdparty/qhttpserver/src/qhttpserver.h \
|
||||
$$PWD/3rdparty/qhttpserver/src/qhttpserverapi.h \
|
||||
$$PWD/3rdparty/qhttpserver/src/qhttpserverfwd.h
|
||||
SOURCES += \
|
||||
$$PWD/3rdparty/qhttpserver/src/qhttpconnection.cpp \
|
||||
$$PWD/3rdparty/qhttpserver/src/qhttprequest.cpp \
|
||||
$$PWD/3rdparty/qhttpserver/src/qhttpresponse.cpp \
|
||||
$$PWD/3rdparty/qhttpserver/src/qhttpserver.cpp
|
||||
# Source file parser
|
||||
include($$PWD/makespec/01-sourcesparser.pri)
|
||||
|
||||
INCLUDEPATH += 3rdparty/qhttpserver/src/
|
||||
# Main config
|
||||
Qv2rayAddSource(base, _, GlobalInstances, hpp)
|
||||
Qv2rayAddSource(base, _, JsonHelpers, hpp)
|
||||
Qv2rayAddSource(base, _, Qv2rayBase, hpp)
|
||||
Qv2rayAddSource(base, _, Qv2rayFeatures, hpp)
|
||||
Qv2rayAddSource(base, _, Qv2rayLog, cpp, hpp)
|
||||
Qv2rayAddSource(base, models, CoreObjectModels, hpp)
|
||||
Qv2rayAddSource(base, models, QvConfigModel, hpp)
|
||||
Qv2rayAddSource(base, models, QvConfigIdentifier, hpp)
|
||||
Qv2rayAddSource(base, models, QvSafeType, hpp)
|
||||
Qv2rayAddSource(base, models, QvRuntimeConfig, hpp)
|
||||
Qv2rayAddSource(base, models, QvStartupConfig, hpp)
|
||||
Qv2rayAddSource(common, _, CommandArgs, cpp, hpp)
|
||||
Qv2rayAddSource(common, _, HTTPRequestHelper, cpp, hpp)
|
||||
Qv2rayAddSource(common, _, LogHighlighter, cpp, hpp)
|
||||
Qv2rayAddSource(common, _, QJsonModel, cpp, hpp)
|
||||
Qv2rayAddSource(common, _, QvHelpers, cpp, hpp)
|
||||
Qv2rayAddSource(common, _, QvTranslator, hpp)
|
||||
Qv2rayAddSource(components, autolaunch, QvAutoLaunch, cpp, hpp)
|
||||
Qv2rayAddSource(components, pac, QvGFWPACConverter, cpp)
|
||||
Qv2rayAddSource(components, pac, QvPACHandler, cpp, hpp)
|
||||
Qv2rayAddSource(components, plugins/toolbar, QvToolbar, cpp, hpp)
|
||||
Qv2rayAddSource(components, plugins/toolbar, QvToolbar_linux, cpp)
|
||||
Qv2rayAddSource(components, plugins/toolbar, QvToolbar_win, cpp)
|
||||
Qv2rayAddSource(components, proxy, QvProxyConfigurator, cpp, hpp)
|
||||
Qv2rayAddSource(components, tcping, QvTCPing, cpp, hpp)
|
||||
Qv2rayAddSource(components, speedchart, speedwidget, cpp, hpp)
|
||||
Qv2rayAddSource(components, speedchart, speedplotview, cpp, hpp)
|
||||
Qv2rayAddSource(components, geosite, QvGeositeReader, cpp, hpp)
|
||||
Qv2rayAddSource(core, config, ConfigBackend, cpp, hpp)
|
||||
Qv2rayAddSource(core, config, ConfigUpgrade, cpp)
|
||||
Qv2rayAddSource(core, connection, ConnectionIO, cpp, hpp)
|
||||
Qv2rayAddSource(core, connection, Generation, cpp, hpp)
|
||||
Qv2rayAddSource(core, connection, Serialization, cpp, hpp)
|
||||
Qv2rayAddSource(core, _, CoreUtils, cpp, hpp)
|
||||
Qv2rayAddSource(core, kernel, KernelInteractions, cpp, hpp)
|
||||
Qv2rayAddSource(core, kernel, APIBackend, cpp, hpp)
|
||||
Qv2rayAddSource(ui, editors, w_InboundEditor, cpp, hpp, ui)
|
||||
Qv2rayAddSource(ui, editors, w_JsonEditor, cpp, hpp, ui)
|
||||
Qv2rayAddSource(ui, editors, w_OutboundEditor, cpp, hpp, ui)
|
||||
Qv2rayAddSource(ui, editors, w_RoutesEditor, cpp, hpp, ui)
|
||||
Qv2rayAddSource(ui, editors, w_RoutesEditor_extra, cpp)
|
||||
Qv2rayAddSource(ui, nodemodels, InboundNodeModel, cpp, hpp)
|
||||
Qv2rayAddSource(ui, nodemodels, OutboundNodeModel, cpp, hpp)
|
||||
Qv2rayAddSource(ui, nodemodels, RuleNodeModel, cpp, hpp)
|
||||
Qv2rayAddSource(ui, nodemodels, NodeModelsBase, hpp)
|
||||
Qv2rayAddSource(ui, messaging, QvMessageBus, cpp, hpp)
|
||||
Qv2rayAddSource(ui, _, w_ExportConfig, cpp, hpp, ui)
|
||||
Qv2rayAddSource(ui, _, w_ImportConfig, cpp, hpp, ui)
|
||||
Qv2rayAddSource(ui, _, w_MainWindow, cpp, hpp, ui)
|
||||
Qv2rayAddSource(ui, _, w_MainWindow_extra, cpp)
|
||||
Qv2rayAddSource(ui, _, w_PreferencesWindow, cpp, hpp, ui)
|
||||
Qv2rayAddSource(ui, _, w_ScreenShot_Core, cpp, hpp, ui)
|
||||
Qv2rayAddSource(ui, _, w_SubscriptionManager, cpp, hpp, ui)
|
||||
Qv2rayAddSource(ui, widgets, StreamSettingsWidget, cpp, hpp, ui)
|
||||
|
||||
message(" --> Adding http parser")
|
||||
HEADERS += 3rdparty/qhttpserver/http-parser/http_parser.h
|
||||
SOURCES += 3rdparty/qhttpserver/http-parser/http_parser.c
|
||||
INCLUDEPATH += 3rdparty/qhttpserver/http-parser/
|
||||
SOURCES += $$PWD/src/main.cpp
|
||||
HEADERS +=
|
||||
FORMS +=
|
||||
INCLUDEPATH += $$PWD/src
|
||||
RESOURCES += $$PWD/resources.qrc
|
||||
ICON = $$PWD/assets/icons/qv2ray.icns
|
||||
RC_ICONS += $$PWD/assets/icons/qv2ray.ico
|
||||
|
||||
message(" ")
|
||||
win {
|
||||
message("Configuring for win32 environment")
|
||||
DEFINES += QHTTPSERVER_EXPORT
|
||||
message(" --> Setting up target descriptions")
|
||||
QMAKE_TARGET_DESCRIPTION = "Qv2ray, a cross-platform v2ray GUI client."
|
||||
QMAKE_TARGET_PRODUCT = "Qv2ray"
|
||||
include($$PWD/makespec/02-translations.pri)
|
||||
|
||||
message(" --> Adding Taskbar Toolbox CPP files.")
|
||||
SOURCES += src/ui/NetSpeedBar/QvNetSpeedBar_win.cpp
|
||||
|
||||
message(" --> Linking against winHTTP and winSock2.")
|
||||
LIBS += -lwinhttp -lwininet -lws2_32
|
||||
}
|
||||
|
||||
macx {
|
||||
# For Linux and macOS
|
||||
message("Configuring for macOS specific environment")
|
||||
LIBS += -framework Carbon -framework Cocoa
|
||||
}
|
||||
|
||||
# Reuse unix for macx as well
|
||||
unix {
|
||||
# For Linux and macOS
|
||||
message("Configuring for unix-like environment")
|
||||
# macOS homebrew include path
|
||||
message(" --> Adding local include folder to search path")
|
||||
INCLUDEPATH += /usr/local/include/
|
||||
|
||||
message(" --> Adding Plasma Toolbox CPP files.")
|
||||
SOURCES += $$PWD/src/ui/NetSpeedBar/QvNetSpeedBar_linux.cpp
|
||||
|
||||
message(" --> Generating desktop dependency.")
|
||||
desktop.files += ./assets/qv2ray.desktop
|
||||
desktop.path = $$PREFIX/share/applications/
|
||||
|
||||
message(" --> Generating icons dependency.")
|
||||
icon.files += ./assets/icons/qv2ray.png
|
||||
icon.path = $$PREFIX/share/icons/hicolor/256x256/apps/
|
||||
|
||||
target.path = $$PREFIX/bin/
|
||||
INSTALLS += target desktop icon
|
||||
include($$PWD/makespec/03-unix.pri)
|
||||
# Sub-process of Qv2ray per-OS build
|
||||
!macx: include($$PWD/makespec/04-unix-linux.pri)
|
||||
macx: include($$PWD/makespec/04-unix-macOS.pri)
|
||||
} else {
|
||||
include($$PWD/makespec/03-Windows.pri)
|
||||
}
|
||||
|
||||
with_metainfo {
|
||||
message(" --> Generating metainfo dependency.")
|
||||
appdataXml.files += ./assets/qv2ray.metainfo.xml
|
||||
appdataXml.path = $$PREFIX/share/metainfo/
|
||||
INSTALLS += appdataXml
|
||||
DEFINES += WITH_FLATHUB_CONFIG_PATH
|
||||
# ------------------------------------------ Begin checking protobuf domain list headers.
|
||||
!exists($$PWD/libs/gen/v2ray_geosite.pb.cc) || !exists($$PWD/libs/gen/v2ray_geosite.pb.cc) {
|
||||
Qv2rayQMakeError("Protobuf headers for v2ray geosite is missing.")
|
||||
}
|
||||
|
||||
SOURCES += $$PWD/libs/gen/v2ray_geosite.pb.cc
|
||||
HEADERS += $$PWD/libs/gen/v2ray_geosite.pb.h
|
||||
|
||||
# General header and source files for gRPC and libQvb
|
||||
message(" ")
|
||||
use_grpc {
|
||||
DEFINES += WITH_LIB_GRPCPP
|
||||
message("Qv2ray will use gRPC as API backend")
|
||||
|
||||
!exists($$PWD/libs/gen/v2ray_api.grpc.pb.h) || !exists($$PWD/libs/gen/v2ray_api.grpc.pb.cc) || !exists($$PWD/libs/gen/v2ray_api.pb.h) || !exists($$PWD/libs/gen/v2ray_api.pb.cc) {
|
||||
Qv2rayQMakeError("gRPC and protobuf headers for v2ray API is missing.")
|
||||
}
|
||||
|
||||
SOURCES += $$PWD/libs/gen/v2ray_api.pb.cc $$PWD/libs/gen/v2ray_api.grpc.pb.cc
|
||||
HEADERS += $$PWD/libs/gen/v2ray_api.pb.h $$PWD/libs/gen/v2ray_api.grpc.pb.h
|
||||
} else {
|
||||
message("Qv2ray will use libqvb as API backend")
|
||||
!exists($$PWD/libs/libqvb/build/libqvb.h) {
|
||||
Qv2rayQMakeError("libs/libqvb/build/libqvb.h is missing.")
|
||||
}
|
||||
HEADERS += $$PWD/libs/libqvb/build/libqvb.h
|
||||
}
|
||||
|
||||
# Misc steps to build Qv2ray.
|
||||
include($$PWD/makespec/99-others.pri)
|
||||
|
||||
message(" ")
|
||||
message("Done configuring Qv2ray project. Build output will be at:" $$OUT_PWD)
|
||||
message("Type `make` or `mingw32-make` to start building Qv2ray")
|
||||
message("This Qv2ray build contains: ")
|
||||
message(" --> $${size(SOURCES)} source files")
|
||||
message(" --> $${size(HEADERS)} header files")
|
||||
message(" --> $${size(FORMS)} ui files")
|
||||
message(" --> $${size(TRANSLATIONS)} translation files")
|
||||
message(" --> $${size(EXTRA_TRANSLATIONS)} extra translation files")
|
||||
message(" ")
|
||||
message("Finished configuring Qv2ray project. Build output will be at:" $$OUT_PWD)
|
||||
message("Type 'make' or 'nmake' to start building Qv2ray")
|
||||
|
@ -41,6 +41,7 @@
|
||||
- Copyright (c) 2019 Itay Grudev (@itay-grudev): **SingleApplication** (MIT)
|
||||
- Copyright (c) 2019 paceholder (@paceholder): **nodeeditor (QNodeEditor modified by lhy0403)** (BSD-3-Clause)
|
||||
- Copyright (c) 2020 Ram Pani (@DuckSoft): **QvRPCBridge** (WTFPL)
|
||||
- Copyright (c) 2019 ShadowSocks (@shadowsocks): **libQtShadowsocks** (LGPLv3)
|
||||
|
||||
## Licences
|
||||
|
||||
|
@ -1,84 +0,0 @@
|
||||
***Note: Qv2ray can only be used for learning related technologies such as Qt/c++/linux/CI/automation and use within the scope permitted by law. Any individual or group may not use Qv2ray for any violation of relevant laws and regulations.***
|
||||
|
||||
*Any attempt to download or download any branch or distribution of Qv2ray constitutes your agreement that the author of the project will not be liable for any legal liability arising from your breach of the above guidelines.*
|
||||
|
||||
# Qv2ray
|
||||
|
||||
[](http://hits.dwyl.io/lhy0403/Qv2ray) 
|
||||
|
||||
The v2ray GUI client using Qt, supports Windows, Linux and macOS
|
||||
|
||||
Supports editing connection config, importing any file and `vmess://` shared link.
|
||||
|
||||
Special Thanks to [Hv2ray](https://github.com/aliyuchang33/Hv2ray) by [@aliyuchang33](https://github.com/aliyuchang33)
|
||||
|
||||
## Relevant Links
|
||||
|
||||
- **Brief Feature Introduction: https://lhy0403.github.io/Qv2ray**
|
||||
- Latest **[Release Distribution](https://github.com/lhy0403/Qv2ray/releases/latest)**
|
||||
- Crowdin Translation Platform: **[Public Translation Platform](https://crwd.in/qv2ray)**
|
||||
- ArchLinux - AUR: **[qv2ray](https://aur.archlinux.org/packages/qv2ray/)**
|
||||
- **[qv2ray-dev-git](https://aur.archlinux.org/packages/qv2ray-dev-git)**: Development version from branch`dev`, maintained by **[@axionl](https://github.com/axionl)**
|
||||
- **Note: The development branch contains unstable feature update and (even more) unstable bug fixes, it's not recommended to use a development version in production.**
|
||||
|
||||
### First time using Qv2ray please refer to ➡ [User Manual](https://lhy0403.github.io/Qv2ray)
|
||||
|
||||
|
||||
|
||||
## Qv2ray Project Status
|
||||
|
||||
### Jenkins CI
|
||||
|
||||
- Server Website: [https://jenkins.lhy0403.top/](https://jenkins.lhy0403.top/)
|
||||
|
||||
| | [Master](https://github.com/lhy0403/Qv2ray/tree/master) | [Developement](https://github.com/lhy0403/Qv2ray/tree/dev) | [Version v1](https://github.com/lhy0403/Qv2ray/tree/version-v1) | [Version v2](https://github.com/lhy0403/Qv2ray/tree/version-v2) | Testing |
|
||||
| -------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
|
||||
| Linux AppImage | [](https://jenkins.lhy0403.top/job/Qv2ray-AppImage-Release/) | [](https://jenkins.lhy0403.top/job/Qv2ray-AppImage-Dev/) | [](https://jenkins.lhy0403.top/job/Qv2ray-AppImage-Version1/) | [](https://jenkins.lhy0403.top/job/Qv2ray-AppImage-Version2/) | [](https://jenkins.lhy0403.top/job/Qv2ray-AppImage-Testing/) |
|
||||
| Windows Zip | [](https://jenkins.lhy0403.top/job/Qv2ray-Win32-Release/) | [](https://jenkins.lhy0403.top/job/Qv2ray-Win32-Dev/) | [](https://jenkins.lhy0403.top/job/Qv2ray-Win32-Version1/) | [](https://jenkins.lhy0403.top/job/Qv2ray-Win32-Version2/) | [](https://jenkins.lhy0403.top/job/Qv2ray-Win32-Testing/) |
|
||||
|
||||
### Travis & Appveyor Cross Platform Build Status
|
||||
|
||||
*Travis per machine badge provided by: [badge-matrix](https://github.com/exogen/badge-matrix)*
|
||||
|
||||
| OS | [master](https://github.com/lhy0403/Qv2ray/tree/master) | [dev](https://github.com/lhy0403/Qv2ray/tree/dev) | [version-v1](https://github.com/lhy0403/Qv2ray/tree/version-v1) | [version-v2](https://github.com/lhy0403/Qv2ray/tree/version-v2) |
|
||||
| --------------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
|
||||
| [Linux](https://travis-ci.com/lhy0403/Qv2ray) |  |  |  |  |
|
||||
| [macOS](https://travis-ci.com/lhy0403/Qv2ray) |  |  |  |  |
|
||||
| [Windows](https://ci.appveyor.com/project/lhy0403/qv2ray) | [](https://ci.appveyor.com/project/lhy0403/qv2ray/branch/master) | [](https://ci.appveyor.com/project/lhy0403/qv2ray/branch/dev) | [](https://ci.appveyor.com/project/lhy0403/qv2ray/branch/version-v1) | [](https://ci.appveyor.com/project/lhy0403/qv2ray/branch/version-v2) |
|
||||
|
||||
|
||||
|
||||
## Special Thanks
|
||||
|
||||
| NickName (@GithubAccount) | Contributions |
|
||||
| ------------------------------------------------------------ | ------------------------------ |
|
||||
| Leroy.H.Y [@lhy0403](https://github.com/lhy0403) | Qv2ray Current Maintainer |
|
||||
| Hork [@aliyuchang33](https://github.com/aliyuchang33) | Hv2ray Initial Idea and Design |
|
||||
| SOneWinstone [@SoneWinstone](https://github.com/SoneWinstone) | Qt HTTP Interactive Logics |
|
||||
| ArielAxionL [@axionl](https://github.com/axionl) | Qv2ray Artworks |
|
||||
| TheBadGateway [@thebadgateway](https://github.com/thebadgateway) | Translation for Russian |
|
||||
|
||||
|
||||
|
||||
## Open Source License
|
||||
|
||||
Qv2ray is licensed under [](https://www.gnu.org/licenses/gpl-3.0)
|
||||
|
||||
Submodule [X2Struct](https://github.com/xyz347/x2struct) is licensed under 
|
||||
|
||||
```
|
||||
Qv2ray, A Qt frontend for v2ray. Written in c++
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
```
|
@ -1 +0,0 @@
|
||||
theme: jekyll-theme-architect
|
@ -4,7 +4,7 @@ p, li { white-space: pre-wrap; }
|
||||
</style></head><body style=" font-family:'WenQuanYi Micro Hei'; font-size:10pt; font-weight:400; font-style:normal;">
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MonoSpace'; font-weight:600; color:#d68952;">This program comes with ABSOLUTELY NO WARRANTY.</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MonoSpace'; font-weight:600; color:#d68952;">This is free software, and you are welcome to redistribute it under certain conditions.</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MonoSpace'; color:#d68952;">Copyright (c) 2019 2020 Qv2ray Developemnt Group.</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MonoSpace'; color:#d68952;">Copyright (c) 2019-2020 Qv2ray Developemnt Group.</span></p>
|
||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Noto Sans'; color:#d68952;"><br /></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MonoSpace'; font-weight:600; color:#d68952;">Libraries that have been used in Qv2ray are listed below (Sorted by date added):</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MonoSpace'; color:#d68952;">Copyright (c) 2020 dridk (@dridk): X2Struct (Apache)</span></p>
|
||||
@ -15,4 +15,5 @@ p, li { white-space: pre-wrap; }
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MonoSpace'; color:#d68952;">Copyright (c) 2020 Itay Grudev (@itay-grudev): SingleApplication (MIT)</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MonoSpace'; color:#d68952;">Copyright (c) 2020 paceholder (@paceholder): nodeeditor (QNodeEditor modified by lhy0403) (BSD-3-Clause)</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MonoSpace'; color:#d68952;">Copyright (c) 2019 TheWanderingCoel (@TheWanderingCoel): ShadowClash (launchatlogin) (GPLv3)</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MonoSpace'; color:#d68952;">Copyright (c) 2020 DuckSoft (@DuckSoft): QvRPCBridge (WTFPL)</span></p></body></html>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MonoSpace'; color:#d68952;">Copyright (c) 2020 DuckSoft (@DuckSoft): QvRPCBridge (WTFPL)</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MonoSpace'; color:#d68952;">Copyright (c) 2019 ShadowSocks (@shadowsocks): libQtShadowsocks (LGPLv3)</span></p></body></html>
|
||||
|
@ -29,11 +29,14 @@
|
||||
</screenshots>
|
||||
|
||||
<releases>
|
||||
<release version="2.0.0" date="2019-1-24">
|
||||
<release version="2.0.1" date="2019-1-28">
|
||||
<description>
|
||||
<ul>
|
||||
<li>fix: fixed windows startup issue</li>
|
||||
<li>fix: autostart, tProxyPrep and negative API fixed</li>
|
||||
<li>Prevent messing up vcore logs</li>
|
||||
<li>upstream: solved QTimer issue and golang memory leak issue</li>
|
||||
<li>fix: fixed duplicated scheme:// in GNOME system settings</li>
|
||||
<li>add: added os process signals support</li>
|
||||
<li>add: enable hiding Qv2ray when performing screenshot, fixed #283</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
|
@ -1 +0,0 @@
|
||||
Subproject commit a2c741f03d0ceac6bc3ff1ae4097610400afa172
|
Binary file not shown.
Binary file not shown.
@ -1 +1 @@
|
||||
Subproject commit c7199c1d876c012f8b40a38de25c90f69dcda0ed
|
||||
Subproject commit e4765aee61f58c13a8a5228727856ae622ab4e3c
|
Binary file not shown.
BIN
libs/libssl.lib
BIN
libs/libssl.lib
Binary file not shown.
32
libs/x64-windows/README.x64-windows.txt
Normal file
32
libs/x64-windows/README.x64-windows.txt
Normal file
@ -0,0 +1,32 @@
|
||||
Qv2ray Dependencies Directory for Windows x64
|
||||
|
||||
THIS IS A PLACEHOLDER WHICH ALLOWS GIT TO INCLUDE THIS DIRECTORY
|
||||
|
||||
If you want to build Qv2ray on Windows, please download the gRPC and
|
||||
protobuf headers and libraries according to your CPU architecture:
|
||||
|
||||
- Go to https://github.com/Qv2ray/Qv2ray-deps/releases
|
||||
- Download "Qv2ray-deps-x64-windows.7z"
|
||||
|
||||
Then extract the 7z zipball here.
|
||||
|
||||
Make sure these file exists:
|
||||
- Qv2raySourceRoot/libs/x64-windows/tools/grpc/grpc_cpp_plugin.exe
|
||||
- Qv2raySourceRoot/libs/x64-windows/tools/protobuf/protoc.exe
|
||||
|
||||
# ======================================================================
|
||||
|
||||
64 位 Windows 的 Qv2ray 依赖文件夹
|
||||
这个文件的存在使得 git 可以添加此文件夹
|
||||
|
||||
如果你想在 Windows 下编译 Qv2ray, 请遵循这些步骤下载 gRPC 和 protobuf 头文件
|
||||
和库文件:
|
||||
|
||||
- 访问 https://github.com/Qv2ray/Qv2ray-deps/releases
|
||||
- 下载 "Qv2ray-deps-x64-windows.7z"
|
||||
|
||||
然后将这个 7z 压缩包解压到这里
|
||||
|
||||
请确保这些文件存在:
|
||||
- Qv2ray源码根目录/libs/x64-windows/tools/grpc/grpc_cpp_plugin.exe
|
||||
- Qv2ray源码根目录/libs/x64-windows/tools/protobuf/protoc.exe
|
32
libs/x86-windows/README.x86-windows.txt
Normal file
32
libs/x86-windows/README.x86-windows.txt
Normal file
@ -0,0 +1,32 @@
|
||||
Qv2ray Dependencies Directory for Windows x86
|
||||
|
||||
THIS IS A PLACEHOLDER WHICH ALLOWS GIT TO INCLUDE THIS DIRECTORY
|
||||
|
||||
If you want to build Qv2ray on Windows, please download the gRPC and
|
||||
protobuf headers and libraries according to your CPU architecture:
|
||||
|
||||
- Go to https://github.com/Qv2ray/Qv2ray-deps/releases
|
||||
- Download "Qv2ray-deps-x86-windows.7z"
|
||||
|
||||
Then extract the 7z zipball here.
|
||||
|
||||
Make sure these file exists:
|
||||
- Qv2raySourceRoot/libs/x86-windows/tools/grpc/grpc_cpp_plugin.exe
|
||||
- Qv2raySourceRoot/libs/x86-windows/tools/protobuf/protoc.exe
|
||||
|
||||
# ======================================================================
|
||||
|
||||
32 位 Windows 的 Qv2ray 依赖文件夹
|
||||
这个文件的存在使得 git 可以添加此文件夹
|
||||
|
||||
如果你想在 Windows 下编译 Qv2ray, 请遵循这些步骤下载 gRPC 和 protobuf 头文件
|
||||
和库文件:
|
||||
|
||||
- 访问 https://github.com/Qv2ray/Qv2ray-deps/releases
|
||||
- 下载 "Qv2ray-deps-x86-windows.7z"
|
||||
|
||||
然后将这个 7z 压缩包解压到这里
|
||||
|
||||
请确保这些文件存在:
|
||||
- Qv2ray源码根目录/libs/x86-windows/tools/grpc/grpc_cpp_plugin.exe
|
||||
- Qv2ray源码根目录/libs/x86-windows/tools/protobuf/protoc.exe
|
48
makespec/00-deps.pri
Normal file
48
makespec/00-deps.pri
Normal file
@ -0,0 +1,48 @@
|
||||
message(" ")
|
||||
message("Configuring Qv2ray Dependencies...")
|
||||
|
||||
defineTest(Qv2rayQMakeError) {
|
||||
message(" ")
|
||||
message("-----------------------------------------------")
|
||||
message("Cannot continue: ")
|
||||
message(" --> Qv2ray is not properly configured yet: ")
|
||||
message(" $$ARGS")
|
||||
message(" --> Please read the build wiki: https://github.com/Qv2ray/Qv2ray/wiki/Manually-Build-Qv2ray")
|
||||
message("-----------------------------------------------")
|
||||
message(" ")
|
||||
warning("IF YOU THINK IT'S A MISTAKE, PLEASE OPEN AN ISSUE")
|
||||
error("! ABORTING THE BUILD !")
|
||||
message(" ")
|
||||
}
|
||||
|
||||
CONFIG += enable_decoder_qr_code enable_encoder_qr_code
|
||||
include($$PWD/../3rdparty/qzxing/src/QZXing-components.pri)
|
||||
include($$PWD/../3rdparty/SingleApplication/singleapplication.pri)
|
||||
include($$PWD/../3rdparty/QNodeEditor/QNodeEditor.pri)
|
||||
|
||||
#include(3rdparty/x2struct/x2struct.pri)
|
||||
|
||||
message("Adding QHttpServer Support")
|
||||
message(" --> Adding qhttpserver")
|
||||
HEADERS += \
|
||||
$$PWD/../3rdparty/qhttpserver/src/qhttpconnection.h \
|
||||
$$PWD/../3rdparty/qhttpserver/src/qhttprequest.h \
|
||||
$$PWD/../3rdparty/qhttpserver/src/qhttpresponse.h \
|
||||
$$PWD/../3rdparty/qhttpserver/src/qhttpserver.h \
|
||||
$$PWD/../3rdparty/qhttpserver/src/qhttpserverapi.h \
|
||||
$$PWD/../3rdparty/qhttpserver/src/qhttpserverfwd.h
|
||||
|
||||
SOURCES += \
|
||||
$$PWD/../3rdparty/qhttpserver/src/qhttpconnection.cpp \
|
||||
$$PWD/../3rdparty/qhttpserver/src/qhttprequest.cpp \
|
||||
$$PWD/../3rdparty/qhttpserver/src/qhttpresponse.cpp \
|
||||
$$PWD/../3rdparty/qhttpserver/src/qhttpserver.cpp
|
||||
|
||||
INCLUDEPATH += 3rdparty/qhttpserver/src/
|
||||
|
||||
message(" --> Adding http parser")
|
||||
HEADERS += $$PWD/../3rdparty/qhttpserver/http-parser/http_parser.h
|
||||
SOURCES += $$PWD/../3rdparty/qhttpserver/http-parser/http_parser.c
|
||||
INCLUDEPATH += $$PWD/../3rdparty/qhttpserver/http-parser/
|
||||
|
||||
|
50
makespec/01-sourcesparser.pri
Normal file
50
makespec/01-sourcesparser.pri
Normal file
@ -0,0 +1,50 @@
|
||||
message(" ")
|
||||
defineTest(Qv2rayAddFile) {
|
||||
ext = $$take_last(ARGS)
|
||||
filename = $${take_first(ARGS)}.$${ext}
|
||||
qmake_debug: message("Qv2rayAddFile: filename: $$filename")
|
||||
!exists($$filename) {
|
||||
error("File: \"$$filename\" is not found, Qv2ray build preparation cannot continue")
|
||||
}
|
||||
equals(ext, "cpp") {
|
||||
SOURCES += $$filename
|
||||
} else {
|
||||
equals(ext, "hpp") {
|
||||
HEADERS += $$filename
|
||||
} else {
|
||||
equals(ext, "ui") {
|
||||
FORMS += $$filename
|
||||
} else {
|
||||
error("Unknown extension: $${ext}")
|
||||
}
|
||||
}
|
||||
}
|
||||
export(SOURCES)
|
||||
export(HEADERS)
|
||||
export(FORMS)
|
||||
}
|
||||
|
||||
defineTest(Qv2rayAddSource) {
|
||||
# Module Compnent Filename extlist
|
||||
module = $$take_first(ARGS)
|
||||
component = $$take_first(ARGS)
|
||||
filename = $$take_first(ARGS)
|
||||
extlist = $$ARGS
|
||||
FILEPATH = "$$PWD/src/$${module}"
|
||||
qmake_debug: message(Qv2rayAddSource: Adding \"$${filename}\" of module \"$${module}\", component \"$${component}\" to the project)
|
||||
equals(component, "_") {
|
||||
qmake_debug: message("Qv2rayAddSource: Component is empty, ignore")
|
||||
FILEPATH += "/$${filename}"
|
||||
FILEPATH=$$join(FILEPATH)
|
||||
} else {
|
||||
FILEPATH += "/$${component}/$${filename}"
|
||||
FILEPATH=$$join(FILEPATH)
|
||||
}
|
||||
qmake_debug: message("Qv2rayAddSource: filepath: $${FILEPATH}, extlist: $${extlist}")
|
||||
for(iterate, extlist) {
|
||||
Qv2rayAddFile($$FILEPATH, $$iterate)
|
||||
}
|
||||
export(SOURCES)
|
||||
export(HEADERS)
|
||||
export(FORMS)
|
||||
}
|
15
makespec/02-translations.pri
Normal file
15
makespec/02-translations.pri
Normal file
@ -0,0 +1,15 @@
|
||||
message(" ")
|
||||
message("Looking for language support.")
|
||||
QM_FILES_RESOURCE_PREFIX = "translations"
|
||||
for(var, $$list($$files("$$PWD/../translations/*.ts", true))) {
|
||||
LOCALE_FILENAME = $$basename(var)
|
||||
message(" --> Found:" $$LOCALE_FILENAME)
|
||||
!equals(LOCALE_FILENAME, "en_US.ts") {
|
||||
# ONLY USED IN LRELEASE CONTEXT
|
||||
# en_US is not EXTRA...
|
||||
EXTRA_TRANSLATIONS += $$PWD/../translations/$$LOCALE_FILENAME
|
||||
}
|
||||
}
|
||||
|
||||
TRANSLATIONS += $$PWD/../translations/en_US.ts
|
||||
message("Qv2ray will build with" $${replace(EXTRA_TRANSLATIONS, "$$PWD/../translations/", "")} and $${replace(TRANSLATIONS, "$$PWD/../translations/", "")})
|
43
makespec/03-Windows.pri
Normal file
43
makespec/03-Windows.pri
Normal file
@ -0,0 +1,43 @@
|
||||
message(" ")
|
||||
message("Configuring for Windows environment")
|
||||
QMAKE_CXXFLAGS += /MP /wo4251
|
||||
DEFINES += QHTTPSERVER_EXPORT
|
||||
|
||||
CONFIG += Qv2ray_Windows use_grpc
|
||||
!contains(QMAKE_TARGET.arch, x86_64) {
|
||||
CONFIG+=Qv2ray_win32
|
||||
GRPC_DEPS_PATH=$$PWD/../libs/x86-windows
|
||||
#
|
||||
!no_generate_headers: message(" --> Generating gRPC and protobuf headers for Windows x86")
|
||||
!no_generate_headers: system("$$PWD/../tools/win32-generate-pb.bat")
|
||||
} else {
|
||||
CONFIG+=Qv2ray_win64
|
||||
GRPC_DEPS_PATH=$$PWD/../libs/x64-windows
|
||||
#
|
||||
!no_generate_headers: message(" --> Generating gRPC and protobuf headers for Windows x64")
|
||||
!no_generate_headers: system("$$PWD/../tools/win64-generate-pb.bat")
|
||||
}
|
||||
|
||||
message(" --> Applying a hack for protobuf header")
|
||||
DEFINES += _WIN32_WINNT=0x600
|
||||
|
||||
DEPENDPATH += $$GRPC_DEPS_PATH/include
|
||||
INCLUDEPATH += $$GRPC_DEPS_PATH/include
|
||||
|
||||
message(" --> Setting up target descriptions")
|
||||
QMAKE_TARGET_DESCRIPTION = "Qv2ray, a cross-platform v2ray GUI client."
|
||||
QMAKE_TARGET_PRODUCT = "Qv2ray"
|
||||
|
||||
Qv2ray_debug: GRPC_LIB_PATH=$$GRPC_DEPS_PATH/debug
|
||||
Qv2ray_release: GRPC_LIB_PATH=$$GRPC_DEPS_PATH
|
||||
|
||||
message(" --> WIN32: Linking against gRPC library: $$GRPC_LIB_PATH")
|
||||
Qv2ray_debug: LIBS += -L$$GRPC_LIB_PATH/lib/ -laddress_sorting -lcares -lgrpc++_unsecure -lupb -lzlibd -lgrpc_unsecure -lgpr
|
||||
Qv2ray_release: LIBS += -L$$GRPC_LIB_PATH/lib/ -laddress_sorting -lcares -lgrpc++_unsecure -lupb -lzlib -lgrpc_unsecure -lgpr
|
||||
|
||||
message(" --> WIN32: Linking against protobuf library: $$GRPC_LIB_PATH")
|
||||
Qv2ray_release: LIBS += -lmsvcrt -L$$GRPC_LIB_PATH/lib/ -llibprotobuf
|
||||
Qv2ray_debug: LIBS += -lmsvcrtd -L$$GRPC_LIB_PATH/lib/ -llibprotobufd
|
||||
|
||||
message(" --> Linking against winHTTP and winSock2.")
|
||||
LIBS += -lwinhttp -lwininet -lws2_32 -luser32
|
28
makespec/03-unix.pri
Normal file
28
makespec/03-unix.pri
Normal file
@ -0,0 +1,28 @@
|
||||
message(" ")
|
||||
win32: Qv2rayQMakeError("Do not include this file in Windows platform.")
|
||||
|
||||
# For Linux and macOS
|
||||
message("Configuring for unix-like environment")
|
||||
|
||||
message(" --> Setting up QMAKE_CXXFLAGS")
|
||||
QMAKE_CXXFLAGS += -Wno-missing-field-initializers -Wno-unused-parameter -Wno-unused-variable
|
||||
|
||||
# macOS homebrew include path
|
||||
message(" --> Adding local include folder to search path")
|
||||
INCLUDEPATH += /usr/local/include/
|
||||
|
||||
# For protobuf in linux and macOS
|
||||
message(" --> Linking against protobuf library.")
|
||||
LIBS += -L/usr/local/lib -lprotobuf
|
||||
|
||||
message(" --> Generating geosite headers for Unix")
|
||||
system("$$PWD/../tools/unix-generate-geosite.sh $$PWD")
|
||||
|
||||
use_grpc {
|
||||
no_generate_headers {
|
||||
message(" --> Skipped generation of protobuf and/or gRPC header files")
|
||||
} else {
|
||||
message(" --> Generating gRPC and protobuf headers for Unix")
|
||||
system("$$PWD/../tools/unix-generate-api.sh $$PWD")
|
||||
}
|
||||
}
|
26
makespec/04-unix-linux.pri
Normal file
26
makespec/04-unix-linux.pri
Normal file
@ -0,0 +1,26 @@
|
||||
message(" ")
|
||||
message("Configuring Qv2ray build for Linux")
|
||||
isEmpty(PREFIX) {
|
||||
PREFIX=/usr/local
|
||||
}
|
||||
|
||||
use_grpc {
|
||||
# For gRPC and protobuf in linux
|
||||
message(" --> Linking against gRPC and protobuf library.")
|
||||
LIBS += -L/usr/local/lib -lgrpc++ -lgrpc
|
||||
} else {
|
||||
message(" --> Linking libqvb static library, for Linux platform.")
|
||||
LIBS += -L$$PWD/../libs/ -lqvb-linux64
|
||||
}
|
||||
|
||||
message(" --> Generating desktop dependency.")
|
||||
desktop.files += $$PWD/../assets/qv2ray.desktop
|
||||
desktop.path = $$PREFIX/share/applications/
|
||||
|
||||
message(" --> Generating icons dependency.")
|
||||
icon.files += $$PWD/../assets/icons/qv2ray.png
|
||||
icon.path = $$PREFIX/share/icons/hicolor/256x256/apps/
|
||||
|
||||
target.path = $$PREFIX/bin/
|
||||
INSTALLS += target desktop icon
|
||||
|
11
makespec/04-unix-macOS.pri
Normal file
11
makespec/04-unix-macOS.pri
Normal file
@ -0,0 +1,11 @@
|
||||
message(" ")
|
||||
|
||||
# For Linux and macOS
|
||||
message("Configuring for macOS specific environment")
|
||||
LIBS += -framework Carbon -framework Cocoa
|
||||
|
||||
use_grpc: error("The use of gRPC backend is not supported on macOS platform.")
|
||||
|
||||
message(" --> Linking libqvb static library and Security framework, for macOS platform.")
|
||||
LIBS += -L$$PWD/../libs/ -lqvb-darwin
|
||||
LIBS += -framework Security
|
48
makespec/99-others.pri
Normal file
48
makespec/99-others.pri
Normal file
@ -0,0 +1,48 @@
|
||||
message(" ")
|
||||
with_metainfo {
|
||||
message(" --> Generating metainfo dependency.")
|
||||
appdataXml.files += ./assets/qv2ray.metainfo.xml
|
||||
appdataXml.path = $$PREFIX/share/metainfo/
|
||||
INSTALLS += appdataXml
|
||||
DEFINES += WITH_FLATHUB_CONFIG_PATH
|
||||
}
|
||||
|
||||
# Automatically increase build number.
|
||||
no_increase_build_number | qmake_lupdate {
|
||||
message("QV2RAY_BUILD_VERSION will not be increased")
|
||||
} else {
|
||||
QV2RAY_BUILD_VERSION = $$num_add($$QV2RAY_BUILD_VERSION, 1)
|
||||
write_file($$PWD/BUILDVERSION, QV2RAY_BUILD_VERSION)
|
||||
}
|
||||
|
||||
qmake_lupdate {
|
||||
message(" ")
|
||||
message("Running lupdate...")
|
||||
message(" ")
|
||||
lupdate_output = $$system(lupdate $$SOURCES $$HEADERS $$FORMS -ts $$PWD/$$TRANSLATIONS -no-ui-lines)
|
||||
message(" $$lupdate_output")
|
||||
message("lupdate finished.")
|
||||
}
|
||||
|
||||
# Qv2ray manual build info
|
||||
!no_build_info {
|
||||
_QV2RAY_BUILD_INFO_STR_=$$getenv(_QV2RAY_BUILD_INFO_)
|
||||
_QV2RAY_BUILD_EXTRA_INFO_STR_=$$getenv(_QV2RAY_BUILD_EXTRA_INFO_)
|
||||
|
||||
isEmpty(_QV2RAY_BUILD_INFO_STR_) {
|
||||
_QV2RAY_BUILD_INFO_STR_ = "Qv2ray from manual build"
|
||||
}
|
||||
|
||||
isEmpty(_QV2RAY_BUILD_EXTRA_INFO_STR_) {
|
||||
_QV2RAY_BUILD_EXTRA_INFO_STR_ = "Qv2ray $$VERSION"
|
||||
}
|
||||
} else {
|
||||
_QV2RAY_BUILD_INFO_STR_ = "No Info"
|
||||
_QV2RAY_BUILD_EXTRA_INFO_STR_ = "No Extra Info"
|
||||
}
|
||||
|
||||
message("Qv2ray build info:")
|
||||
message(" --> $$_QV2RAY_BUILD_INFO_STR_")
|
||||
message(" --> $$_QV2RAY_BUILD_EXTRA_INFO_STR_")
|
||||
|
||||
DEFINES += _QV2RAY_BUILD_INFO_STR_=\"\\\"$${_QV2RAY_BUILD_INFO_STR_}\\\"\" _QV2RAY_BUILD_EXTRA_INFO_STR_=\"\\\"$${_QV2RAY_BUILD_EXTRA_INFO_STR_}\\\"\"
|
1
makespec/BUILDVERSION
Normal file
1
makespec/BUILDVERSION
Normal file
@ -0,0 +1 @@
|
||||
3856
|
1
makespec/VERSION
Normal file
1
makespec/VERSION
Normal file
@ -0,0 +1 @@
|
||||
2.0.1
|
121
snap/snapcraft.yaml
Normal file
121
snap/snapcraft.yaml
Normal file
@ -0,0 +1,121 @@
|
||||
name: qv2ray
|
||||
base: core18
|
||||
adopt-info: qv2ray
|
||||
icon: assets/icons/qv2ray.png
|
||||
license: GPL-3.0
|
||||
|
||||
grade: devel
|
||||
confinement: devmode
|
||||
|
||||
plugs:
|
||||
gsettings:
|
||||
gtk-3-themes:
|
||||
interface: content
|
||||
target: $SNAP/data-dir/themes
|
||||
default-provider: gtk-common-themes
|
||||
icon-themes:
|
||||
interface: content
|
||||
target: $SNAP/data-dir/icons
|
||||
default-provider: gtk-common-themes
|
||||
sound-themes:
|
||||
interface: content
|
||||
target: $SNAP/data-dir/sounds
|
||||
default-provider: gtk-common-themes
|
||||
|
||||
apps:
|
||||
qv2ray:
|
||||
command: bin/desktop-launch qv2ray
|
||||
environment:
|
||||
QT_QPA_PLATFORMTHEME: gtk3
|
||||
common-id: com.github.Qv2ray
|
||||
desktop: "usr/share/applications/qv2ray.desktop"
|
||||
|
||||
parts:
|
||||
qv2ray:
|
||||
plugin: qmake
|
||||
source-type: git
|
||||
source: .
|
||||
parse-info: [usr/share/metainfo/qv2ray.metainfo.xml]
|
||||
build-packages:
|
||||
- build-essential
|
||||
- qttools5-dev-tools
|
||||
- qt5-default
|
||||
- libgrpc++-dev
|
||||
- libprotobuf-dev
|
||||
- protobuf-compiler-grpc
|
||||
stage-packages:
|
||||
- libgcc1
|
||||
- libstdc++6
|
||||
- libssl1.1
|
||||
- libgrpc++1
|
||||
- libprotobuf17
|
||||
- libqt5core5a
|
||||
- libqt5gui5
|
||||
- libqt5network5
|
||||
- libqt5widgets5
|
||||
- fonts-noto-cjk
|
||||
- libglib2.0-bin
|
||||
options:
|
||||
- PREFIX=/usr
|
||||
- CONFIG+=with_metainfo
|
||||
- CONFIG+=use_grpc
|
||||
override-pull: |
|
||||
snapcraftctl pull
|
||||
build_number=$(cat makespec/BUILDVERSION)
|
||||
tag=$(git describe --abbrev=0 --tags)
|
||||
version=$tag.$build_number
|
||||
snapcraftctl set-version "$version"
|
||||
sed -i 's|^Icon=.*|Icon=/usr/share/icons/hicolor/256x256/apps/qv2ray.png|g' assets/qv2ray.desktop
|
||||
after:
|
||||
- mytools
|
||||
- desktop-qt5
|
||||
|
||||
desktop-qt5:
|
||||
source: https://github.com/ubuntu/snapcraft-desktop-helpers.git
|
||||
source-subdir: qt
|
||||
plugin: make
|
||||
make-parameters: ["FLAVOR=qt5"]
|
||||
build-packages:
|
||||
- build-essential
|
||||
- qtbase5-dev
|
||||
- dpkg-dev
|
||||
stage-packages:
|
||||
- libxkbcommon0
|
||||
- ttf-ubuntu-font-family
|
||||
- dmz-cursor-theme
|
||||
- light-themes
|
||||
- adwaita-icon-theme
|
||||
- gnome-themes-standard
|
||||
- shared-mime-info
|
||||
- libqt5gui5
|
||||
- libgdk-pixbuf2.0-0
|
||||
- libqt5svg5 # for loading icon themes which are svg
|
||||
- try: [appmenu-qt5] # not available on core18
|
||||
- locales-all
|
||||
- xdg-user-dirs
|
||||
- fcitx-frontend-qt5
|
||||
after:
|
||||
- mytools
|
||||
|
||||
qt5-gtk-platform:
|
||||
plugin: nil
|
||||
stage-packages:
|
||||
- qt5-gtk-platformtheme
|
||||
after:
|
||||
- mytools
|
||||
|
||||
mytools:
|
||||
plugin: nil
|
||||
build-packages:
|
||||
- dirmngr
|
||||
override-pull: |
|
||||
echo 'deb http://archive.neon.kde.org/unstable bionic main' > /etc/apt/sources.list.d/neon.list
|
||||
echo "deb http://ppa.launchpad.net/ymshenyu/grpc/ubuntu bionic main" >> /etc/apt/sources.list.d/tools.list
|
||||
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv E6D4736255751E5D
|
||||
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 281f24e574404629aa3bda1a4f10c386c55cdb04
|
||||
sudo apt-get update
|
||||
snapcraftctl pull
|
||||
override-build: |
|
||||
snapcraftctl build
|
||||
sudo apt upgrade -y
|
||||
sudo apt dist-upgrade -y
|
@ -1,4 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// Qv2ray build features.
|
||||
//#ifdef __Q_Build_Without_Chart
|
@ -1,407 +0,0 @@
|
||||
#ifndef V2CONFIG_H
|
||||
#define V2CONFIG_H
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QVariant>
|
||||
#include <x2struct/x2struct.hpp>
|
||||
using namespace x2struct;
|
||||
using namespace std;
|
||||
|
||||
#define SAFE_TYPEDEF(Base, name) \
|
||||
class name : public Base { \
|
||||
public: \
|
||||
template <class... Args> \
|
||||
explicit name (Args... args) : Base(args...) {} \
|
||||
const Base& raw() const { return *this; } \
|
||||
};
|
||||
|
||||
struct QvConfigIdentifier {
|
||||
QString subscriptionName;
|
||||
QString connectionName;
|
||||
QvConfigIdentifier() { };
|
||||
//
|
||||
bool isEmpty()
|
||||
{
|
||||
return connectionName.isEmpty();
|
||||
}
|
||||
QvConfigIdentifier(QString connectionName)
|
||||
{
|
||||
this->connectionName = connectionName;
|
||||
}
|
||||
QvConfigIdentifier(QString connectionName, QString subscriptionName)
|
||||
{
|
||||
this->connectionName = connectionName;
|
||||
this->subscriptionName = subscriptionName;
|
||||
}
|
||||
const QString IdentifierString() const
|
||||
{
|
||||
return connectionName + (subscriptionName.isEmpty() ? "" : " (" + subscriptionName + ")");
|
||||
}
|
||||
friend bool operator==(QvConfigIdentifier &left, QvConfigIdentifier &right)
|
||||
{
|
||||
return left.subscriptionName == right.subscriptionName && left.connectionName == right.connectionName;
|
||||
}
|
||||
friend bool operator!=(QvConfigIdentifier &left, QvConfigIdentifier &right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
friend bool operator==(QvConfigIdentifier &left, QString &right)
|
||||
{
|
||||
return left.IdentifierString() == right;
|
||||
}
|
||||
friend bool operator!=(QvConfigIdentifier &left, QString &right)
|
||||
{
|
||||
return !(left.IdentifierString() == right);
|
||||
}
|
||||
// To make QMap happy
|
||||
friend bool operator<(const QvConfigIdentifier left, const QvConfigIdentifier right)
|
||||
{
|
||||
return left.IdentifierString() < right.IdentifierString();
|
||||
}
|
||||
friend bool operator>(const QvConfigIdentifier left, const QvConfigIdentifier right)
|
||||
{
|
||||
return left.IdentifierString() > right.IdentifierString();
|
||||
}
|
||||
XTOSTRUCT(O(subscriptionName, connectionName))
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(QvConfigIdentifier);
|
||||
|
||||
namespace Qv2ray
|
||||
{
|
||||
// To prevent anonying QJsonObject misuse
|
||||
SAFE_TYPEDEF(QJsonObject, INBOUNDSETTING)
|
||||
SAFE_TYPEDEF(QJsonObject, OUTBOUNDSETTING)
|
||||
SAFE_TYPEDEF(QJsonObject, INBOUND)
|
||||
SAFE_TYPEDEF(QJsonObject, OUTBOUND)
|
||||
SAFE_TYPEDEF(QJsonObject, CONFIGROOT)
|
||||
SAFE_TYPEDEF(QJsonObject, PROXYSETTING)
|
||||
//
|
||||
SAFE_TYPEDEF(QJsonArray, INOUTLIST)
|
||||
SAFE_TYPEDEF(INOUTLIST, OUTBOUNDS)
|
||||
SAFE_TYPEDEF(INOUTLIST, INBOUNDS)
|
||||
SAFE_TYPEDEF(QJsonObject, ROUTING)
|
||||
SAFE_TYPEDEF(QJsonObject, ROUTERULE)
|
||||
SAFE_TYPEDEF(QJsonArray, ROUTERULELIST)
|
||||
}
|
||||
|
||||
/* ----------------------------------------- */
|
||||
|
||||
namespace Qv2ray
|
||||
{
|
||||
namespace V2ConfigModels
|
||||
{
|
||||
//
|
||||
// Used in config generation
|
||||
struct AccountObject {
|
||||
QString user;
|
||||
QString pass;
|
||||
XTOSTRUCT(O(user, pass))
|
||||
};
|
||||
//
|
||||
//
|
||||
struct ApiObject {
|
||||
QString tag;
|
||||
QList<QString> services;
|
||||
ApiObject() : tag("api"), services() {}
|
||||
XTOSTRUCT(O(tag, services))
|
||||
};
|
||||
//
|
||||
//
|
||||
struct SystemPolicyObject {
|
||||
bool statsInboundUplink;
|
||||
bool statsInboundDownlink;
|
||||
SystemPolicyObject() : statsInboundUplink(), statsInboundDownlink() {}
|
||||
XTOSTRUCT(O(statsInboundUplink, statsInboundDownlink))
|
||||
};
|
||||
//
|
||||
//
|
||||
struct LevelPolicyObject {
|
||||
int handshake;
|
||||
int connIdle;
|
||||
int uplinkOnly;
|
||||
int downlinkOnly;
|
||||
bool statsUserUplink;
|
||||
bool statsUserDownlink;
|
||||
int bufferSize;
|
||||
LevelPolicyObject(): handshake(), connIdle(), uplinkOnly(), downlinkOnly(), statsUserUplink(), statsUserDownlink(), bufferSize() {}
|
||||
XTOSTRUCT(O(handshake, connIdle, uplinkOnly, downlinkOnly, statsUserUplink, statsUserDownlink, bufferSize))
|
||||
};
|
||||
//
|
||||
//
|
||||
struct PolicyObject {
|
||||
QMap<QString, LevelPolicyObject> level;
|
||||
list<SystemPolicyObject> system;
|
||||
PolicyObject(): level(), system() {}
|
||||
XTOSTRUCT(O(level, system))
|
||||
};
|
||||
//
|
||||
//
|
||||
struct RuleObject {
|
||||
// Added due to the request of @aliyuchang33
|
||||
bool QV2RAY_RULE_ENABLED;
|
||||
bool QV2RAY_RULE_USE_BALANCER;
|
||||
QString QV2RAY_RULE_TAG;
|
||||
//
|
||||
QString type;
|
||||
QList<QString> domain;
|
||||
QList<QString> ip;
|
||||
QString port;
|
||||
QString network;
|
||||
QList<QString> source;
|
||||
QList<QString> user;
|
||||
QList<QString> inboundTag;
|
||||
QList<QString> protocol;
|
||||
QString attrs;
|
||||
QString outboundTag;
|
||||
QString balancerTag;
|
||||
RuleObject() : QV2RAY_RULE_ENABLED(true), QV2RAY_RULE_USE_BALANCER(false), QV2RAY_RULE_TAG("new rule"), type("field"), domain(), ip(), port("1-65535"), network(""), source(), user(), inboundTag(), protocol(), attrs(), outboundTag(""), balancerTag("") {}
|
||||
XTOSTRUCT(O(QV2RAY_RULE_ENABLED, QV2RAY_RULE_USE_BALANCER, QV2RAY_RULE_TAG, type, domain, ip, port, network, source, user, inboundTag, protocol, attrs, outboundTag, balancerTag))
|
||||
};
|
||||
//
|
||||
//
|
||||
struct BalancerObject {
|
||||
QString tag ;
|
||||
QList<QString> selector;
|
||||
BalancerObject() : tag(), selector() {}
|
||||
XTOSTRUCT(O(tag, selector))
|
||||
};
|
||||
//
|
||||
//
|
||||
namespace TSObjects
|
||||
{
|
||||
struct HTTPRequestObject {
|
||||
QString version;
|
||||
QString method;
|
||||
QList<QString> path;
|
||||
QMap<QString, QList<QString>> headers;
|
||||
HTTPRequestObject(): version("1.1"), method("GET"), path(), headers() {}
|
||||
XTOSTRUCT(O(version, method, path, headers))
|
||||
};
|
||||
//
|
||||
//
|
||||
struct HTTPResponseObject {
|
||||
QString version;
|
||||
QString status;
|
||||
QString reason;
|
||||
QMap<QString, QList<QString>> headers;
|
||||
HTTPResponseObject(): version("1.1"), status("200"), reason("OK"), headers() {}
|
||||
XTOSTRUCT(O(version, status, reason, headers))
|
||||
};
|
||||
//
|
||||
//
|
||||
struct TCPHeader_M_Object {
|
||||
QString type;
|
||||
HTTPRequestObject request;
|
||||
HTTPResponseObject response;
|
||||
TCPHeader_M_Object(): type("none"), request(), response() {}
|
||||
XTOSTRUCT(O(type, request, response))
|
||||
};
|
||||
//
|
||||
//
|
||||
struct HeaderObject {
|
||||
QString type;
|
||||
HeaderObject(): type("none") {}
|
||||
XTOSTRUCT(O(type))
|
||||
};
|
||||
//
|
||||
//
|
||||
struct TCPObject {
|
||||
TCPHeader_M_Object header;
|
||||
TCPObject(): header() {}
|
||||
XTOSTRUCT(O(header))
|
||||
};
|
||||
//
|
||||
//
|
||||
struct KCPObject {
|
||||
int mtu = 1350;
|
||||
int tti = 20;
|
||||
int uplinkCapacity = 5;
|
||||
int downlinkCapacity = 20;
|
||||
bool congestion = false;
|
||||
int readBufferSize = 1;
|
||||
int writeBufferSize = 1;
|
||||
HeaderObject header;
|
||||
KCPObject(): header() {}
|
||||
XTOSTRUCT(O(mtu, tti, uplinkCapacity, downlinkCapacity, congestion, readBufferSize, writeBufferSize, header))
|
||||
};
|
||||
//
|
||||
//
|
||||
struct WebSocketObject {
|
||||
QString path;
|
||||
QMap<QString, QString> headers;
|
||||
WebSocketObject(): path("/"), headers() {}
|
||||
XTOSTRUCT(O(path, headers))
|
||||
};
|
||||
//
|
||||
//
|
||||
struct HttpObject {
|
||||
QList<QString> host;
|
||||
QString path;
|
||||
HttpObject() : host(), path("/") {}
|
||||
XTOSTRUCT(O(host, path))
|
||||
};
|
||||
//
|
||||
//
|
||||
struct DomainSocketObject {
|
||||
QString path;
|
||||
DomainSocketObject(): path("/") {}
|
||||
XTOSTRUCT(O(path))
|
||||
};
|
||||
//
|
||||
//
|
||||
struct QuicObject {
|
||||
QString security;
|
||||
QString key;
|
||||
HeaderObject header;
|
||||
QuicObject(): security(""), key(""), header() {}
|
||||
XTOSTRUCT(O(security, key, header))
|
||||
};
|
||||
//
|
||||
//
|
||||
struct SockoptObject {
|
||||
int mark;
|
||||
bool tcpFastOpen;
|
||||
QString tproxy;
|
||||
SockoptObject(): mark(0), tcpFastOpen(false), tproxy("off") {}
|
||||
XTOSTRUCT(O(mark, tcpFastOpen, tproxy))
|
||||
};
|
||||
//
|
||||
//
|
||||
struct CertificateObject {
|
||||
QString usage;
|
||||
QString certificateFile;
|
||||
QString keyFile;
|
||||
QList<QString> certificate;
|
||||
QList<QString> key;
|
||||
CertificateObject(): usage(), certificateFile(), keyFile(), certificate(), key() {}
|
||||
XTOSTRUCT(O(usage, certificateFile, keyFile, certificate, key))
|
||||
};
|
||||
//
|
||||
//
|
||||
struct TLSObject {
|
||||
QString serverName;
|
||||
bool allowInsecure;
|
||||
QList<QString> alpn;
|
||||
list<CertificateObject> certificates;
|
||||
bool disableSystemRoot;
|
||||
TLSObject(): serverName(), allowInsecure(), certificates(), disableSystemRoot() {}
|
||||
XTOSTRUCT(O(serverName, allowInsecure, alpn, certificates, disableSystemRoot))
|
||||
};
|
||||
}
|
||||
//
|
||||
//
|
||||
struct SniffingObject {
|
||||
bool enabled = false;
|
||||
QList<QString> destOverride;
|
||||
SniffingObject(): enabled(), destOverride() {}
|
||||
XTOSTRUCT(O(enabled, destOverride))
|
||||
};
|
||||
//
|
||||
//
|
||||
struct StreamSettingsObject {
|
||||
QString network;
|
||||
QString security;
|
||||
TSObjects::SockoptObject sockopt;
|
||||
TSObjects::TLSObject tlsSettings;
|
||||
TSObjects::TCPObject tcpSettings;
|
||||
TSObjects::KCPObject kcpSettings;
|
||||
TSObjects::WebSocketObject wsSettings;
|
||||
TSObjects::HttpObject httpSettings;
|
||||
TSObjects::DomainSocketObject dsSettings;
|
||||
TSObjects::QuicObject quicSettings;
|
||||
StreamSettingsObject(): network("tcp"), security("none"), sockopt(), tlsSettings(), tcpSettings(), kcpSettings(), wsSettings(), httpSettings(), dsSettings(), quicSettings() {}
|
||||
XTOSTRUCT(O(network, security, sockopt, tcpSettings, tlsSettings, kcpSettings, wsSettings, httpSettings, dsSettings, quicSettings))
|
||||
};
|
||||
//
|
||||
//
|
||||
struct MuxObject {
|
||||
bool enabled;
|
||||
int concurrency;
|
||||
MuxObject(): enabled(), concurrency() {}
|
||||
XTOSTRUCT(O(enabled, concurrency))
|
||||
};
|
||||
//
|
||||
// Some protocols from: https://v2ray.com/chapter_02/02_protocols.html
|
||||
namespace Protocols
|
||||
{
|
||||
// DNS, OutBound
|
||||
struct DNSOut {
|
||||
QString network;
|
||||
QString address;
|
||||
int port;
|
||||
DNSOut(): network(""), address("0.0.0.0"), port(0) {}
|
||||
XTOSTRUCT(O(network, address, port))
|
||||
};
|
||||
//
|
||||
// MTProto, InBound || OutBound
|
||||
struct MTProtoIn {
|
||||
struct UserObject {
|
||||
QString email;
|
||||
int level;
|
||||
QString secret;
|
||||
UserObject() : email("user@domain.com"), level(0), secret("") {}
|
||||
XTOSTRUCT(O(email, level, secret))
|
||||
};
|
||||
list<UserObject> users;
|
||||
XTOSTRUCT(O(users))
|
||||
};
|
||||
//
|
||||
// Socks, OutBound
|
||||
struct SocksServerObject {
|
||||
struct UserObject {
|
||||
QString user;
|
||||
QString pass;
|
||||
int level;
|
||||
UserObject(): user("username"), pass("password"), level(0) {}
|
||||
XTOSTRUCT(O(user, pass, level))
|
||||
};
|
||||
|
||||
QString address;
|
||||
int port;
|
||||
list<UserObject> users;
|
||||
SocksServerObject(): address("0.0.0.0"), port(0), users() {}
|
||||
XTOSTRUCT(O(address, port, users))
|
||||
};
|
||||
//
|
||||
// VMess Server
|
||||
struct VMessServerObject {
|
||||
struct UserObject {
|
||||
QString id;
|
||||
int alterId;
|
||||
QString security;
|
||||
int level;
|
||||
UserObject() : id(""), alterId(64), security("auto"), level(0) {}
|
||||
XTOSTRUCT(O(id, alterId, security, level))
|
||||
};
|
||||
|
||||
QString address;
|
||||
int port;
|
||||
list<UserObject> users;
|
||||
VMessServerObject(): address(""), port(0), users() {}
|
||||
XTOSTRUCT(O(address, port, users))
|
||||
};
|
||||
//
|
||||
// ShadowSocks Server
|
||||
struct ShadowSocksServerObject {
|
||||
QString email;
|
||||
QString address;
|
||||
QString method;
|
||||
QString password;
|
||||
bool ota;
|
||||
int level;
|
||||
int port;
|
||||
ShadowSocksServerObject(): email("user@domain.com"), address("0.0.0.0"), method("aes-256-cfb"), password(""), ota(false), level(0), port(0) {}
|
||||
XTOSTRUCT(O(email, address, port, method, password, ota, level))
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
using namespace Qv2ray;
|
||||
using namespace Qv2ray::V2ConfigModels;
|
||||
using namespace Qv2ray::V2ConfigModels::Protocols;
|
||||
|
||||
#endif
|
@ -1,74 +0,0 @@
|
||||
#include "QvCoreConfigOperations.hpp"
|
||||
|
||||
namespace Qv2ray
|
||||
{
|
||||
namespace ConfigOperations
|
||||
{
|
||||
CONFIGROOT _ReadConnection(const QString &connection)
|
||||
{
|
||||
QString jsonString = StringFromFile(new QFile(connection));
|
||||
auto conf = CONFIGROOT(JsonFromString(jsonString));
|
||||
|
||||
if (conf.count() == 0) {
|
||||
LOG(MODULE_CONFIG, "WARN: Possible file corruption, failed to load file: " + connection + " --> File might be empty.")
|
||||
}
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
QMap<QString, CONFIGROOT> GetRegularConnections(QStringList connectionNames)
|
||||
{
|
||||
QMap<QString, CONFIGROOT> list;
|
||||
|
||||
for (auto conn : connectionNames) {
|
||||
list.insert(conn, _ReadConnection(QV2RAY_CONFIG_DIR + conn + QV2RAY_CONFIG_FILE_EXTENSION));
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
QMap<QString, CONFIGROOT> GetSubscriptionConnection(QString subscription)
|
||||
{
|
||||
auto _files = GetFileList(QV2RAY_SUBSCRIPTION_DIR + subscription);
|
||||
QMap<QString, CONFIGROOT> _config;
|
||||
|
||||
for (auto _file : _files) {
|
||||
// check if is proper connection file.
|
||||
if (_file.endsWith(QV2RAY_CONFIG_FILE_EXTENSION)) {
|
||||
auto confName = _file;
|
||||
// Remove the extension
|
||||
confName.chop(sizeof(QV2RAY_CONFIG_FILE_EXTENSION) - 1);
|
||||
_config[confName] = _ReadConnection(QV2RAY_SUBSCRIPTION_DIR + subscription + "/" + _file);
|
||||
} else {
|
||||
LOG(MODULE_SUBSCRIPTION, "Found a file in subscription folder but without proper suffix: " + _file)
|
||||
}
|
||||
}
|
||||
|
||||
if (_config.isEmpty()) {
|
||||
LOG(MODULE_SUBSCRIPTION, "WARN: Maybe loading an empty subscrption: " + subscription)
|
||||
}
|
||||
|
||||
return _config;
|
||||
}
|
||||
|
||||
QMap<QString, QMap<QString, CONFIGROOT>> GetSubscriptionConnections(QStringList subscriptions)
|
||||
{
|
||||
// SUB-NAME CONN-NAME CONN-ROOT
|
||||
QMap<QString, QMap<QString, CONFIGROOT>> list;
|
||||
|
||||
for (auto singleSub : subscriptions) {
|
||||
LOG(MODULE_SUBSCRIPTION, "Processing subscription: " + singleSub)
|
||||
list[singleSub] = GetSubscriptionConnection(singleSub);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
bool CheckIsComplexConfig(CONFIGROOT root)
|
||||
{
|
||||
bool cRouting = root.contains("routing");
|
||||
bool cRule = cRouting && root["routing"].toObject().contains("rules");
|
||||
bool cRules = cRule && root["routing"].toObject()["rules"].toArray().count() > 0;
|
||||
return cRules;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
#ifndef CONFIGGENERATION_H
|
||||
#define CONFIGGENERATION_H
|
||||
|
||||
#include "QvUtils.hpp"
|
||||
#include "QJsonObjectInsertMacros.h"
|
||||
|
||||
#define OUTBOUND_TAG_DIRECT "outBound_DIRECT"
|
||||
#define OUTBOUND_TAG_PROXY "outBound_PROXY"
|
||||
#define OUTBOUND_TAG_FORWARD_PROXY "_QV2RAY_FORWARD_PROXY_"
|
||||
|
||||
#define API_TAG_DEFAULT "_QV2RAY_API_"
|
||||
#define API_TAG_INBOUND "_QV2RAY_API_INBOUND_"
|
||||
|
||||
#define QV2RAY_USE_FPROXY_KEY "_QV2RAY_USE_GLOBAL_FORWARD_PROXY_"
|
||||
|
||||
#define JSON_ROOT_TRY_REMOVE(obj) if (root.contains(obj)) { root.remove(obj); }
|
||||
|
||||
namespace Qv2ray
|
||||
{
|
||||
// -------------------------- BEGIN GENERAL FUNCTIONS ----------------------------------------------
|
||||
namespace ConfigOperations
|
||||
{
|
||||
QMap<QString, CONFIGROOT> GetRegularConnections(QStringList connections);
|
||||
QMap<QString, CONFIGROOT> GetSubscriptionConnection(QString subscription);
|
||||
QMap<QString, QMap<QString, CONFIGROOT>> GetSubscriptionConnections(QStringList subscriptions);
|
||||
bool CheckIsComplexConfig(CONFIGROOT root);
|
||||
|
||||
//
|
||||
// -------------------------- BEGIN CONFIG CONVERSIONS --------------------------
|
||||
inline namespace Convertion
|
||||
{
|
||||
//int VerifyVMessProtocolString(QString vmess);
|
||||
QString DecodeSubscriptionString(QByteArray arr);
|
||||
//
|
||||
// Save Connection Config
|
||||
bool SaveConnectionConfig(CONFIGROOT obj, QString *alias, bool canOverrideExisting);
|
||||
bool SaveSubscriptionConfig(CONFIGROOT obj, const QString &subscription, QString *name);
|
||||
bool RemoveConnection(const QString &alias);
|
||||
bool RemoveSubscriptionConnection(const QString &subsName, const QString &name);
|
||||
bool RenameConnection(const QString &originalName, const QString &newName);
|
||||
bool RenameSubscription(const QString &originalName, const QString &newName);
|
||||
|
||||
// VMess URI Protocol
|
||||
CONFIGROOT ConvertConfigFromVMessString(const QString &vmess, QString *alias, QString *errMessage);
|
||||
CONFIGROOT ConvertConfigFromFile(QString sourceFilePath, bool keepInbounds);
|
||||
QString ConvertConfigToVMessString(const StreamSettingsObject &transfer, const VMessServerObject &serverConfig, const QString &alias);
|
||||
}
|
||||
|
||||
//
|
||||
// -------------------------- BEGIN CONFIG GENERATIONS ---------------------------------------------
|
||||
inline namespace Generation
|
||||
{
|
||||
ROUTING GenerateRoutes(bool enableProxy, bool cnProxy);
|
||||
ROUTERULE GenerateSingleRouteRule(QStringList list, bool isDomain, QString outboundTag, QString type = "field");
|
||||
QJsonObject GenerateDNS(bool withLocalhost, QStringList dnsServers);
|
||||
QJsonObject GenerateAPIEntry(QString tag, bool withHandler = true, bool withLogger = true, bool withStats = true);
|
||||
//
|
||||
// Outbound Protocols
|
||||
OUTBOUNDSETTING GenerateFreedomOUT(QString domainStrategy, QString redirect, int userLevel);
|
||||
OUTBOUNDSETTING GenerateBlackHoleOUT(bool useHTTP);
|
||||
OUTBOUNDSETTING GenerateShadowSocksServerOUT(QString email, QString address, int port, QString method, QString password, bool ota, int level);
|
||||
OUTBOUNDSETTING GenerateShadowSocksOUT(QList<QJsonObject> servers);
|
||||
OUTBOUNDSETTING GenerateHTTPSOCKSOut(QString address, int port, bool useAuth, QString username, QString password);
|
||||
//
|
||||
// Inbounds Protocols
|
||||
INBOUNDSETTING GenerateDokodemoIN(QString address, int port, QString network, int timeout, bool followRedirect, int userLevel);
|
||||
INBOUNDSETTING GenerateHTTPIN(QList<AccountObject> accounts, int timeout = 300, bool allowTransparent = true, int userLevel = 0);
|
||||
INBOUNDSETTING GenerateSocksIN(QString auth, QList<AccountObject> _accounts, bool udp = false, QString ip = "127.0.0.1", int userLevel = 0);
|
||||
//
|
||||
// Generate FINAL Configs
|
||||
CONFIGROOT GenerateRuntimeConfig(CONFIGROOT root);
|
||||
OUTBOUND GenerateOutboundEntry(QString protocol, OUTBOUNDSETTING settings, QJsonObject streamSettings, QJsonObject mux = QJsonObject(), QString sendThrough = "0.0.0.0", QString tag = "");
|
||||
INBOUND GenerateInboundEntry(QString listen, int port, QString protocol, INBOUNDSETTING settings, QString tag, QJsonObject sniffing = QJsonObject(), QJsonObject allocate = QJsonObject());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
using namespace Qv2ray::ConfigOperations;
|
||||
|
||||
#endif // CONFIGGENERATION_H
|
@ -1,334 +0,0 @@
|
||||
#include "QvCoreConfigOperations.hpp"
|
||||
|
||||
namespace Qv2ray
|
||||
{
|
||||
namespace ConfigOperations
|
||||
{
|
||||
inline namespace Convertion
|
||||
{
|
||||
// From https://github.com/2dust/v2rayN/wiki/%E5%88%86%E4%BA%AB%E9%93%BE%E6%8E%A5%E6%A0%BC%E5%BC%8F%E8%AF%B4%E6%98%8E(ver-2)
|
||||
QString ConvertConfigToVMessString(const StreamSettingsObject &transfer, const VMessServerObject &serverConfig, const QString &alias)
|
||||
{
|
||||
QJsonObject vmessUriRoot;
|
||||
// Constant
|
||||
vmessUriRoot["v"] = 2;
|
||||
vmessUriRoot["ps"] = alias;
|
||||
vmessUriRoot["add"] = serverConfig.address;
|
||||
vmessUriRoot["port"] = serverConfig.port;
|
||||
vmessUriRoot["id"] = serverConfig.users.front().id;
|
||||
vmessUriRoot["aid"] = serverConfig.users.front().alterId;
|
||||
vmessUriRoot["net"] = transfer.network;
|
||||
vmessUriRoot["tls"] = transfer.security;
|
||||
|
||||
if (transfer.network == "tcp") {
|
||||
vmessUriRoot["type"] = transfer.tcpSettings.header.type;
|
||||
} else if (transfer.network == "kcp") {
|
||||
vmessUriRoot["type"] = transfer.kcpSettings.header.type;
|
||||
} else if (transfer.network == "quic") {
|
||||
vmessUriRoot["type"] = transfer.quicSettings.header.type;
|
||||
vmessUriRoot["host"] = transfer.quicSettings.security;
|
||||
vmessUriRoot["path"] = transfer.quicSettings.key;
|
||||
} else if (transfer.network == "ws") {
|
||||
auto x = transfer.wsSettings.headers;
|
||||
auto host = x.contains("host");
|
||||
auto CapHost = x.contains("Host");
|
||||
auto realHost = host ? x["host"] : (CapHost ? x["Host"] : "");
|
||||
//
|
||||
vmessUriRoot["host"] = realHost;
|
||||
vmessUriRoot["path"] = transfer.wsSettings.path;
|
||||
} else if (transfer.network == "h2" || transfer.network == "http") {
|
||||
vmessUriRoot["host"] = transfer.httpSettings.host.join(",");
|
||||
vmessUriRoot["path"] = transfer.httpSettings.path;
|
||||
}
|
||||
|
||||
//
|
||||
auto vmessPart = Base64Encode(JsonToString(vmessUriRoot, QJsonDocument::JsonFormat::Compact));
|
||||
return "vmess://" + vmessPart;
|
||||
}
|
||||
QString DecodeSubscriptionString(QByteArray arr)
|
||||
{
|
||||
// Some subscription providers may use plain vmess:// saperated by lines
|
||||
// But others may use base64 of above.
|
||||
auto result = QString::fromUtf8(arr).trimmed();
|
||||
return result.startsWith("vmess://") ? result : Base64Decode(result);
|
||||
}
|
||||
//
|
||||
// Save Connection to a place, with checking if there's existing file.
|
||||
// If so, append "_N" to the name.
|
||||
bool SaveConnectionConfig(CONFIGROOT obj, QString *alias, bool canOverrideExisting)
|
||||
{
|
||||
auto str = JsonToString(obj);
|
||||
QFile *config = new QFile(QV2RAY_CONFIG_DIR + *alias + QV2RAY_CONFIG_FILE_EXTENSION);
|
||||
|
||||
// If there's already a file AND we CANNOT override existing file.
|
||||
if (config->exists() && !canOverrideExisting) {
|
||||
// Alias is a pointer to a QString.
|
||||
DeducePossibleFileName(QV2RAY_CONFIG_DIR, alias, QV2RAY_CONFIG_FILE_EXTENSION);
|
||||
config = new QFile(QV2RAY_CONFIG_DIR + *alias + QV2RAY_CONFIG_FILE_EXTENSION);
|
||||
}
|
||||
|
||||
LOG(MODULE_CONFIG, "Saving a config named: " + *alias)
|
||||
return StringToFile(&str, config);
|
||||
}
|
||||
|
||||
bool SaveSubscriptionConfig(CONFIGROOT obj, const QString &subscription, QString *name)
|
||||
{
|
||||
auto str = JsonToString(obj);
|
||||
auto fName = *name;
|
||||
|
||||
if (!IsValidFileName(fName)) {
|
||||
fName = RemoveInvalidFileName(fName);
|
||||
}
|
||||
|
||||
QFile *config = new QFile(QV2RAY_SUBSCRIPTION_DIR + subscription + "/" + fName + QV2RAY_CONFIG_FILE_EXTENSION);
|
||||
|
||||
// If there's already a file. THIS IS EXTREMELY RARE
|
||||
if (config->exists()) {
|
||||
LOG(MODULE_FILE, "Trying to overrwrite an existing subscription config file. THIS IS RARE")
|
||||
}
|
||||
|
||||
LOG(MODULE_CONFIG, "Saving a subscription named: " + fName)
|
||||
bool result = StringToFile(&str, config);
|
||||
|
||||
if (!result) {
|
||||
LOG(MODULE_FILE, "Failed to save a connection config from subscription: " + subscription + ", name: " + fName)
|
||||
}
|
||||
|
||||
*name = fName;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool RemoveConnection(const QString &alias)
|
||||
{
|
||||
QFile config(QV2RAY_CONFIG_DIR + alias + QV2RAY_CONFIG_FILE_EXTENSION);
|
||||
|
||||
if (!config.exists()) {
|
||||
LOG(MODULE_FILE, "Trying to remove a non-existing file?")
|
||||
return false;
|
||||
} else {
|
||||
return config.remove();
|
||||
}
|
||||
}
|
||||
|
||||
bool RemoveSubscriptionConnection(const QString &subsName, const QString &name)
|
||||
{
|
||||
QFile config(QV2RAY_SUBSCRIPTION_DIR + subsName + "/" + name + QV2RAY_CONFIG_FILE_EXTENSION);
|
||||
|
||||
if (!config.exists()) {
|
||||
LOG(MODULE_FILE, "Trying to remove a non-existing file?")
|
||||
return false;
|
||||
} else {
|
||||
return config.remove();
|
||||
}
|
||||
}
|
||||
|
||||
// This generates global config containing only one outbound....
|
||||
CONFIGROOT ConvertConfigFromVMessString(const QString &vmessStr, QString *alias, QString *errMessage)
|
||||
{
|
||||
#define default CONFIGROOT()
|
||||
LOG(MODULE_CONFIG, "Trying to convert from a vmess string.")
|
||||
QString vmess = vmessStr;
|
||||
|
||||
if (vmess.trimmed() != vmess) {
|
||||
LOG(MODULE_CONFIG, "VMess string has some prefix/postfix spaces, trimming.")
|
||||
vmess = vmessStr.trimmed();
|
||||
}
|
||||
|
||||
// Reset errMessage
|
||||
*errMessage = "";
|
||||
|
||||
if (!vmess.toLower().startsWith("vmess://")) {
|
||||
*errMessage = QObject::tr("VMess string should start with 'vmess://'");
|
||||
return default;
|
||||
}
|
||||
|
||||
try {
|
||||
QStringRef vmessJsonB64(&vmess, 8, vmess.length() - 8);
|
||||
auto b64Str = vmessJsonB64.toString();
|
||||
|
||||
if (b64Str.isEmpty()) {
|
||||
*errMessage = QObject::tr("VMess string should be a valid base64 string");
|
||||
return default;
|
||||
}
|
||||
|
||||
auto vmessString = Base64Decode(b64Str);
|
||||
auto jsonErr = VerifyJsonString(vmessString);
|
||||
|
||||
if (!jsonErr.isEmpty()) {
|
||||
*errMessage = jsonErr;
|
||||
return default;
|
||||
}
|
||||
|
||||
auto vmessConf = JsonFromString(vmessString);
|
||||
|
||||
if (vmessConf.isEmpty()) {
|
||||
*errMessage = QObject::tr("JSON should not be empty");
|
||||
return default;
|
||||
}
|
||||
|
||||
bool flag = true;
|
||||
// C is a quick hack...
|
||||
#define C(k) vmessConf.contains(k)
|
||||
// id, aid, port and add are mandatory fields of a vmess:// link.
|
||||
flag = flag && C("id") && C("aid") && C("port") && C("add");
|
||||
// Stream Settings
|
||||
auto net = C("net") ? vmessConf["net"].toString() : "tcp";
|
||||
|
||||
if (net == "http" || net == "ws")
|
||||
flag = flag && C("host") && C("path");
|
||||
else if (net == "domainsocket")
|
||||
flag = flag && C("path");
|
||||
else if (net == "quic")
|
||||
flag = flag && C("host") && C("type") && C("path");
|
||||
|
||||
#undef C
|
||||
//return flag ? 0 : 1;
|
||||
} catch (exception *e) {
|
||||
LOG(MODULE_IMPORT, "Failed to decode vmess string: " + QString(e->what()))
|
||||
*errMessage = e->what();
|
||||
return default;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
CONFIGROOT root;
|
||||
auto b64String = QStringRef(&vmess, 8, vmess.length() - 8).toString();
|
||||
auto vmessConf = JsonFromString(Base64Decode(b64String));
|
||||
//
|
||||
QString ps, add, id, net, type, host, path, tls;
|
||||
int port, aid;
|
||||
//
|
||||
// key = key in JSON and the variable name.
|
||||
// values = Candidate variable list, if not match, the first one is used as default.
|
||||
// [[val.size() <= 1]] is used when only the default value exists.
|
||||
// - It can be empty, if so, if the key is not in the JSON, or the value is empty, it'll report an error.
|
||||
// - Else if it contains one thing. if the key is not in the JSON, or the value is empty, it'll use that one.
|
||||
// - Else if it contains many things, when the key IS in the JSON but not in those THINGS, it'll use the first one in the THINGS
|
||||
// - Else, it'll use the value found from the JSON object.
|
||||
//
|
||||
#define empty_arg
|
||||
#define __vmess_checker__func(key, values) \
|
||||
{\
|
||||
auto val = QStringList() values;\
|
||||
if (vmessConf.contains(#key) && !vmessConf[#key].toVariant().toString().trimmed().isEmpty() \
|
||||
&& (val.size() <= 1 || val.contains(vmessConf[#key].toVariant().toString()))) {\
|
||||
key = vmessConf[#key].toVariant().toString();\
|
||||
DEBUG(MODULE_IMPORT, "Found key \"" #key "\" within the vmess object.")\
|
||||
} else if (!val.isEmpty()) {\
|
||||
key = val.first(); \
|
||||
DEBUG(MODULE_IMPORT, "Using key \"" #key "\" from the first candidate list: " + key)\
|
||||
} else{\
|
||||
*errMessage = QObject::tr(#key " does not exist."); \
|
||||
LOG(MODULE_IMPORT, "Cannot process \"" #key "\" since it's not included in the json object." ) \
|
||||
LOG(MODULE_IMPORT, " --> values: " + val.join(";")) \
|
||||
LOG(MODULE_IMPORT, " --> PS: " + ps) \
|
||||
}\
|
||||
}
|
||||
// Strict check of VMess protocol, to check if the specified value is in the correct range.
|
||||
//
|
||||
// Get Alias (AKA ps) from address and port.
|
||||
__vmess_checker__func(ps, << vmessConf["add"].toVariant().toString() + ":" + vmessConf["port"].toVariant().toString());
|
||||
__vmess_checker__func(add, empty_arg)
|
||||
__vmess_checker__func(id, empty_arg)
|
||||
__vmess_checker__func(net, << "tcp" << "http" << "h2" << "ws" << "kcp" << "domainsocket" << "quic")
|
||||
__vmess_checker__func(type, << "none" << "http" << "srtp" << "utp" << "wechat-video")
|
||||
__vmess_checker__func(path, << "")
|
||||
__vmess_checker__func(host, << "")
|
||||
__vmess_checker__func(tls, << "")
|
||||
//
|
||||
port = vmessConf["port"].toVariant().toInt();
|
||||
aid = vmessConf["aid"].toVariant().toInt();
|
||||
// Apply the settings.
|
||||
//
|
||||
// User
|
||||
VMessServerObject::UserObject user;
|
||||
user.id = id;
|
||||
user.alterId = aid;
|
||||
//
|
||||
// Server
|
||||
VMessServerObject serv;
|
||||
serv.port = port;
|
||||
serv.address = add;
|
||||
serv.users.push_back(user);
|
||||
//
|
||||
// VMess root config
|
||||
OUTBOUNDSETTING vConf;
|
||||
QJsonArray vnextArray;
|
||||
vnextArray.append(JsonFromString(StructToJsonString(serv)));
|
||||
vConf["vnext"] = vnextArray;
|
||||
//
|
||||
// Stream Settings
|
||||
StreamSettingsObject streaming;
|
||||
|
||||
if (net == "tcp") {
|
||||
streaming.tcpSettings.header.type = type;
|
||||
} else if (net == "http" || net == "h2") {
|
||||
// Fill hosts for HTTP
|
||||
for (auto _host : host.split(',')) {
|
||||
streaming.httpSettings.host.push_back(_host.trimmed());
|
||||
}
|
||||
|
||||
streaming.httpSettings.path = path;
|
||||
} else if (net == "ws") {
|
||||
streaming.wsSettings.headers["Host"] = host;
|
||||
streaming.wsSettings.path = path;
|
||||
} else if (net == "kcp") {
|
||||
streaming.kcpSettings.header.type = type;
|
||||
} else if (net == "domainsocket") {
|
||||
streaming.dsSettings.path = path;
|
||||
} else if (net == "quic") {
|
||||
streaming.quicSettings.security = host;
|
||||
streaming.quicSettings.header.type = type;
|
||||
streaming.quicSettings.key = path;
|
||||
}
|
||||
|
||||
streaming.security = tls;
|
||||
//
|
||||
// Network type
|
||||
streaming.network = net;
|
||||
//
|
||||
// WARN Mux is missing here.
|
||||
auto outbound = GenerateOutboundEntry("vmess", vConf, GetRootObject(streaming), QJsonObject(), "0.0.0.0", OUTBOUND_TAG_PROXY);
|
||||
//
|
||||
root["outbounds"] = QJsonArray() << outbound;
|
||||
// If previous alias is empty, just the PS is needed, else, append a "_"
|
||||
*alias = alias->trimmed().isEmpty() ? ps : *alias + "_" + ps;
|
||||
#undef default
|
||||
return root;
|
||||
}
|
||||
|
||||
CONFIGROOT ConvertConfigFromFile(QString sourceFilePath, bool keepInbounds)
|
||||
{
|
||||
QFile source(sourceFilePath);
|
||||
|
||||
if (!source.exists()) {
|
||||
LOG(MODULE_FILE, "Trying to import from an non-existing file.")
|
||||
return CONFIGROOT();
|
||||
}
|
||||
|
||||
auto root = CONFIGROOT(JsonFromString(StringFromFile(&source)));
|
||||
|
||||
if (!keepInbounds) {
|
||||
JSON_ROOT_TRY_REMOVE("inbounds")
|
||||
}
|
||||
|
||||
JSON_ROOT_TRY_REMOVE("log")
|
||||
JSON_ROOT_TRY_REMOVE("api")
|
||||
JSON_ROOT_TRY_REMOVE("stats")
|
||||
JSON_ROOT_TRY_REMOVE("dns")
|
||||
return root;
|
||||
}
|
||||
|
||||
bool RenameConnection(const QString &originalName, const QString &newName)
|
||||
{
|
||||
LOG(MODULE_CONFIG, "[RENAME] --> ORIGINAL: " + originalName + ", NEW: " + newName)
|
||||
return QFile::rename(QV2RAY_CONFIG_DIR + originalName + QV2RAY_CONFIG_FILE_EXTENSION, QV2RAY_CONFIG_DIR + newName + QV2RAY_CONFIG_FILE_EXTENSION);
|
||||
}
|
||||
|
||||
bool RenameSubscription(const QString &originalName, const QString &newName)
|
||||
{
|
||||
LOG(MODULE_SUBSCRIPTION, "[RENAME] --> ORIGINAL: " + originalName + ", NEW: " + newName)
|
||||
return QDir().rename(QV2RAY_SUBSCRIPTION_DIR + originalName, QV2RAY_SUBSCRIPTION_DIR + newName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,425 +0,0 @@
|
||||
#include "QvCoreConfigOperations.hpp"
|
||||
|
||||
namespace Qv2ray
|
||||
{
|
||||
namespace ConfigOperations
|
||||
{
|
||||
inline namespace Generation
|
||||
{
|
||||
// Important config generation algorithms.
|
||||
static const QStringList vLogLevels = {"none", "debug", "info", "warning", "error"};
|
||||
// -------------------------- BEGIN CONFIG GENERATIONS ----------------------------------------------------------------------------
|
||||
ROUTING GenerateRoutes(bool enableProxy, bool proxyCN)
|
||||
{
|
||||
ROUTING root;
|
||||
root.insert("domainStrategy", "IPIfNonMatch");
|
||||
//
|
||||
// For Rules list
|
||||
ROUTERULELIST rulesList;
|
||||
|
||||
if (!enableProxy) {
|
||||
// This is added to disable all proxies, as a alternative influence of #64
|
||||
rulesList.append(GenerateSingleRouteRule(QStringList() << "regexp:.*", true, OUTBOUND_TAG_DIRECT));
|
||||
}
|
||||
|
||||
// Private IPs should always NOT TO PROXY!
|
||||
rulesList.append(GenerateSingleRouteRule(QStringList() << "geoip:private", false, OUTBOUND_TAG_DIRECT));
|
||||
//
|
||||
// Check if CN needs proxy, or direct.
|
||||
rulesList.append(GenerateSingleRouteRule(QStringList() << "geoip:cn", false, proxyCN ? OUTBOUND_TAG_DIRECT : OUTBOUND_TAG_PROXY));
|
||||
rulesList.append(GenerateSingleRouteRule(QStringList() << "geosite:cn", true, proxyCN ? OUTBOUND_TAG_DIRECT : OUTBOUND_TAG_PROXY));
|
||||
//
|
||||
// As a bug fix of #64, this default rule has been disabled.
|
||||
//rulesList.append(GenerateSingleRouteRule(QStringList({"regexp:.*"}), true, globalProxy ? OUTBOUND_TAG_PROXY : OUTBOUND_TAG_DIRECT));
|
||||
root.insert("rules", rulesList);
|
||||
RROOT
|
||||
}
|
||||
|
||||
ROUTERULE GenerateSingleRouteRule(QStringList list, bool isDomain, QString outboundTag, QString type)
|
||||
{
|
||||
ROUTERULE root;
|
||||
root.insert(isDomain ? "domain" : "ip", QJsonArray::fromStringList(list));
|
||||
JADD(outboundTag, type)
|
||||
RROOT
|
||||
}
|
||||
|
||||
OUTBOUNDSETTING GenerateFreedomOUT(QString domainStrategy, QString redirect, int userLevel)
|
||||
{
|
||||
OUTBOUNDSETTING root;
|
||||
JADD(domainStrategy, redirect, userLevel)
|
||||
RROOT
|
||||
}
|
||||
OUTBOUNDSETTING GenerateBlackHoleOUT(bool useHTTP)
|
||||
{
|
||||
OUTBOUNDSETTING root;
|
||||
QJsonObject resp;
|
||||
resp.insert("type", useHTTP ? "http" : "none");
|
||||
root.insert("response", resp);
|
||||
RROOT
|
||||
}
|
||||
|
||||
OUTBOUNDSETTING GenerateShadowSocksOUT(QList<QJsonObject> servers)
|
||||
{
|
||||
OUTBOUNDSETTING root;
|
||||
QJsonArray x;
|
||||
|
||||
foreach (auto server, servers) {
|
||||
x.append(server);
|
||||
}
|
||||
|
||||
root.insert("servers", x);
|
||||
RROOT
|
||||
}
|
||||
|
||||
OUTBOUNDSETTING GenerateShadowSocksServerOUT(QString email, QString address, int port, QString method, QString password, bool ota, int level)
|
||||
{
|
||||
OUTBOUNDSETTING root;
|
||||
JADD(email, address, port, method, password, level, ota)
|
||||
RROOT
|
||||
}
|
||||
|
||||
QJsonObject GenerateDNS(bool withLocalhost, QStringList dnsServers)
|
||||
{
|
||||
QJsonObject root;
|
||||
QJsonArray servers(QJsonArray::fromStringList(dnsServers));
|
||||
|
||||
if (withLocalhost) {
|
||||
// https://github.com/lhy0403/Qv2ray/issues/64
|
||||
// The fix patch didn't touch this line below.
|
||||
//
|
||||
// Should we APPEND localhost or PUSH_FRONT localhost?
|
||||
servers.append("localhost");
|
||||
}
|
||||
|
||||
root.insert("servers", servers);
|
||||
RROOT
|
||||
}
|
||||
|
||||
INBOUNDSETTING GenerateDokodemoIN(QString address, int port, QString network, int timeout, bool followRedirect, int userLevel)
|
||||
{
|
||||
INBOUNDSETTING root;
|
||||
JADD(address, port, network, timeout, followRedirect, userLevel)
|
||||
RROOT
|
||||
}
|
||||
|
||||
INBOUNDSETTING GenerateHTTPIN(QList<AccountObject> _accounts, int timeout, bool allowTransparent, int userLevel)
|
||||
{
|
||||
INBOUNDSETTING root;
|
||||
QJsonArray accounts;
|
||||
|
||||
foreach (auto account, _accounts) {
|
||||
if (account.user.isEmpty() && account.pass.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
accounts.append(GetRootObject(account));
|
||||
}
|
||||
|
||||
if (!accounts.isEmpty()) {
|
||||
JADD(accounts)
|
||||
}
|
||||
|
||||
JADD(timeout, allowTransparent, userLevel)
|
||||
RROOT
|
||||
}
|
||||
|
||||
OUTBOUNDSETTING GenerateHTTPSOCKSOut(QString address, int port, bool useAuth, QString username, QString password)
|
||||
{
|
||||
OUTBOUNDSETTING root;
|
||||
QJsonArray servers;
|
||||
{
|
||||
QJsonObject oneServer;
|
||||
oneServer["address"] = address;
|
||||
oneServer["port"] = port;
|
||||
|
||||
if (useAuth) {
|
||||
QJsonArray users;
|
||||
QJsonObject oneUser;
|
||||
oneUser["user"] = username;
|
||||
oneUser["pass"] = password;
|
||||
users.push_back(oneUser);
|
||||
oneServer["users"] = users;
|
||||
}
|
||||
|
||||
servers.push_back(oneServer);
|
||||
}
|
||||
JADD(servers)
|
||||
RROOT
|
||||
}
|
||||
|
||||
INBOUNDSETTING GenerateSocksIN(QString auth, QList<AccountObject> _accounts, bool udp, QString ip, int userLevel)
|
||||
{
|
||||
INBOUNDSETTING root;
|
||||
QJsonArray accounts;
|
||||
|
||||
foreach (auto acc, _accounts) {
|
||||
if (acc.user.isEmpty() && acc.pass.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
accounts.append(GetRootObject(acc));
|
||||
}
|
||||
|
||||
if (!accounts.isEmpty()) {
|
||||
JADD(accounts)
|
||||
}
|
||||
|
||||
if (udp) {
|
||||
JADD(auth, udp, ip, userLevel)
|
||||
} else {
|
||||
JADD(auth, userLevel)
|
||||
}
|
||||
|
||||
RROOT
|
||||
}
|
||||
|
||||
OUTBOUND GenerateOutboundEntry(QString protocol, OUTBOUNDSETTING settings, QJsonObject streamSettings, QJsonObject mux, QString sendThrough, QString tag)
|
||||
{
|
||||
OUTBOUND root;
|
||||
JADD(sendThrough, protocol, settings, tag, streamSettings, mux)
|
||||
RROOT
|
||||
}
|
||||
|
||||
INBOUND GenerateInboundEntry(QString listen, int port, QString protocol, INBOUNDSETTING settings, QString tag, QJsonObject sniffing, QJsonObject allocate)
|
||||
{
|
||||
INBOUND root;
|
||||
LOG(MODULE_CONNECTION, "allocation is not used here.")
|
||||
Q_UNUSED(allocate)
|
||||
JADD(listen, port, protocol, settings, tag, sniffing)
|
||||
RROOT
|
||||
}
|
||||
|
||||
QJsonObject GenerateAPIEntry(QString tag, bool withHandler, bool withLogger, bool withStats)
|
||||
{
|
||||
QJsonObject root;
|
||||
QJsonArray services;
|
||||
|
||||
if (withHandler)
|
||||
services << "HandlerService";
|
||||
|
||||
if (withLogger)
|
||||
services << "LoggerService";
|
||||
|
||||
if (withStats)
|
||||
services << "StatsService";
|
||||
|
||||
JADD(services, tag)
|
||||
RROOT
|
||||
}
|
||||
|
||||
// -------------------------- END CONFIG GENERATIONS ------------------------------------------------------------------------------
|
||||
// BEGIN RUNTIME CONFIG GENERATION
|
||||
|
||||
CONFIGROOT GenerateRuntimeConfig(CONFIGROOT root)
|
||||
{
|
||||
auto gConf = GetGlobalConfig();
|
||||
QJsonObject logObject;
|
||||
//
|
||||
//logObject.insert("access", QV2RAY_CONFIG_PATH + QV2RAY_VCORE_LOG_DIRNAME + QV2RAY_VCORE_ACCESS_LOG_FILENAME);
|
||||
//logObject.insert("error", QV2RAY_CONFIG_PATH + QV2RAY_VCORE_LOG_DIRNAME + QV2RAY_VCORE_ERROR_LOG_FILENAME);
|
||||
//
|
||||
logObject.insert("loglevel", vLogLevels[gConf.logLevel]);
|
||||
root.insert("log", logObject);
|
||||
//
|
||||
auto dnsList = gConf.connectionConfig.dnsList;
|
||||
auto dnsObject = GenerateDNS(gConf.connectionConfig.withLocalDNS, dnsList);
|
||||
root.insert("dns", dnsObject);
|
||||
//
|
||||
//
|
||||
|
||||
// If inbounds list is empty we append our global configured inbounds to the config.
|
||||
if (!root.contains("inbounds") || root["inbounds"].toArray().empty()) {
|
||||
INBOUNDS inboundsList;
|
||||
|
||||
// HTTP Inbound
|
||||
if (gConf.inboundConfig.useHTTP) {
|
||||
INBOUND httpInBoundObject;
|
||||
httpInBoundObject.insert("listen", gConf.inboundConfig.listenip);
|
||||
httpInBoundObject.insert("port", gConf.inboundConfig.http_port);
|
||||
httpInBoundObject.insert("protocol", "http");
|
||||
httpInBoundObject.insert("tag", "http_IN");
|
||||
|
||||
if (gConf.inboundConfig.http_useAuth) {
|
||||
auto httpInSettings = GenerateHTTPIN(QList<AccountObject>() << gConf.inboundConfig.httpAccount);
|
||||
httpInBoundObject.insert("settings", httpInSettings);
|
||||
}
|
||||
|
||||
inboundsList.append(httpInBoundObject);
|
||||
}
|
||||
|
||||
// SOCKS Inbound
|
||||
if (gConf.inboundConfig.useSocks) {
|
||||
INBOUND socksInBoundObject;
|
||||
socksInBoundObject.insert("listen", gConf.inboundConfig.listenip);
|
||||
socksInBoundObject.insert("port", gConf.inboundConfig.socks_port);
|
||||
socksInBoundObject.insert("protocol", "socks");
|
||||
socksInBoundObject.insert("tag", "socks_IN");
|
||||
auto socksInSettings = GenerateSocksIN(gConf.inboundConfig.socks_useAuth ? "password" : "noauth",
|
||||
QList<AccountObject>() << gConf.inboundConfig.socksAccount,
|
||||
gConf.inboundConfig.socksUDP,
|
||||
gConf.inboundConfig.socksLocalIP);
|
||||
socksInBoundObject.insert("settings", socksInSettings);
|
||||
inboundsList.append(socksInBoundObject);
|
||||
}
|
||||
|
||||
root["inbounds"] = inboundsList;
|
||||
DEBUG(MODULE_CONFIG, "Added global config inbounds to the config")
|
||||
}
|
||||
|
||||
// Process every inbounds to make sure a tag is configured, fixed API 0 speed
|
||||
// issue when no tag is configured.
|
||||
INBOUNDS newTaggedInbounds = INBOUNDS(root["inbounds"].toArray());
|
||||
|
||||
for (auto i = 0; i < newTaggedInbounds.count(); i++) {
|
||||
auto _inboundItem = newTaggedInbounds[i].toObject();
|
||||
|
||||
if (!_inboundItem.contains("tag") || _inboundItem["tag"].toString().isEmpty()) {
|
||||
LOG(MODULE_CONFIG, "Adding a tag to an inbound.")
|
||||
_inboundItem["tag"] = GenerateRandomString(8);
|
||||
newTaggedInbounds[i] = _inboundItem;
|
||||
}
|
||||
}
|
||||
|
||||
root["inbounds"] = newTaggedInbounds;
|
||||
//
|
||||
//
|
||||
// Note: The part below always makes the whole functionality in trouble......
|
||||
// BE EXTREME CAREFUL when changing these code below...
|
||||
// See: https://github.com/lhy0403/Qv2ray/issues/129
|
||||
// routeCountLabel in Mainwindow makes here failed to ENOUGH-ly check the routing tables
|
||||
bool isComplex = CheckIsComplexConfig(root);
|
||||
|
||||
if (isComplex) {
|
||||
// For some config files that has routing entries already.
|
||||
// We DO NOT add extra routings.
|
||||
//
|
||||
// HOWEVER, we need to verify the QV2RAY_RULE_ENABLED entry.
|
||||
// And what's more, process (by removing unused items) from a rule object.
|
||||
ROUTING routing = ROUTING(root["routing"].toObject());
|
||||
ROUTERULELIST rules;
|
||||
LOG(MODULE_CONNECTION, "Processing an existing routing table.")
|
||||
|
||||
for (auto _rule : routing["rules"].toArray()) {
|
||||
auto _b = _rule.toObject();
|
||||
|
||||
if (_b.contains("QV2RAY_RULE_USE_BALANCER")) {
|
||||
if (_b["QV2RAY_RULE_USE_BALANCER"].toBool()) {
|
||||
// We use balancer
|
||||
_b.remove("outboundTag");
|
||||
} else {
|
||||
// We only use the normal outbound
|
||||
_b.remove("balancerTag");
|
||||
}
|
||||
} else {
|
||||
LOG(MODULE_CONFIG, "We found a rule without QV2RAY_RULE_USE_BALANCER, so don't process it.")
|
||||
}
|
||||
|
||||
// If this entry has been disabled.
|
||||
if (_b.contains("QV2RAY_RULE_ENABLED") && _b["QV2RAY_RULE_ENABLED"].toBool() == true) {
|
||||
rules.append(_b);
|
||||
} else {
|
||||
LOG(MODULE_CONFIG, "Discarded a rule as it's been set DISABLED")
|
||||
}
|
||||
}
|
||||
|
||||
routing["rules"] = rules;
|
||||
root["routing"] = routing;
|
||||
} else {
|
||||
//
|
||||
LOG(MODULE_CONNECTION, "Inserting default values to simple config")
|
||||
|
||||
if (root["outbounds"].toArray().count() != 1) {
|
||||
// There are no ROUTING but 2 or more outbounds.... This is rare, but possible.
|
||||
LOG(MODULE_CONNECTION, "WARN: This message usually indicates the config file has logic errors:")
|
||||
LOG(MODULE_CONNECTION, "WARN: --> The config file has NO routing section, however more than 1 outbounds are detected.")
|
||||
}
|
||||
|
||||
auto routeObject = GenerateRoutes(gConf.connectionConfig.enableProxy, gConf.connectionConfig.bypassCN);
|
||||
root.insert("routing", routeObject);
|
||||
//
|
||||
// Process forward proxy
|
||||
#define fpConf gConf.connectionConfig.forwardProxyConfig
|
||||
|
||||
if (fpConf.enableForwardProxy) {
|
||||
auto outboundArray = root["outbounds"].toArray();
|
||||
auto firstOutbound = outboundArray.first().toObject();
|
||||
|
||||
if (firstOutbound[QV2RAY_USE_FPROXY_KEY].toBool(false)) {
|
||||
LOG(MODULE_CONNECTION, "Applying forward proxy to current connection.")
|
||||
auto proxy = PROXYSETTING();
|
||||
proxy["tag"] = OUTBOUND_TAG_FORWARD_PROXY;
|
||||
firstOutbound["proxySettings"] = proxy;
|
||||
// FP Outbound.
|
||||
OUTBOUNDSETTING fpOutbound;
|
||||
|
||||
if (fpConf.type.toLower() == "http" || fpConf.type.toLower() == "socks") {
|
||||
fpOutbound = GenerateHTTPSOCKSOut(fpConf.serverAddress, fpConf.port, fpConf.useAuth, fpConf.username, fpConf.password);
|
||||
outboundArray.push_back(GenerateOutboundEntry(fpConf.type.toLower(), fpOutbound, QJsonObject(), QJsonObject(), "0.0.0.0", OUTBOUND_TAG_FORWARD_PROXY));
|
||||
} else {
|
||||
LOG(MODULE_CONNECTION, "WARNING: Unsupported outbound type: " + fpConf.type)
|
||||
}
|
||||
} else {
|
||||
// Remove proxySettings from firstOutbound
|
||||
firstOutbound.remove("proxySettings");
|
||||
}
|
||||
|
||||
outboundArray.replace(0, firstOutbound);
|
||||
root["outbounds"] = outboundArray;
|
||||
}
|
||||
|
||||
#undef fpConf
|
||||
OUTBOUNDS outbounds = OUTBOUNDS(root["outbounds"].toArray());
|
||||
outbounds.append(GenerateOutboundEntry("freedom", GenerateFreedomOUT("AsIs", ":0", 0), QJsonObject(), QJsonObject(), "0.0.0.0", OUTBOUND_TAG_DIRECT));
|
||||
root["outbounds"] = outbounds;
|
||||
}
|
||||
|
||||
// Let's process some api features.
|
||||
{
|
||||
//
|
||||
// Stats
|
||||
//
|
||||
root.insert("stats", QJsonObject());
|
||||
//
|
||||
// Routes
|
||||
//
|
||||
QJsonObject routing = root["routing"].toObject();
|
||||
QJsonArray routingRules = routing["rules"].toArray();
|
||||
QJsonObject APIRouteRoot;
|
||||
APIRouteRoot["type"] = "field";
|
||||
APIRouteRoot["outboundTag"] = API_TAG_DEFAULT;
|
||||
QJsonArray inboundTag;
|
||||
inboundTag.append(API_TAG_INBOUND);
|
||||
APIRouteRoot["inboundTag"] = inboundTag;
|
||||
// Add this to root.
|
||||
routingRules.push_front(APIRouteRoot);
|
||||
routing["rules"] = routingRules;
|
||||
root["routing"] = routing;
|
||||
//
|
||||
// Policy
|
||||
//
|
||||
QJsonObject policyRoot = root.contains("policy") ? root["policy"].toObject() : QJsonObject();
|
||||
QJsonObject systemPolicy = policyRoot.contains("system") ? policyRoot["system"].toObject() : QJsonObject();
|
||||
systemPolicy["statsInboundUplink"] = true;
|
||||
systemPolicy["statsInboundDownlink"] = true;
|
||||
policyRoot["system"] = systemPolicy;
|
||||
// Add this to root.
|
||||
root["policy"] = policyRoot;
|
||||
//
|
||||
// Inbounds
|
||||
//
|
||||
INBOUNDS inbounds = INBOUNDS(root["inbounds"].toArray());
|
||||
INBOUNDSETTING fakeDocodemoDoor;
|
||||
fakeDocodemoDoor["address"] = "127.0.0.1";
|
||||
QJsonObject apiInboundsRoot = GenerateInboundEntry("127.0.0.1", gConf.apiConfig.statsPort, "dokodemo-door", fakeDocodemoDoor, API_TAG_INBOUND);
|
||||
inbounds.push_front(apiInboundsRoot);
|
||||
root["inbounds"] = inbounds;
|
||||
//
|
||||
// API
|
||||
//
|
||||
root["api"] = GenerateAPIEntry(API_TAG_DEFAULT);
|
||||
}
|
||||
return root;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
109
src/QvUtils.cpp
109
src/QvUtils.cpp
@ -1,109 +0,0 @@
|
||||
#include "QvUtils.hpp"
|
||||
#include "QApplication"
|
||||
|
||||
namespace Qv2ray
|
||||
{
|
||||
namespace Utils
|
||||
{
|
||||
static bool _isQv2rayExiting = false;
|
||||
static Qv2rayConfig GlobalConfig;
|
||||
static QString ConfigDirPath;
|
||||
void SetGlobalConfig(Qv2rayConfig conf)
|
||||
{
|
||||
GlobalConfig = conf;
|
||||
QFile config(QV2RAY_CONFIG_FILE);
|
||||
QString str = StructToJsonString(GetGlobalConfig());
|
||||
StringToFile(&str, &config);
|
||||
}
|
||||
|
||||
Qv2rayConfig GetGlobalConfig()
|
||||
{
|
||||
return GlobalConfig;
|
||||
}
|
||||
|
||||
QString GetConfigDirPath()
|
||||
{
|
||||
return ConfigDirPath;
|
||||
}
|
||||
|
||||
void SetConfigDirPath(const QString *path)
|
||||
{
|
||||
ConfigDirPath = *path;
|
||||
|
||||
if (!path->endsWith("/")) {
|
||||
ConfigDirPath += "/";
|
||||
}
|
||||
}
|
||||
|
||||
void LoadGlobalConfig()
|
||||
{
|
||||
QFile file(QV2RAY_CONFIG_FILE);
|
||||
file.open(QFile::ReadOnly);
|
||||
QTextStream stream(&file);
|
||||
auto str = stream.readAll();
|
||||
auto config = StructFromJsonString<Qv2rayConfig>(str);
|
||||
SetGlobalConfig(config);
|
||||
file.close();
|
||||
}
|
||||
|
||||
void ExitQv2ray()
|
||||
{
|
||||
_isQv2rayExiting = true;
|
||||
QApplication::quit();
|
||||
}
|
||||
|
||||
bool isExiting()
|
||||
{
|
||||
return _isQv2rayExiting;
|
||||
}
|
||||
tuple<QString, int, QString> GetConnectionInfo(const CONFIGROOT &root)
|
||||
{
|
||||
bool validOutboundFound = false;
|
||||
QString host;
|
||||
int port;
|
||||
|
||||
for (auto item : root["outbounds"].toArray()) {
|
||||
OUTBOUND outBoundRoot = OUTBOUND(item.toObject());
|
||||
QString outboundType = "";
|
||||
validOutboundFound = GetOutboundData(outBoundRoot, &host, &port, &outboundType);
|
||||
|
||||
if (validOutboundFound) {
|
||||
return make_tuple(host, port, outboundType);
|
||||
} else {
|
||||
LOG(MODULE_UI, "Unknown outbound entry: " + outboundType + ", cannot deduce host and port.")
|
||||
}
|
||||
}
|
||||
|
||||
return make_tuple(QObject::tr("N/A"), 0, QObject::tr("N/A"));
|
||||
}
|
||||
|
||||
bool GetOutboundData(const OUTBOUND &out, QString *host, int *port, QString *protocol)
|
||||
{
|
||||
// Set initial values.
|
||||
*host = QObject::tr("N/A");
|
||||
*port = 0;
|
||||
*protocol = out["protocol"].toString(QObject::tr("N/A"));
|
||||
|
||||
if (*protocol == "vmess") {
|
||||
auto Server = StructFromJsonString<VMessServerObject>(JsonToString(out["settings"].toObject()["vnext"].toArray().first().toObject()));
|
||||
*host = Server.address;
|
||||
*port = Server.port;
|
||||
return true;
|
||||
} else if (*protocol == "shadowsocks") {
|
||||
auto x = JsonToString(out["settings"].toObject()["servers"].toArray().first().toObject());
|
||||
auto Server = StructFromJsonString<ShadowSocksServerObject>(x);
|
||||
*host = Server.address;
|
||||
*port = Server.port;
|
||||
return true;
|
||||
} else if (*protocol == "socks") {
|
||||
auto x = JsonToString(out["settings"].toObject()["servers"].toArray().first().toObject());
|
||||
auto Server = StructFromJsonString<SocksServerObject>(x);
|
||||
*host = Server.address;
|
||||
*port = Server.port;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
#ifndef UTILS_H
|
||||
#define UTILS_H
|
||||
|
||||
#include "Qv2rayBase.hpp"
|
||||
#include "QvHelpers.hpp"
|
||||
#include "QvCore/QvCommandLineArgs.hpp"
|
||||
|
||||
namespace Qv2ray
|
||||
{
|
||||
namespace Utils
|
||||
{
|
||||
void SetConfigDirPath(const QString *path);
|
||||
QString GetConfigDirPath();
|
||||
|
||||
void SetGlobalConfig(Qv2rayConfig conf);
|
||||
Qv2rayConfig GetGlobalConfig();
|
||||
|
||||
void LoadGlobalConfig();
|
||||
|
||||
inline QString getTag(const INBOUND &in)
|
||||
{
|
||||
return in["tag"].toString();
|
||||
}
|
||||
|
||||
inline QString getTag(const OUTBOUND &in)
|
||||
{
|
||||
return in["tag"].toString();
|
||||
}
|
||||
|
||||
void ExitQv2ray();
|
||||
bool isExiting();
|
||||
tuple<QString, int, QString> GetConnectionInfo(const CONFIGROOT &alias);
|
||||
bool GetOutboundData(const OUTBOUND &out, QString *host, int *port, QString *protocol);
|
||||
}
|
||||
}
|
||||
|
||||
using namespace Qv2ray::Utils;
|
||||
#endif // UTILS_H
|
26
src/base/GlobalInstances.hpp
Normal file
26
src/base/GlobalInstances.hpp
Normal file
@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "base/models/QvRuntimeConfig.hpp"
|
||||
#include "base/models/QvStartupConfig.hpp"
|
||||
#include "base/models/QvConfigModel.hpp"
|
||||
|
||||
#include <QTranslator>
|
||||
|
||||
// Instantiation for Qv2ray global objects.
|
||||
#ifdef QT_DEBUG
|
||||
const bool isDebugBuild = true;
|
||||
#else
|
||||
const bool isDebugBuild = false;
|
||||
#endif
|
||||
|
||||
namespace Qv2ray
|
||||
{
|
||||
// Qv2ray runtime config
|
||||
inline bool isExiting = false;
|
||||
inline QString Qv2rayConfigPath = "/";
|
||||
inline base::Qv2rayRuntimeConfig RuntimeConfig = base::Qv2rayRuntimeConfig();
|
||||
inline base::config::Qv2rayConfig GlobalConfig = base::config::Qv2rayConfig();
|
||||
inline base::QvStartupOptions StartupOption = base::QvStartupOptions();
|
||||
//
|
||||
inline std::unique_ptr<QTranslator> Qv2rayTranslator;
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
#ifndef QJSONMACROS_H
|
||||
#define QJSONMACROS_H
|
||||
#pragma once
|
||||
|
||||
#define STRINGIZE(arg) STRINGIZE1(arg)
|
||||
#define STRINGIZE1(arg) STRINGIZE2(arg)
|
||||
@ -47,5 +46,3 @@
|
||||
#define JADD(...) FOR_EACH(JADDEx, __VA_ARGS__)
|
||||
|
||||
#define RROOT return root;
|
||||
|
||||
#endif
|
131
src/base/Qv2rayBase.hpp
Normal file
131
src/base/Qv2rayBase.hpp
Normal file
@ -0,0 +1,131 @@
|
||||
#pragma once
|
||||
//
|
||||
#ifndef XTOSTRUCT_QT
|
||||
# define XTOSTRUCT_QT
|
||||
#endif
|
||||
//
|
||||
#include <QtCore>
|
||||
#include <QtGui>
|
||||
#include <QApplication>
|
||||
#include <QMap>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <ctime>
|
||||
// Base support.
|
||||
#include "base/Qv2rayLog.hpp"
|
||||
#include "base/Qv2rayFeatures.hpp"
|
||||
#include "base/JsonHelpers.hpp"
|
||||
#include "base/GlobalInstances.hpp"
|
||||
// Code Models
|
||||
#include "base/models/QvSafeType.hpp"
|
||||
#include "base/models/CoreObjectModels.hpp"
|
||||
#include "base/models/QvConfigModel.hpp"
|
||||
#include "base/models/QvConfigIdentifier.hpp"
|
||||
#include "base/models/QvStartupConfig.hpp"
|
||||
#include "base/models/QvRuntimeConfig.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace std::chrono;
|
||||
using namespace Qv2ray;
|
||||
using namespace Qv2ray::base;
|
||||
using namespace Qv2ray::base::safetype;
|
||||
using namespace Qv2ray::base::config;
|
||||
using namespace Qv2ray::base::objects;
|
||||
using namespace Qv2ray::base::objects::protocol;
|
||||
using namespace Qv2ray::base::objects::transfer;
|
||||
|
||||
// Linux users and DEs should handle the darkMode UI themselves.
|
||||
#ifndef QV2RAY_USE_BUILTIN_DARKTHEME
|
||||
# ifndef Q_OS_LINUX
|
||||
# define QV2RAY_USE_BUILTIN_DARKTHEME
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#define QV2RAY_BUILD_INFO QString(_QV2RAY_BUILD_INFO_STR_)
|
||||
#define QV2RAY_BUILD_EXTRA_INFO QString(_QV2RAY_BUILD_EXTRA_INFO_STR_)
|
||||
|
||||
extern const bool isDebugBuild;
|
||||
// Base folder suffix.
|
||||
#ifdef QT_DEBUG
|
||||
# define QV2RAY_CONFIG_DIR_SUFFIX "_debug/"
|
||||
#else
|
||||
# define QV2RAY_CONFIG_DIR_SUFFIX "/"
|
||||
#endif
|
||||
|
||||
// Get Configured Config Dir Path
|
||||
#define QV2RAY_CONFIG_DIR (Qv2ray::Qv2rayConfigPath)
|
||||
#define QV2RAY_CONFIG_FILE (QV2RAY_CONFIG_DIR + "Qv2ray.conf")
|
||||
#define QV2RAY_SUBSCRIPTION_DIR (QV2RAY_CONFIG_DIR + "subscriptions/")
|
||||
|
||||
// Get GFWList and PAC file path.
|
||||
#define QV2RAY_RULES_DIR (QV2RAY_CONFIG_DIR + "rules/")
|
||||
#define QV2RAY_RULES_GFWLIST_PATH (QV2RAY_RULES_DIR + "gfwList.txt")
|
||||
#define QV2RAY_RULES_PAC_PATH (QV2RAY_RULES_DIR + "pac.txt")
|
||||
|
||||
#define QV2RAY_CONFIG_FILE_EXTENSION ".qv2ray.json"
|
||||
#define QV2RAY_GENERATED_DIR (QV2RAY_CONFIG_DIR + "generated/")
|
||||
#define QV2RAY_GENERATED_FILE_PATH (QV2RAY_GENERATED_DIR + "config.gen.json")
|
||||
|
||||
#if ! defined (QV2RAY_DEFAULT_VCORE_PATH) && ! defined (QV2RAY_DEFAULT_VASSETS_PATH)
|
||||
# define QV2RAY_DEFAULT_VASSETS_PATH (QV2RAY_CONFIG_DIR + "vcore/")
|
||||
# ifdef Q_OS_WIN
|
||||
# define QV2RAY_DEFAULT_VCORE_PATH (QV2RAY_CONFIG_DIR + "vcore/v2ray.exe")
|
||||
# else
|
||||
# define QV2RAY_DEFAULT_VCORE_PATH (QV2RAY_CONFIG_DIR + "vcore/v2ray")
|
||||
# endif
|
||||
#elif defined (QV2RAY_DEFAULT_VCORE_PATH) && defined (QV2RAY_DEFAULT_VASSETS_PATH)
|
||||
// ---- Using user-specified VCore and VAssets path
|
||||
#else
|
||||
# error Both QV2RAY_DEFAULT_VCORE_PATH and QV2RAY_DEFAULT_VASSETS_PATH need to present when specifying the paths.
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
/// There's no tProxy thing on Windows....
|
||||
//# define QV2RAY_TPROXY_VCORE_PATH (QV2RAY_CONFIG_DIR + "vcore/v2ray.exe")
|
||||
//# define QV2RAY_TPROXY_VCTL_PATH (QV2RAY_CONFIG_DIR + "vcore/v2ctl.exe")
|
||||
#else
|
||||
# define QV2RAY_TPROXY_VCORE_PATH (QV2RAY_CONFIG_DIR + "vcore/v2ray")
|
||||
# define QV2RAY_TPROXY_VCTL_PATH (QV2RAY_CONFIG_DIR + "vcore/v2ctl")
|
||||
#endif
|
||||
|
||||
#define QV2RAY_VCORE_LOG_DIRNAME "logs/"
|
||||
#define QV2RAY_VCORE_ACCESS_LOG_FILENAME "access.log"
|
||||
#define QV2RAY_VCORE_ERROR_LOG_FILENAME "error.log"
|
||||
|
||||
// GUI TOOLS
|
||||
#define RED(obj) \
|
||||
auto _temp = obj->palette(); \
|
||||
_temp.setColor(QPalette::Text, Qt::red); \
|
||||
obj->setPalette(_temp);
|
||||
|
||||
#define BLACK(obj) \
|
||||
obj->setPalette(this->palette());
|
||||
|
||||
#define QV2RAY_UI_RESOURCES_ROOT ((GlobalConfig.uiConfig.useDarkTheme) ? QStringLiteral(":/assets/icons/ui_dark/") : QStringLiteral(":/assets/icons/ui_light/"))
|
||||
#define QICON_R(file) QIcon(QV2RAY_UI_RESOURCES_ROOT + file)
|
||||
|
||||
#define QSTRN(num) QString::number(num)
|
||||
|
||||
|
||||
#define OUTBOUND_TAG_DIRECT "outBound_DIRECT"
|
||||
#define OUTBOUND_TAG_PROXY "outBound_PROXY"
|
||||
#define OUTBOUND_TAG_FORWARD_PROXY "_QV2RAY_FORWARD_PROXY_"
|
||||
|
||||
#define API_TAG_DEFAULT "_QV2RAY_API_"
|
||||
#define API_TAG_INBOUND "_QV2RAY_API_INBOUND_"
|
||||
|
||||
#define QV2RAY_USE_FPROXY_KEY "_QV2RAY_USE_GLOBAL_FORWARD_PROXY_"
|
||||
|
||||
#define JSON_ROOT_TRY_REMOVE(obj) if (root.contains(obj)) { root.remove(obj); }
|
||||
|
||||
namespace Qv2ray
|
||||
{
|
||||
// Extra header for QvConfigUpgrade.cpp
|
||||
QJsonObject UpgradeConfig(int fromVersion, int toVersion, QJsonObject root);
|
||||
|
||||
inline void ExitQv2ray()
|
||||
{
|
||||
isExiting = true;
|
||||
QApplication::quit();
|
||||
}
|
||||
}
|
9
src/base/Qv2rayFeatures.hpp
Normal file
9
src/base/Qv2rayFeatures.hpp
Normal file
@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
// Qv2ray build features.
|
||||
//
|
||||
// Always use libgRPC++ on windows platform.
|
||||
#ifndef WITH_LIB_GRPCPP
|
||||
# ifdef _WIN32
|
||||
# define WITH_LIB_GRPCPP
|
||||
# endif
|
||||
#endif
|
52
src/base/Qv2rayLog.cpp
Normal file
52
src/base/Qv2rayLog.cpp
Normal file
@ -0,0 +1,52 @@
|
||||
#include "Qv2rayLog.hpp"
|
||||
#include "GlobalInstances.hpp"
|
||||
|
||||
namespace Qv2ray::base
|
||||
{
|
||||
// Forwarded from QvTinyLog
|
||||
static auto __loggerBuffer = std::make_unique<QStringList>();
|
||||
static auto __purgerBuffer = std::make_unique<QStringList>();
|
||||
static QMutex __loggerMutex;
|
||||
static QMutex __purgerMutex;
|
||||
|
||||
void __QV2RAY_LOG_FUNC__(int type, const std::string &func, int line, const QString &module, const QString &log)
|
||||
{
|
||||
auto logString = "[" + module + "]: " + log;
|
||||
auto funcPrepend = QString::fromStdString(func + ":" + to_string(line) + " ");
|
||||
|
||||
if (isDebugBuild) {
|
||||
// Debug build version, we only print info for DEBUG logs and print ALL info when debugLog presents,
|
||||
if (type == QV2RAY_LOG_DEBUG || StartupOption.debugLog) {
|
||||
logString = logString.prepend(funcPrepend);
|
||||
}
|
||||
} else {
|
||||
// We only process DEBUG log in Release mode
|
||||
if (type == QV2RAY_LOG_DEBUG) {
|
||||
if (StartupOption.debugLog) {
|
||||
logString = logString.prepend(funcPrepend);
|
||||
} else {
|
||||
// Discard debug log in non-debug Qv2ray version with no-debugLog mode.
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cout << logString.toStdString() << endl;
|
||||
{
|
||||
QMutexLocker _(&__loggerMutex);
|
||||
__loggerBuffer->append(logString + NEWLINE);
|
||||
}
|
||||
}
|
||||
|
||||
const QString readLastLog()
|
||||
{
|
||||
QMutexLocker _(&__purgerMutex);
|
||||
{
|
||||
QMutexLocker _(&__loggerMutex);
|
||||
__loggerBuffer.swap(__purgerBuffer);
|
||||
}
|
||||
auto result = __purgerBuffer->join("");
|
||||
__purgerBuffer->clear();
|
||||
return result;
|
||||
}
|
||||
}
|
51
src/base/Qv2rayLog.hpp
Normal file
51
src/base/Qv2rayLog.hpp
Normal file
@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <QtDebug>
|
||||
#include <QBuffer>
|
||||
using namespace std;
|
||||
|
||||
/*
|
||||
* Tiny log module.
|
||||
*/
|
||||
namespace Qv2ray::base
|
||||
{
|
||||
void __QV2RAY_LOG_FUNC__(int type, const std::string &func, int line, const QString &module, const QString &log);
|
||||
const QString readLastLog();
|
||||
}
|
||||
|
||||
#define NEWLINE "\r\n"
|
||||
|
||||
#define QV2RAY_LOG_NORMAL 0
|
||||
#define QV2RAY_LOG_DEBUG 1
|
||||
|
||||
#ifndef Q_OS_WIN
|
||||
#define Qv2ray_PRETTY_FUNCTION __PRETTY_FUNCTION__
|
||||
#else
|
||||
#define Qv2ray_PRETTY_FUNCTION __FUNCSIG__
|
||||
#endif
|
||||
|
||||
#define __LOG_IMPL(LEVEL, MODULE, MSG) __QV2RAY_LOG_FUNC__(LEVEL, Qv2ray_PRETTY_FUNCTION, __LINE__, MODULE, MSG);
|
||||
|
||||
#define LOG(MODULE, MSG) __LOG_IMPL(QV2RAY_LOG_NORMAL, (MODULE), (MSG));
|
||||
#define DEBUG(MODULE, MSG) __LOG_IMPL(QV2RAY_LOG_DEBUG, (MODULE), (MSG));
|
||||
|
||||
// Log modules used by Qv2ray
|
||||
const inline QString INIT = "INIT" ;
|
||||
const inline QString MESSAGING = "BASE-MESSAGING" ;
|
||||
const inline QString UI = "CORE-UI" ;
|
||||
const inline QString GRAPH = "CORE-UI-GRAPH" ;
|
||||
const inline QString SETTINGS = "CORE-SETTINGS" ;
|
||||
const inline QString VCORE = "CORE-VCORE" ;
|
||||
//
|
||||
const inline QString CONNECTION = "CORE-CONNECTION" ;
|
||||
const inline QString SUBSCRIPTION = "CORE-SUBSCRIPTION" ;
|
||||
const inline QString IMPORT = "CORE-IMPORT" ;
|
||||
const inline QString EXPORT = "CORE-EXPORT" ;
|
||||
//
|
||||
const inline QString NETWORK = "COMMON-NETWORK" ;
|
||||
const inline QString FILEIO = "COMMON-FILEIO" ;
|
||||
//
|
||||
const inline QString PROXY = "COMPONENT-PROXY" ;
|
||||
const inline QString UPDATE = "COMPONENT-UPDATE" ;
|
||||
const inline QString PLUGIN = "COMPONENT-PLUGIN" ;
|
313
src/base/models/CoreObjectModels.hpp
Normal file
313
src/base/models/CoreObjectModels.hpp
Normal file
@ -0,0 +1,313 @@
|
||||
#pragma once
|
||||
#include <QString>
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
#include "3rdparty/x2struct/x2struct.hpp"
|
||||
|
||||
namespace Qv2ray::base::objects
|
||||
{
|
||||
//
|
||||
// Used in config generation
|
||||
struct AccountObject {
|
||||
QString user;
|
||||
QString pass;
|
||||
XTOSTRUCT(O(user, pass))
|
||||
};
|
||||
//
|
||||
//
|
||||
struct ApiObject {
|
||||
QString tag;
|
||||
QList<QString> services;
|
||||
ApiObject() : tag("api"), services() {}
|
||||
XTOSTRUCT(O(tag, services))
|
||||
};
|
||||
//
|
||||
//
|
||||
struct SystemPolicyObject {
|
||||
bool statsInboundUplink;
|
||||
bool statsInboundDownlink;
|
||||
SystemPolicyObject() : statsInboundUplink(), statsInboundDownlink() {}
|
||||
XTOSTRUCT(O(statsInboundUplink, statsInboundDownlink))
|
||||
};
|
||||
//
|
||||
//
|
||||
struct LevelPolicyObject {
|
||||
int handshake;
|
||||
int connIdle;
|
||||
int uplinkOnly;
|
||||
int downlinkOnly;
|
||||
bool statsUserUplink;
|
||||
bool statsUserDownlink;
|
||||
int bufferSize;
|
||||
LevelPolicyObject(): handshake(), connIdle(), uplinkOnly(), downlinkOnly(), statsUserUplink(), statsUserDownlink(), bufferSize() {}
|
||||
XTOSTRUCT(O(handshake, connIdle, uplinkOnly, downlinkOnly, statsUserUplink, statsUserDownlink, bufferSize))
|
||||
};
|
||||
//
|
||||
//
|
||||
struct PolicyObject {
|
||||
QMap<QString, LevelPolicyObject> level;
|
||||
QList<SystemPolicyObject> system;
|
||||
PolicyObject(): level(), system() {}
|
||||
XTOSTRUCT(O(level, system))
|
||||
};
|
||||
//
|
||||
//
|
||||
struct RuleObject {
|
||||
// Added due to the request of @aliyuchang33
|
||||
bool QV2RAY_RULE_ENABLED;
|
||||
bool QV2RAY_RULE_USE_BALANCER;
|
||||
QString QV2RAY_RULE_TAG;
|
||||
//
|
||||
QString type;
|
||||
QList<QString> domain;
|
||||
QList<QString> ip;
|
||||
QString port;
|
||||
QString network;
|
||||
QList<QString> source;
|
||||
QList<QString> user;
|
||||
QList<QString> inboundTag;
|
||||
QList<QString> protocol;
|
||||
QString attrs;
|
||||
QString outboundTag;
|
||||
QString balancerTag;
|
||||
RuleObject() : QV2RAY_RULE_ENABLED(true), QV2RAY_RULE_USE_BALANCER(false), QV2RAY_RULE_TAG("new rule"), type("field"), domain(), ip(), port("1-65535"), network(""), source(), user(), inboundTag(), protocol(), attrs(), outboundTag(""), balancerTag("") {}
|
||||
XTOSTRUCT(O(QV2RAY_RULE_ENABLED, QV2RAY_RULE_USE_BALANCER, QV2RAY_RULE_TAG, type, domain, ip, port, network, source, user, inboundTag, protocol, attrs, outboundTag, balancerTag))
|
||||
};
|
||||
//
|
||||
//
|
||||
struct BalancerObject {
|
||||
QString tag ;
|
||||
QList<QString> selector;
|
||||
BalancerObject() : tag(), selector() {}
|
||||
XTOSTRUCT(O(tag, selector))
|
||||
};
|
||||
//
|
||||
//
|
||||
namespace transfer
|
||||
{
|
||||
struct HTTPRequestObject {
|
||||
QString version;
|
||||
QString method;
|
||||
QList<QString> path;
|
||||
QMap<QString, QList<QString>> headers;
|
||||
HTTPRequestObject(): version("1.1"), method("GET"), path(), headers() {}
|
||||
XTOSTRUCT(O(version, method, path, headers))
|
||||
};
|
||||
//
|
||||
//
|
||||
struct HTTPResponseObject {
|
||||
QString version;
|
||||
QString status;
|
||||
QString reason;
|
||||
QMap<QString, QList<QString>> headers;
|
||||
HTTPResponseObject(): version("1.1"), status("200"), reason("OK"), headers() {}
|
||||
XTOSTRUCT(O(version, status, reason, headers))
|
||||
};
|
||||
//
|
||||
//
|
||||
struct TCPHeader_M_Object {
|
||||
QString type;
|
||||
HTTPRequestObject request;
|
||||
HTTPResponseObject response;
|
||||
TCPHeader_M_Object(): type("none"), request(), response() {}
|
||||
XTOSTRUCT(O(type, request, response))
|
||||
};
|
||||
//
|
||||
//
|
||||
struct HeaderObject {
|
||||
QString type;
|
||||
HeaderObject(): type("none") {}
|
||||
XTOSTRUCT(O(type))
|
||||
};
|
||||
//
|
||||
//
|
||||
struct TCPObject {
|
||||
TCPHeader_M_Object header;
|
||||
TCPObject(): header() {}
|
||||
XTOSTRUCT(O(header))
|
||||
};
|
||||
//
|
||||
//
|
||||
struct KCPObject {
|
||||
int mtu = 1350;
|
||||
int tti = 20;
|
||||
int uplinkCapacity = 5;
|
||||
int downlinkCapacity = 20;
|
||||
bool congestion = false;
|
||||
int readBufferSize = 1;
|
||||
int writeBufferSize = 1;
|
||||
HeaderObject header;
|
||||
KCPObject(): header() {}
|
||||
XTOSTRUCT(O(mtu, tti, uplinkCapacity, downlinkCapacity, congestion, readBufferSize, writeBufferSize, header))
|
||||
};
|
||||
//
|
||||
//
|
||||
struct WebSocketObject {
|
||||
QString path;
|
||||
QMap<QString, QString> headers;
|
||||
WebSocketObject(): path("/"), headers() {}
|
||||
XTOSTRUCT(O(path, headers))
|
||||
};
|
||||
//
|
||||
//
|
||||
struct HttpObject {
|
||||
QList<QString> host;
|
||||
QString path;
|
||||
HttpObject() : host(), path("/") {}
|
||||
XTOSTRUCT(O(host, path))
|
||||
};
|
||||
//
|
||||
//
|
||||
struct DomainSocketObject {
|
||||
QString path;
|
||||
DomainSocketObject(): path("/") {}
|
||||
XTOSTRUCT(O(path))
|
||||
};
|
||||
//
|
||||
//
|
||||
struct QuicObject {
|
||||
QString security;
|
||||
QString key;
|
||||
HeaderObject header;
|
||||
QuicObject(): security(""), key(""), header() {}
|
||||
XTOSTRUCT(O(security, key, header))
|
||||
};
|
||||
//
|
||||
//
|
||||
struct SockoptObject {
|
||||
int mark;
|
||||
bool tcpFastOpen;
|
||||
QString tproxy;
|
||||
SockoptObject(): mark(0), tcpFastOpen(false), tproxy("off") {}
|
||||
XTOSTRUCT(O(mark, tcpFastOpen, tproxy))
|
||||
};
|
||||
//
|
||||
//
|
||||
struct CertificateObject {
|
||||
QString usage;
|
||||
QString certificateFile;
|
||||
QString keyFile;
|
||||
QList<QString> certificate;
|
||||
QList<QString> key;
|
||||
CertificateObject(): usage(), certificateFile(), keyFile(), certificate(), key() {}
|
||||
XTOSTRUCT(O(usage, certificateFile, keyFile, certificate, key))
|
||||
};
|
||||
//
|
||||
//
|
||||
struct TLSObject {
|
||||
QString serverName;
|
||||
bool allowInsecure;
|
||||
QList<QString> alpn;
|
||||
QList<CertificateObject> certificates;
|
||||
bool disableSystemRoot;
|
||||
TLSObject(): serverName(), allowInsecure(), certificates(), disableSystemRoot() {}
|
||||
XTOSTRUCT(O(serverName, allowInsecure, alpn, certificates, disableSystemRoot))
|
||||
};
|
||||
}
|
||||
//
|
||||
//
|
||||
struct SniffingObject {
|
||||
bool enabled = false;
|
||||
QList<QString> destOverride;
|
||||
SniffingObject(): enabled(), destOverride() {}
|
||||
XTOSTRUCT(O(enabled, destOverride))
|
||||
};
|
||||
//
|
||||
//
|
||||
struct StreamSettingsObject {
|
||||
QString network;
|
||||
QString security;
|
||||
transfer::SockoptObject sockopt;
|
||||
transfer::TLSObject tlsSettings;
|
||||
transfer::TCPObject tcpSettings;
|
||||
transfer::KCPObject kcpSettings;
|
||||
transfer::WebSocketObject wsSettings;
|
||||
transfer::HttpObject httpSettings;
|
||||
transfer::DomainSocketObject dsSettings;
|
||||
transfer::QuicObject quicSettings;
|
||||
StreamSettingsObject(): network("tcp"), security("none"), sockopt(), tlsSettings(), tcpSettings(), kcpSettings(), wsSettings(), httpSettings(), dsSettings(), quicSettings() {}
|
||||
XTOSTRUCT(O(network, security, sockopt, tcpSettings, tlsSettings, kcpSettings, wsSettings, httpSettings, dsSettings, quicSettings))
|
||||
};
|
||||
//
|
||||
//
|
||||
struct MuxObject {
|
||||
bool enabled;
|
||||
int concurrency;
|
||||
MuxObject(): enabled(), concurrency() {}
|
||||
XTOSTRUCT(O(enabled, concurrency))
|
||||
};
|
||||
//
|
||||
// Some protocols from: https://v2ray.com/chapter_02/02_protocols.html
|
||||
namespace protocol
|
||||
{
|
||||
// DNS, OutBound
|
||||
struct DNSOut {
|
||||
QString network;
|
||||
QString address;
|
||||
int port;
|
||||
DNSOut(): network(""), address("0.0.0.0"), port(0) {}
|
||||
XTOSTRUCT(O(network, address, port))
|
||||
};
|
||||
//
|
||||
// MTProto, InBound || OutBound
|
||||
struct MTProtoIn {
|
||||
struct UserObject {
|
||||
QString email;
|
||||
int level;
|
||||
QString secret;
|
||||
UserObject() : email("user@domain.com"), level(0), secret("") {}
|
||||
XTOSTRUCT(O(email, level, secret))
|
||||
};
|
||||
QList<UserObject> users;
|
||||
XTOSTRUCT(O(users))
|
||||
};
|
||||
//
|
||||
// Socks, OutBound
|
||||
struct SocksServerObject {
|
||||
struct UserObject {
|
||||
QString user;
|
||||
QString pass;
|
||||
int level;
|
||||
UserObject(): user("username"), pass("password"), level(0) {}
|
||||
XTOSTRUCT(O(user, pass, level))
|
||||
};
|
||||
|
||||
QString address;
|
||||
int port;
|
||||
QList<UserObject> users;
|
||||
SocksServerObject(): address("0.0.0.0"), port(0), users() {}
|
||||
XTOSTRUCT(O(address, port, users))
|
||||
};
|
||||
//
|
||||
// VMess Server
|
||||
struct VMessServerObject {
|
||||
struct UserObject {
|
||||
QString id;
|
||||
int alterId;
|
||||
QString security;
|
||||
int level;
|
||||
UserObject() : id(""), alterId(64), security("auto"), level(0) {}
|
||||
XTOSTRUCT(O(id, alterId, security, level))
|
||||
};
|
||||
|
||||
QString address;
|
||||
int port;
|
||||
QList<UserObject> users;
|
||||
VMessServerObject(): address(""), port(0), users() {}
|
||||
XTOSTRUCT(O(address, port, users))
|
||||
};
|
||||
//
|
||||
// ShadowSocks Server
|
||||
struct ShadowSocksServerObject {
|
||||
QString email;
|
||||
QString address;
|
||||
QString method;
|
||||
QString password;
|
||||
bool ota;
|
||||
int level;
|
||||
int port;
|
||||
ShadowSocksServerObject(): email("user@domain.com"), address("0.0.0.0"), method("aes-256-cfb"), password(""), ota(false), level(0), port(0) {}
|
||||
XTOSTRUCT(O(email, address, port, method, password, ota, level))
|
||||
};
|
||||
}
|
||||
}
|
56
src/base/models/QvConfigIdentifier.hpp
Normal file
56
src/base/models/QvConfigIdentifier.hpp
Normal file
@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
#include <QString>
|
||||
#include <QtCore>
|
||||
#include "3rdparty/x2struct/x2struct.hpp"
|
||||
namespace Qv2ray::base
|
||||
{
|
||||
struct ConnectionIdentifier {
|
||||
QString subscriptionName;
|
||||
QString connectionName;
|
||||
ConnectionIdentifier() { };
|
||||
bool isEmpty()
|
||||
{
|
||||
return connectionName.isEmpty();
|
||||
}
|
||||
ConnectionIdentifier(QString connectionName)
|
||||
{
|
||||
this->connectionName = connectionName;
|
||||
}
|
||||
ConnectionIdentifier(QString connectionName, QString subscriptionName)
|
||||
{
|
||||
this->connectionName = connectionName;
|
||||
this->subscriptionName = subscriptionName;
|
||||
}
|
||||
const QString IdentifierString() const
|
||||
{
|
||||
return connectionName + (subscriptionName.isEmpty() ? "" : " (" + subscriptionName + ")");
|
||||
}
|
||||
friend bool operator==(ConnectionIdentifier &left, ConnectionIdentifier &right)
|
||||
{
|
||||
return left.subscriptionName == right.subscriptionName && left.connectionName == right.connectionName;
|
||||
}
|
||||
friend bool operator!=(ConnectionIdentifier &left, ConnectionIdentifier &right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
friend bool operator==(ConnectionIdentifier &left, QString &right)
|
||||
{
|
||||
return left.IdentifierString() == right;
|
||||
}
|
||||
friend bool operator!=(ConnectionIdentifier &left, QString &right)
|
||||
{
|
||||
return !(left.IdentifierString() == right);
|
||||
}
|
||||
// To make QMap happy
|
||||
friend bool operator<(const ConnectionIdentifier left, const ConnectionIdentifier right)
|
||||
{
|
||||
return left.IdentifierString() < right.IdentifierString();
|
||||
}
|
||||
friend bool operator>(const ConnectionIdentifier left, const ConnectionIdentifier right)
|
||||
{
|
||||
return left.IdentifierString() > right.IdentifierString();
|
||||
}
|
||||
XTOSTRUCT(O(subscriptionName, connectionName))
|
||||
};
|
||||
}
|
||||
Q_DECLARE_METATYPE(Qv2ray::base::ConnectionIdentifier);
|
@ -1,87 +1,16 @@
|
||||
#ifndef QV2RAYBASE_H
|
||||
#define QV2RAYBASE_H
|
||||
#include <QtCore>
|
||||
#include <QtGui>
|
||||
#include <QMap>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <ctime>
|
||||
#include "QvTinyLog.hpp"
|
||||
#include "QvCoreConfigObjects.hpp"
|
||||
#pragma once
|
||||
#include "3rdparty/x2struct/x2struct.hpp"
|
||||
#include "base/models/CoreObjectModels.hpp"
|
||||
#include "base/models/QvConfigIdentifier.hpp"
|
||||
#include <chrono>
|
||||
|
||||
const int QV2RAY_CONFIG_VERSION = 8;
|
||||
|
||||
// Linux users and DEs should handle the darkMode UI themselves.
|
||||
#ifndef QV2RAY_USE_BUILTIN_DARKTHEME
|
||||
# ifndef Q_OS_LINUX
|
||||
# define QV2RAY_USE_BUILTIN_DARKTHEME
|
||||
# endif
|
||||
#endif
|
||||
|
||||
extern bool isDebug;
|
||||
// Base folder suffix.
|
||||
#ifdef QT_DEBUG
|
||||
# define QV2RAY_CONFIG_DIR_SUFFIX "_debug/"
|
||||
#else
|
||||
# define QV2RAY_CONFIG_DIR_SUFFIX "/"
|
||||
#endif
|
||||
|
||||
// Get Configured Config Dir Path
|
||||
#define QV2RAY_CONFIG_DIR (Qv2ray::Utils::GetConfigDirPath())
|
||||
#define QV2RAY_CONFIG_FILE (QV2RAY_CONFIG_DIR + "Qv2ray.conf")
|
||||
#define QV2RAY_SUBSCRIPTION_DIR (QV2RAY_CONFIG_DIR + "subscriptions/")
|
||||
|
||||
// Get GFWList and PAC file path.
|
||||
#define QV2RAY_RULES_DIR (QV2RAY_CONFIG_DIR + "rules/")
|
||||
#define QV2RAY_RULES_GFWLIST_PATH (QV2RAY_RULES_DIR + "gfwList.txt")
|
||||
#define QV2RAY_RULES_PAC_PATH (QV2RAY_RULES_DIR + "pac.txt")
|
||||
|
||||
#define QV2RAY_CONFIG_FILE_EXTENSION ".qv2ray.json"
|
||||
#define QV2RAY_GENERATED_DIR (QV2RAY_CONFIG_DIR + "generated/")
|
||||
#define QV2RAY_GENERATED_FILE_PATH (QV2RAY_GENERATED_DIR + "config.gen.json")
|
||||
|
||||
#if ! defined (QV2RAY_DEFAULT_VCORE_PATH) && ! defined (QV2RAY_DEFAULT_VASSETS_PATH)
|
||||
# define QV2RAY_DEFAULT_VASSETS_PATH (QV2RAY_CONFIG_DIR + "vcore/")
|
||||
# ifdef Q_OS_WIN
|
||||
# define QV2RAY_DEFAULT_VCORE_PATH (QV2RAY_CONFIG_DIR + "vcore/v2ray.exe")
|
||||
# else
|
||||
# define QV2RAY_DEFAULT_VCORE_PATH (QV2RAY_CONFIG_DIR + "vcore/v2ray")
|
||||
# endif
|
||||
#elif defined (QV2RAY_DEFAULT_VCORE_PATH) && defined (QV2RAY_DEFAULT_VASSETS_PATH)
|
||||
// ---- Using user-specified VCore and VAssets path
|
||||
#else
|
||||
# error Both QV2RAY_DEFAULT_VCORE_PATH and QV2RAY_DEFAULT_VASSETS_PATH need to present when specifying the paths.
|
||||
#endif
|
||||
|
||||
#define QV2RAY_VCORE_LOG_DIRNAME "logs/"
|
||||
#define QV2RAY_VCORE_ACCESS_LOG_FILENAME "access.log"
|
||||
#define QV2RAY_VCORE_ERROR_LOG_FILENAME "error.log"
|
||||
|
||||
// GUI TOOLS
|
||||
#define QV2RAY_IS_DARKTHEME (GetGlobalConfig().uiConfig.useDarkTheme)
|
||||
#define RED(obj) \
|
||||
auto _temp = obj->palette(); \
|
||||
_temp.setColor(QPalette::Text, Qt::red); \
|
||||
obj->setPalette(_temp);
|
||||
|
||||
#define BLACK(obj) \
|
||||
obj->setPalette(this->palette());
|
||||
|
||||
#define QV2RAY_UI_RESOURCES_ROOT (QV2RAY_IS_DARKTHEME ? QStringLiteral(":/assets/icons/ui_dark/") : QStringLiteral(":/assets/icons/ui_light/"))
|
||||
#define QICON_R(file) QIcon(QV2RAY_UI_RESOURCES_ROOT + file)
|
||||
|
||||
#define QSTRN(num) QString::number(num)
|
||||
|
||||
#define NEWLINE "\r\n"
|
||||
|
||||
using namespace std;
|
||||
using namespace std::chrono;
|
||||
|
||||
namespace Qv2ray
|
||||
namespace Qv2ray::base
|
||||
{
|
||||
//
|
||||
// Extra header for QvConfigUpgrade.cpp
|
||||
QJsonObject UpgradeConfig(int fromVersion, int toVersion, QJsonObject root);
|
||||
namespace config
|
||||
{
|
||||
|
||||
|
||||
struct QvBarLine {
|
||||
QString Family;
|
||||
@ -117,13 +46,11 @@ namespace Qv2ray
|
||||
XTOSTRUCT(O(Pages))
|
||||
};
|
||||
|
||||
namespace QvConfigModels
|
||||
{
|
||||
struct Qv2raySubscriptionConfig {
|
||||
time_t lastUpdated;
|
||||
float updateInterval;
|
||||
QString address;
|
||||
Qv2raySubscriptionConfig() : lastUpdated(system_clock::to_time_t(system_clock::now())), updateInterval(5), address("") { }
|
||||
Qv2raySubscriptionConfig() : lastUpdated(std::chrono::system_clock::to_time_t(std::chrono::system_clock::now())), updateInterval(5), address("") { }
|
||||
XTOSTRUCT(O(lastUpdated, updateInterval, address))
|
||||
};
|
||||
|
||||
@ -165,12 +92,12 @@ namespace Qv2ray
|
||||
bool socks_useAuth;
|
||||
bool socksUDP;
|
||||
QString socksLocalIP;
|
||||
AccountObject socksAccount;
|
||||
objects::AccountObject socksAccount;
|
||||
// HTTP
|
||||
bool useHTTP;
|
||||
int http_port;
|
||||
bool http_useAuth;
|
||||
AccountObject httpAccount;
|
||||
objects::AccountObject httpAccount;
|
||||
|
||||
Qv2rayInboundsConfig():
|
||||
listenip("127.0.0.1"), setSystemProxy(false), pacConfig(),
|
||||
@ -186,7 +113,7 @@ namespace Qv2ray
|
||||
bool useDarkTheme;
|
||||
bool useDarkTrayIcon;
|
||||
int maximumLogLines;
|
||||
Qv2rayUIConfig() : theme("Fusion"), language("en-US"), useDarkTheme(false), useDarkTrayIcon(true), maximumLogLines(500) { }
|
||||
Qv2rayUIConfig() : theme("Fusion"), language("en_US"), useDarkTheme(false), useDarkTrayIcon(true), maximumLogLines(500) { }
|
||||
XTOSTRUCT(O(theme, language, useDarkTheme, useDarkTrayIcon, maximumLogLines))
|
||||
};
|
||||
|
||||
@ -215,7 +142,7 @@ namespace Qv2ray
|
||||
//
|
||||
QString v2CorePath;
|
||||
QString v2AssetsPath;
|
||||
QvConfigIdentifier autoStartConfig;
|
||||
ConnectionIdentifier autoStartConfig;
|
||||
QString ignoredVersion;
|
||||
//
|
||||
QList<QString> configs;
|
||||
@ -253,13 +180,5 @@ namespace Qv2ray
|
||||
uiConfig,
|
||||
subscriptions, inboundConfig, connectionConfig, toolBarConfig, apiConfig))
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
using namespace std;
|
||||
using namespace Qv2ray;
|
||||
using namespace Qv2ray::V2ConfigModels;
|
||||
using namespace Qv2ray::QvConfigModels;
|
||||
|
||||
#endif // QV2RAYBASE_H
|
12
src/base/models/QvRuntimeConfig.hpp
Normal file
12
src/base/models/QvRuntimeConfig.hpp
Normal file
@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#define SET_RUNTIME_CONFIG(conf, val) RuntimeConfig.conf = val();
|
||||
#define RESTORE_RUNTIME_CONFIG(conf, func) func(RuntimeConfig.conf);
|
||||
|
||||
namespace Qv2ray::base
|
||||
{
|
||||
struct Qv2rayRuntimeConfig {
|
||||
//
|
||||
bool screenShotHideQv2ray = false;
|
||||
};
|
||||
}
|
31
src/base/models/QvSafeType.hpp
Normal file
31
src/base/models/QvSafeType.hpp
Normal file
@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
|
||||
#define SAFE_TYPEDEF(Base, name) \
|
||||
class name : public Base { \
|
||||
public: \
|
||||
template <class... Args> \
|
||||
explicit name (Args... args) : Base(args...) {} \
|
||||
const Base& raw() const { return *this; } \
|
||||
};
|
||||
|
||||
namespace Qv2ray::base::safetype
|
||||
{
|
||||
// To prevent anonying QJsonObject misuse
|
||||
SAFE_TYPEDEF(QJsonObject, INBOUNDSETTING)
|
||||
SAFE_TYPEDEF(QJsonObject, OUTBOUNDSETTING)
|
||||
SAFE_TYPEDEF(QJsonObject, INBOUND)
|
||||
SAFE_TYPEDEF(QJsonObject, OUTBOUND)
|
||||
SAFE_TYPEDEF(QJsonObject, CONFIGROOT)
|
||||
SAFE_TYPEDEF(QJsonObject, PROXYSETTING)
|
||||
//
|
||||
SAFE_TYPEDEF(QJsonArray, INOUTLIST)
|
||||
SAFE_TYPEDEF(INOUTLIST, OUTBOUNDS)
|
||||
SAFE_TYPEDEF(INOUTLIST, INBOUNDS)
|
||||
SAFE_TYPEDEF(QJsonObject, ROUTING)
|
||||
SAFE_TYPEDEF(QJsonObject, ROUTERULE)
|
||||
SAFE_TYPEDEF(QJsonArray, ROUTERULELIST)
|
||||
}
|
18
src/base/models/QvStartupConfig.hpp
Normal file
18
src/base/models/QvStartupConfig.hpp
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
namespace Qv2ray
|
||||
{
|
||||
namespace base
|
||||
{
|
||||
struct QvStartupOptions {
|
||||
/// No API subsystem
|
||||
bool noAPI;
|
||||
/// Explicitly run as root user.
|
||||
bool forceRunAsRootUser;
|
||||
/// Enable Debug Log.
|
||||
bool debugLog;
|
||||
/// Enable Network toolbar plugin.
|
||||
bool enableToolbarPlguin;
|
||||
};
|
||||
}
|
||||
}
|
61
src/common/CommandArgs.cpp
Normal file
61
src/common/CommandArgs.cpp
Normal file
@ -0,0 +1,61 @@
|
||||
#include "CommandArgs.hpp"
|
||||
#include "base/Qv2rayBase.hpp"
|
||||
|
||||
namespace Qv2ray::common
|
||||
{
|
||||
QvCommandArgParser::QvCommandArgParser() : QObject(),
|
||||
noAPIOption("noAPI", QObject::tr("Disable gRPC API subsystems.")),
|
||||
runAsRootOption("I-just-wanna-run-with-root", QObject::tr("Explicitly run Qv2ray as root.")),
|
||||
debugOption("debug", QObject::tr("Enable Debug Output")),
|
||||
withToolbarOption("withToolbarPlugin", QObject::tr("Enable Qv2ray network toolbar plugin")),
|
||||
//
|
||||
helpOption("FAKE"), versionOption("FAKE")
|
||||
{
|
||||
parser.setApplicationDescription(QObject::tr("Qv2ray - A cross-platform Qt frontend for V2ray."));
|
||||
parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
|
||||
//
|
||||
parser.addOption(noAPIOption);
|
||||
parser.addOption(runAsRootOption);
|
||||
parser.addOption(debugOption);
|
||||
parser.addOption(withToolbarOption);
|
||||
helpOption = parser.addHelpOption();
|
||||
versionOption = parser.addVersionOption();
|
||||
}
|
||||
|
||||
CommandLineParseResult QvCommandArgParser::ParseCommandLine(QString *errorMessage)
|
||||
{
|
||||
if (!parser.parse(QCoreApplication::arguments())) {
|
||||
*errorMessage = parser.errorText();
|
||||
return CommandLineError;
|
||||
}
|
||||
|
||||
if (parser.isSet(versionOption))
|
||||
return CommandLineVersionRequested;
|
||||
|
||||
if (parser.isSet(helpOption))
|
||||
return CommandLineHelpRequested;
|
||||
|
||||
if (parser.isSet(noAPIOption)) {
|
||||
DEBUG(INIT, "noAPIOption is set.")
|
||||
StartupOption.noAPI = true;
|
||||
}
|
||||
|
||||
if (parser.isSet(runAsRootOption)) {
|
||||
DEBUG(INIT, "runAsRootOption is set.")
|
||||
StartupOption.forceRunAsRootUser = true;
|
||||
}
|
||||
|
||||
if (parser.isSet(debugOption)) {
|
||||
DEBUG(INIT, "debugOption is set.")
|
||||
StartupOption.debugLog = true;
|
||||
}
|
||||
|
||||
if (parser.isSet(withToolbarOption)) {
|
||||
DEBUG(INIT, "withToolbarOption is set.")
|
||||
StartupOption.enableToolbarPlguin = true;
|
||||
}
|
||||
|
||||
return CommandLineOk;
|
||||
}
|
||||
|
||||
}
|
35
src/common/CommandArgs.hpp
Normal file
35
src/common/CommandArgs.hpp
Normal file
@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <QCommandLineParser>
|
||||
|
||||
namespace Qv2ray::common
|
||||
{
|
||||
enum CommandLineParseResult {
|
||||
CommandLineOk,
|
||||
CommandLineError,
|
||||
CommandLineVersionRequested,
|
||||
CommandLineHelpRequested
|
||||
};
|
||||
class QvCommandArgParser : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QvCommandArgParser();
|
||||
CommandLineParseResult ParseCommandLine(QString *errorMessage);
|
||||
const QCommandLineParser *Parser()
|
||||
{
|
||||
return &parser;
|
||||
}
|
||||
|
||||
private:
|
||||
QCommandLineParser parser;
|
||||
QCommandLineOption noAPIOption;
|
||||
QCommandLineOption runAsRootOption;
|
||||
QCommandLineOption debugOption;
|
||||
QCommandLineOption withToolbarOption;
|
||||
QCommandLineOption helpOption;
|
||||
QCommandLineOption versionOption;
|
||||
};
|
||||
}
|
||||
|
||||
using namespace Qv2ray::common;
|
@ -1,8 +1,9 @@
|
||||
#include "QvHTTPRequestHelper.hpp"
|
||||
#include "HTTPRequestHelper.hpp"
|
||||
#include <QByteArray>
|
||||
#include <QNetworkProxy>
|
||||
#include "base/Qv2rayBase.hpp"
|
||||
|
||||
namespace Qv2ray
|
||||
namespace Qv2ray::common
|
||||
{
|
||||
QvHttpRequestHelper::QvHttpRequestHelper() : reply()
|
||||
{
|
||||
@ -18,7 +19,7 @@ namespace Qv2ray
|
||||
QUrl qUrl = QUrl(url);
|
||||
|
||||
if (!qUrl.isValid()) {
|
||||
LOG(MODULE_NETWORK, "Provided URL is invalid: " + url)
|
||||
LOG(NETWORK, "Provided URL is invalid: " + url)
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -28,7 +29,7 @@ namespace Qv2ray
|
||||
|
||||
void QvHttpRequestHelper::setHeader(const QByteArray &key, const QByteArray &value)
|
||||
{
|
||||
DEBUG(MODULE_NETWORK, "Adding HTTP request header: " + key + ":" + value)
|
||||
DEBUG(NETWORK, "Adding HTTP request header: " + key + ":" + value)
|
||||
request.setRawHeader(key, value);
|
||||
}
|
||||
|
||||
@ -43,7 +44,7 @@ namespace Qv2ray
|
||||
accessManager.setProxy(QNetworkProxy(QNetworkProxy::ProxyType::NoProxy));
|
||||
}
|
||||
|
||||
LOG(MODULE_NETWORK, "Sync get is using system proxy settings")
|
||||
LOG(NETWORK, "Sync get is using system proxy settings")
|
||||
request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
|
||||
reply = accessManager.get(request);
|
||||
connect(reply, &QNetworkReply::finished, this, &QvHttpRequestHelper::onRequestFinished);
|
||||
@ -103,13 +104,13 @@ namespace Qv2ray
|
||||
|
||||
void QvHttpRequestHelper::onRequestFinished()
|
||||
{
|
||||
LOG(MODULE_NETWORK, "Network request errcode: " + QSTRN(reply->error()))
|
||||
LOG(NETWORK, "Network request errcode: " + QSTRN(reply->error()))
|
||||
emit httpRequestFinished(this->data);
|
||||
}
|
||||
|
||||
void QvHttpRequestHelper::onReadyRead()
|
||||
{
|
||||
DEBUG(MODULE_NETWORK, "A request is now ready read")
|
||||
DEBUG(NETWORK, "A request is now ready read")
|
||||
this->data += reply->readAll();
|
||||
}
|
||||
}
|
@ -16,16 +16,14 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef QVHTTPREQUESTHELPER_H
|
||||
#define QVHTTPREQUESTHELPER_H
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QNetworkRequest>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkAccessManager>
|
||||
#include "QvUtils.hpp"
|
||||
|
||||
namespace Qv2ray
|
||||
namespace Qv2ray::common
|
||||
{
|
||||
class QvHttpRequestHelper : public QObject
|
||||
{
|
||||
@ -61,4 +59,4 @@ namespace Qv2ray
|
||||
};
|
||||
}
|
||||
|
||||
#endif // QVHTTPREQUESTHELPER_H
|
||||
using namespace Qv2ray::common;
|
133
src/common/LogHighlighter.cpp
Normal file
133
src/common/LogHighlighter.cpp
Normal file
@ -0,0 +1,133 @@
|
||||
#include "LogHighlighter.hpp"
|
||||
#include "common/QvHelpers.hpp"
|
||||
|
||||
#define TO_EOL "(([\\s\\S]*)|([\\d\\D]*)|([\\w\\W]*))$"
|
||||
|
||||
namespace Qv2ray::common
|
||||
{
|
||||
SyntaxHighlighter::SyntaxHighlighter(bool darkMode, QTextDocument *parent)
|
||||
: QSyntaxHighlighter(parent)
|
||||
{
|
||||
HighlightingRule rule;
|
||||
keywordFormat.setForeground(darkMode ? Qt::darkMagenta : Qt::magenta);
|
||||
keywordFormat.setFontWeight(QFont::Bold);
|
||||
const QString keywordPatterns[] = {
|
||||
"tcp", "udp"
|
||||
};
|
||||
|
||||
for (const QString &pattern : keywordPatterns) {
|
||||
rule.pattern = QRegularExpression(pattern);
|
||||
rule.format = keywordFormat;
|
||||
highlightingRules.append(rule);
|
||||
}
|
||||
|
||||
if (darkMode) {
|
||||
ipHostFormat.setForeground(Qt::yellow);
|
||||
warningFormat.setForeground(QColor(230, 180, 0));
|
||||
} else {
|
||||
ipHostFormat.setForeground(Qt::black);
|
||||
ipHostFormat.setFontWeight(QFont::Bold);
|
||||
warningFormat.setForeground(Qt::white);
|
||||
warningFormat.setBackground(QColor(255, 128, 30));
|
||||
}
|
||||
|
||||
//
|
||||
dateFormat.setFontWeight(QFont::Bold);
|
||||
dateFormat.setForeground(darkMode ? Qt::cyan : Qt::darkCyan);
|
||||
rule.pattern = QRegularExpression("\\d\\d\\d\\d/\\d\\d/\\d\\d");
|
||||
rule.format = dateFormat;
|
||||
highlightingRules.append(rule);
|
||||
//
|
||||
timeFormat.setFontWeight(QFont::Bold);
|
||||
timeFormat.setForeground(darkMode ? Qt::cyan : Qt::darkCyan);
|
||||
rule.pattern = QRegularExpression("\\d\\d:\\d\\d:\\d\\d");
|
||||
rule.format = timeFormat;
|
||||
highlightingRules.append(rule);
|
||||
//
|
||||
debugFormat.setForeground(Qt::darkGray);
|
||||
rule.pattern = QRegularExpression("\\[[Dd]ebug\\]" TO_EOL);
|
||||
rule.format = debugFormat;
|
||||
highlightingRules.append(rule);
|
||||
//
|
||||
infoFormat.setForeground(darkMode ? Qt::lightGray : Qt::darkCyan);
|
||||
rule.pattern = QRegularExpression("\\[[Ii]nfo\\]" TO_EOL);
|
||||
rule.format = infoFormat;
|
||||
highlightingRules.append(rule);
|
||||
//
|
||||
//
|
||||
{
|
||||
// IP IPv6 Host;
|
||||
rule.pattern = QRegularExpression(REGEX_IPV4_ADDR ":" REGEX_PORT_NUMBER);
|
||||
rule.pattern.setPatternOptions(QRegularExpression::PatternOption::ExtendedPatternSyntaxOption);
|
||||
rule.format = ipHostFormat;
|
||||
highlightingRules.append(rule);
|
||||
//
|
||||
rule.pattern = QRegularExpression(REGEX_IPV6_ADDR ":" REGEX_PORT_NUMBER);
|
||||
rule.pattern.setPatternOptions(QRegularExpression::PatternOption::ExtendedPatternSyntaxOption);
|
||||
rule.format = ipHostFormat;
|
||||
highlightingRules.append(rule);
|
||||
//
|
||||
rule.pattern = QRegularExpression("([a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,6}(/|):" REGEX_PORT_NUMBER);
|
||||
rule.pattern.setPatternOptions(QRegularExpression::PatternOption::ExtendedPatternSyntaxOption);
|
||||
rule.format = ipHostFormat;
|
||||
highlightingRules.append(rule);
|
||||
}
|
||||
//
|
||||
//
|
||||
acceptedFormat.setForeground(Qt::darkGreen);
|
||||
rule.pattern = QRegularExpression("\\saccepted\\s");
|
||||
rule.format = acceptedFormat;
|
||||
highlightingRules.append(rule);
|
||||
//
|
||||
rejectedFormat.setFontWeight(QFont::Bold);
|
||||
rejectedFormat.setBackground(Qt::red);
|
||||
rejectedFormat.setForeground(Qt::white);
|
||||
rule.pattern = QRegularExpression("\\srejected\\s" TO_EOL);
|
||||
rule.format = rejectedFormat;
|
||||
highlightingRules.append(rule);
|
||||
//
|
||||
v2rayComponentFormat.setFontWeight(QFont::Bold);
|
||||
v2rayComponentFormat.setForeground(darkMode ? Qt::darkGreen : Qt::darkYellow);
|
||||
rule.pattern = QRegularExpression(" v2ray.com(/(\\w*))*: ");
|
||||
rule.format = v2rayComponentFormat;
|
||||
highlightingRules.append(rule);
|
||||
//
|
||||
warningFormat.setFontWeight(QFont::Bold);
|
||||
rule.pattern = QRegularExpression("\\[[Ww]arning\\]" TO_EOL);
|
||||
rule.format = warningFormat;
|
||||
highlightingRules.append(rule);
|
||||
//
|
||||
failedFormat.setFontWeight(QFont::Bold);
|
||||
failedFormat.setBackground(Qt::red);
|
||||
failedFormat.setForeground(Qt::white);
|
||||
rule.pattern = QRegularExpression("failed");
|
||||
rule.format = failedFormat;
|
||||
highlightingRules.append(rule);
|
||||
//
|
||||
qvAppLogFormat.setFontWeight(QFont::Bold);
|
||||
qvAppLogFormat.setForeground(darkMode ? Qt::cyan : Qt::darkCyan);
|
||||
rule.pattern = QRegularExpression("\\[[A-Z]*\\]:");
|
||||
rule.format = qvAppLogFormat;
|
||||
highlightingRules.append(rule);
|
||||
//
|
||||
qvAppDebugLogFormat.setFontWeight(QFont::Bold);
|
||||
qvAppDebugLogFormat.setForeground(darkMode ? Qt::yellow : Qt::darkYellow);
|
||||
rule.pattern = QRegularExpression("\\[\\[DEBUG\\] - [A-Z]*\\]:");
|
||||
rule.format = qvAppDebugLogFormat;
|
||||
highlightingRules.append(rule);
|
||||
}
|
||||
|
||||
void SyntaxHighlighter::highlightBlock(const QString &text)
|
||||
{
|
||||
for (const HighlightingRule &rule : qAsConst(highlightingRules)) {
|
||||
QRegularExpressionMatchIterator matchIterator = rule.pattern.globalMatch(text);
|
||||
|
||||
while (matchIterator.hasNext()) {
|
||||
QRegularExpressionMatch match = matchIterator.next();
|
||||
setFormat(match.capturedStart(), match.capturedLength(), rule.format);
|
||||
}
|
||||
}
|
||||
|
||||
setCurrentBlockState(0);
|
||||
}
|
||||
}
|
@ -48,19 +48,14 @@
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef HIGHLIGHTER_H
|
||||
#define HIGHLIGHTER_H
|
||||
|
||||
#pragma once
|
||||
#include <QSyntaxHighlighter>
|
||||
#include <QTextCharFormat>
|
||||
#include <QRegularExpression>
|
||||
#include <QTextDocument>
|
||||
#include "QvUtils.hpp"
|
||||
|
||||
namespace Qv2ray
|
||||
namespace Qv2ray::common
|
||||
{
|
||||
namespace Components
|
||||
{
|
||||
class SyntaxHighlighter : public QSyntaxHighlighter
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -93,9 +88,6 @@ namespace Qv2ray
|
||||
QTextCharFormat qvAppLogFormat;
|
||||
QTextCharFormat qvAppDebugLogFormat;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
using namespace Qv2ray::Components;
|
||||
|
||||
#endif // HIGHLIGHTER_H
|
||||
using namespace Qv2ray::common;
|
@ -22,8 +22,7 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef QJSONMODEL_H
|
||||
#define QJSONMODEL_H
|
||||
#pragma once
|
||||
|
||||
#include <QAbstractItemModel>
|
||||
#include <QJsonDocument>
|
||||
@ -100,5 +99,3 @@ class QJsonModel : public QAbstractItemModel
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif // QJSONMODEL_H
|
212
src/common/QvHelpers.cpp
Normal file
212
src/common/QvHelpers.cpp
Normal file
@ -0,0 +1,212 @@
|
||||
#include "common/QvHelpers.hpp"
|
||||
#include <QQueue>
|
||||
|
||||
namespace Qv2ray::common
|
||||
{
|
||||
const QString GenerateRandomString(int len)
|
||||
{
|
||||
const QString possibleCharacters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
|
||||
QString randomString;
|
||||
|
||||
for (int i = 0; i < len; ++i) {
|
||||
uint rand = QRandomGenerator::system()->generate();
|
||||
uint max = static_cast<uint>(possibleCharacters.length());
|
||||
QChar nextChar = possibleCharacters[rand % max];
|
||||
randomString.append(nextChar);
|
||||
}
|
||||
|
||||
return randomString;
|
||||
}
|
||||
|
||||
QString StringFromFile(const QString &filePath)
|
||||
{
|
||||
QFile f(filePath);
|
||||
return StringFromFile(&f);
|
||||
}
|
||||
|
||||
QString StringFromFile(QFile *source)
|
||||
{
|
||||
source->open(QFile::ReadOnly);
|
||||
QTextStream stream(source);
|
||||
QString str = stream.readAll();
|
||||
source->close();
|
||||
return str;
|
||||
}
|
||||
|
||||
bool StringToFile(const QString *text, QFile *targetFile)
|
||||
{
|
||||
bool override = targetFile->exists();
|
||||
targetFile->open(QFile::WriteOnly);
|
||||
QTextStream stream(targetFile);
|
||||
stream << *text << endl;
|
||||
stream.flush();
|
||||
targetFile->close();
|
||||
return override;
|
||||
}
|
||||
|
||||
QJsonObject JSONFromFile(QFile *sourceFile)
|
||||
{
|
||||
QString json = StringFromFile(sourceFile);
|
||||
return JsonFromString(json);
|
||||
}
|
||||
|
||||
QString JsonToString(QJsonObject json, QJsonDocument::JsonFormat format)
|
||||
{
|
||||
QJsonDocument doc;
|
||||
doc.setObject(json);
|
||||
return doc.toJson(format);
|
||||
}
|
||||
|
||||
QString JsonToString(QJsonArray array, QJsonDocument::JsonFormat format)
|
||||
{
|
||||
QJsonDocument doc;
|
||||
doc.setArray(array);
|
||||
return doc.toJson(format);
|
||||
}
|
||||
|
||||
QString VerifyJsonString(const QString &source)
|
||||
{
|
||||
QJsonParseError error;
|
||||
QJsonDocument doc = QJsonDocument::fromJson(source.toUtf8(), &error);
|
||||
Q_UNUSED(doc)
|
||||
|
||||
if (error.error == QJsonParseError::NoError) {
|
||||
return "";
|
||||
} else {
|
||||
LOG(UI, "WARNING: Json parse returns: " + error.errorString())
|
||||
return error.errorString();
|
||||
}
|
||||
}
|
||||
|
||||
QJsonObject JsonFromString(QString string)
|
||||
{
|
||||
QJsonDocument doc = QJsonDocument::fromJson(string.toUtf8());
|
||||
return doc.object();
|
||||
}
|
||||
|
||||
QString Base64Encode(QString string)
|
||||
{
|
||||
QByteArray ba = string.toUtf8();
|
||||
return ba.toBase64();
|
||||
}
|
||||
|
||||
QString Base64Decode(QString string)
|
||||
{
|
||||
QByteArray ba = string.toUtf8();
|
||||
return QString(QByteArray::fromBase64(ba));
|
||||
}
|
||||
|
||||
QStringList SplitLines(const QString &_string)
|
||||
{
|
||||
return _string.split(QRegExp("[\r\n]"), QString::SkipEmptyParts);
|
||||
}
|
||||
|
||||
list<string> SplitLines_std(const QString &_string)
|
||||
{
|
||||
list<string> list;
|
||||
|
||||
for (auto line : _string.split(QRegExp("[\r\n]"), QString::SkipEmptyParts)) {
|
||||
list.push_back(line.toStdString());
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
QStringList GetFileList(QDir dir)
|
||||
{
|
||||
return dir.entryList(QStringList() << "*" << "*.*", QDir::Hidden | QDir::Files);
|
||||
}
|
||||
|
||||
bool FileExistsIn(QDir dir, QString fileName)
|
||||
{
|
||||
return GetFileList(dir).contains(fileName);
|
||||
}
|
||||
|
||||
void QvMessageBoxWarn(QWidget *parent, QString title, QString text)
|
||||
{
|
||||
QMessageBox::warning(parent, title, text, QMessageBox::Ok | QMessageBox::Default, 0);
|
||||
}
|
||||
|
||||
void QvMessageBoxInfo(QWidget *parent, QString title, QString text)
|
||||
{
|
||||
QMessageBox::information(parent, title, text, QMessageBox::Ok | QMessageBox::Default, 0);
|
||||
}
|
||||
|
||||
QMessageBox::StandardButton QvMessageBoxAsk(QWidget *parent, QString title, QString text, QMessageBox::StandardButton extraButtons)
|
||||
{
|
||||
return QMessageBox::question(parent, title, text, QMessageBox::Yes | QMessageBox::No | extraButtons);
|
||||
}
|
||||
|
||||
QString FormatBytes(long long bytes)
|
||||
{
|
||||
char str[64];
|
||||
const char *sizes[5] = { "B", "KB", "MB", "GB", "TB" };
|
||||
int i;
|
||||
double dblByte = bytes;
|
||||
|
||||
for (i = 0; i < 5 && bytes >= 1024; i++, bytes /= 1024)
|
||||
dblByte = bytes / 1024.0;
|
||||
|
||||
sprintf(str, "%.2f", dblByte);
|
||||
return strcat(strcat(str, " "), sizes[i]);
|
||||
}
|
||||
|
||||
bool IsValidFileName(const QString &fileName)
|
||||
{
|
||||
QString name = fileName;
|
||||
return name == RemoveInvalidFileName(fileName);
|
||||
}
|
||||
|
||||
QString RemoveInvalidFileName(const QString &fileName)
|
||||
{
|
||||
std::string _name = fileName.toStdString();
|
||||
std::replace_if(_name.begin(), _name.end(), [](char c) {
|
||||
return std::string::npos != string(R"("/\?%&^*;:|><)").find(c);
|
||||
}, '_');
|
||||
return QString::fromStdString(_name);
|
||||
}
|
||||
|
||||
/// This returns a file name without extensions.
|
||||
void DeducePossibleFileName(const QString &baseDir, QString *fileName, const QString &extension)
|
||||
{
|
||||
int i = 1;
|
||||
|
||||
if (!QDir(baseDir).exists()) {
|
||||
QDir(baseDir).mkpath(baseDir);
|
||||
LOG(FILEIO, "Making path: " + baseDir)
|
||||
}
|
||||
|
||||
while (true) {
|
||||
if (!QFile(baseDir + "/" + fileName + "_" + QSTRN(i) + extension).exists()) {
|
||||
*fileName = *fileName + "_" + QSTRN(i);
|
||||
return;
|
||||
} else {
|
||||
DEBUG(FILEIO, "File with name: " + *fileName + "_" + QSTRN(i) + extension + " already exists")
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
QStringList ConvertQStringList(const QList<string> &stdListString)
|
||||
{
|
||||
QStringList listQt;
|
||||
listQt.reserve(stdListString.size());
|
||||
|
||||
for (const std::string &s : stdListString) {
|
||||
listQt.append(QString::fromStdString(s));
|
||||
}
|
||||
|
||||
return listQt;
|
||||
}
|
||||
std::list<string> ConvertStdStringList(const QStringList &qStringList)
|
||||
{
|
||||
std::list<string> stdList;
|
||||
|
||||
for (auto &s : qStringList) {
|
||||
stdList.push_back(s.toStdString());
|
||||
}
|
||||
|
||||
return stdList;
|
||||
}
|
||||
}
|
126
src/common/QvHelpers.hpp
Normal file
126
src/common/QvHelpers.hpp
Normal file
@ -0,0 +1,126 @@
|
||||
#pragma once
|
||||
|
||||
#include "base/Qv2rayBase.hpp"
|
||||
#include <QMessageBox>
|
||||
|
||||
#define REGEX_IPV6_ADDR R"(\[\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*\])"
|
||||
#define REGEX_IPV4_ADDR R"((\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5]))"
|
||||
#define REGEX_PORT_NUMBER R"(([0-9]|[1-9]\d{1,3}|[1-5]\d{4}|6[0-5]{2}[0-3][0-5])*)"
|
||||
|
||||
namespace Qv2ray::common
|
||||
{
|
||||
QStringList GetFileList(QDir dir);
|
||||
QString Base64Encode(QString string);
|
||||
QString Base64Decode(QString string);
|
||||
QStringList SplitLines(const QString &str);
|
||||
list<string> SplitLines_std(const QString &_string);
|
||||
bool FileExistsIn(QDir dir, QString fileName);
|
||||
const QString GenerateRandomString(int len = 12);
|
||||
//
|
||||
void QvMessageBoxWarn(QWidget *parent, QString title, QString text);
|
||||
void QvMessageBoxInfo(QWidget *parent, QString title, QString text);
|
||||
QMessageBox::StandardButton QvMessageBoxAsk(QWidget *parent, QString title, QString text, QMessageBox::StandardButton extraButtons = QMessageBox::NoButton);
|
||||
//
|
||||
QString StringFromFile(const QString &filePath);
|
||||
QString StringFromFile(QFile *source);
|
||||
bool StringToFile(const QString *text, QFile *target);
|
||||
//
|
||||
QJsonObject JsonFromString(QString string);
|
||||
QString JsonToString(QJsonObject json, QJsonDocument::JsonFormat format = QJsonDocument::JsonFormat::Indented);
|
||||
QString JsonToString(QJsonArray array, QJsonDocument::JsonFormat format = QJsonDocument::JsonFormat::Indented);
|
||||
QString VerifyJsonString(const QString &source);
|
||||
//
|
||||
QString FormatBytes(long long bytes);
|
||||
void DeducePossibleFileName(const QString &baseDir, QString *fileName, const QString &extension);
|
||||
QStringList ConvertQStringList(const QList<string> &stdListString);
|
||||
std::list<string> ConvertStdStringList(const QStringList &qStringList);
|
||||
// This function cannot be marked as inline.
|
||||
QString RemoveInvalidFileName(const QString &fileName);
|
||||
bool IsValidFileName(const QString &fileName);
|
||||
//
|
||||
template <typename TYPE>
|
||||
QString StructToJsonString(const TYPE t)
|
||||
{
|
||||
return QString::fromStdString(x2struct::X::tojson(t, "", 4, ' '));
|
||||
}
|
||||
//
|
||||
template <typename TYPE>
|
||||
TYPE StructFromJsonString(const QString &str)
|
||||
{
|
||||
TYPE v;
|
||||
x2struct::X::loadjson(str.toStdString(), v, false);
|
||||
return v;
|
||||
}
|
||||
// Misc
|
||||
template<typename T>
|
||||
QJsonObject GetRootObject(const T &t)
|
||||
{
|
||||
auto json = StructToJsonString(t);
|
||||
return JsonFromString(json);
|
||||
}
|
||||
|
||||
inline bool IsIPv4Address(const QString &addr)
|
||||
{
|
||||
return QRegularExpression(REGEX_IPV4_ADDR "$").match(addr).hasMatch();
|
||||
}
|
||||
|
||||
inline bool IsIPv6Address(const QString &addr)
|
||||
{
|
||||
return QRegularExpression(REGEX_IPV6_ADDR "$").match(addr).hasMatch();
|
||||
}
|
||||
|
||||
inline bool IsValidIPAddress(const QString &addr)
|
||||
{
|
||||
return IsIPv4Address(addr) || IsIPv6Address(addr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Generic function to find if an element of any type exists in list
|
||||
*/
|
||||
template<typename T>
|
||||
bool contains(std::list<T> &listOfElements, const T &element)
|
||||
{
|
||||
// Find the iterator if element in list
|
||||
auto it = std::find(listOfElements.begin(), listOfElements.end(), element);
|
||||
//return if iterator points to end or not. It points to end then it means element
|
||||
// does not exists in list
|
||||
return it != listOfElements.end();
|
||||
}
|
||||
|
||||
inline QString timeToString(const time_t &t)
|
||||
{
|
||||
auto _tm = std::localtime(&t);
|
||||
char MY_TIME[128];
|
||||
// using strftime to display time
|
||||
strftime(MY_TIME, sizeof(MY_TIME), "%x - %I:%M%p", _tm);
|
||||
return QString(MY_TIME);
|
||||
}
|
||||
|
||||
template<typename myMap>
|
||||
std::vector<typename myMap::key_type> Keys(const myMap &m)
|
||||
{
|
||||
std::vector<typename myMap::key_type> r;
|
||||
r.reserve(m.size());
|
||||
|
||||
for (const auto &kvp : m) {
|
||||
r.push_back(kvp.first);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
template<typename myMap>
|
||||
std::vector<typename myMap::mapped_type> Values(const myMap &m)
|
||||
{
|
||||
std::vector<typename myMap::mapped_type> r;
|
||||
r.reserve(m.size());
|
||||
|
||||
for (const auto &kvp : m) {
|
||||
r.push_back(kvp.second);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
using namespace Qv2ray::common;
|
21
src/common/QvTranslator.hpp
Normal file
21
src/common/QvTranslator.hpp
Normal file
@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
#include <QString>
|
||||
#include <QTranslator>
|
||||
#include <memory>
|
||||
|
||||
namespace Qv2ray::common
|
||||
{
|
||||
class QvTranslator
|
||||
{
|
||||
public:
|
||||
QvTranslator(const QString &lang)
|
||||
{
|
||||
QTranslator *translator = new QTranslator();
|
||||
translator->load(lang + ".qm", ":/translations/");
|
||||
this->pTranslator.reset(translator);
|
||||
}
|
||||
|
||||
public:
|
||||
std::unique_ptr<QTranslator> pTranslator;
|
||||
};
|
||||
} // namespace Qv2ray::common
|
@ -1,3 +0,0 @@
|
||||
#include "QvComponentsHandler.hpp"
|
||||
|
||||
|
@ -1,14 +0,0 @@
|
||||
#ifndef QVCOMPONENTSHANDLER_H
|
||||
#define QVCOMPONENTSHANDLER_H
|
||||
#include "QvUtils.hpp"
|
||||
|
||||
namespace Qv2ray
|
||||
{
|
||||
namespace Components
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
using namespace Qv2ray::Components;
|
||||
|
||||
#endif // QVCOMPONENTSHANDLER_H
|
@ -1,68 +0,0 @@
|
||||
#include "QvCommandLineArgs.hpp"
|
||||
#include "Qv2rayBase.hpp"
|
||||
#include <QCommandLineParser>
|
||||
|
||||
|
||||
namespace Qv2ray
|
||||
{
|
||||
namespace CommandArgOperations
|
||||
{
|
||||
// Instantiation
|
||||
QvStartupOptions StartupOption = QvStartupOptions{};
|
||||
|
||||
QvCommandArgParser::QvCommandArgParser() : QObject(),
|
||||
noAPIOption("noAPI", QObject::tr("Disable gRPC API subsystems.")),
|
||||
runAsRootOption("I-just-wanna-run-with-root", QObject::tr("Explicitly run Qv2ray as root.")),
|
||||
debugOption("debug", QObject::tr("Enable Debug Output")),
|
||||
withToolbarOption("withToolbarPlugin", QObject::tr("Enable Qv2ray network toolbar plugin")),
|
||||
//
|
||||
helpOption("FAKE"), versionOption("FAKE")
|
||||
{
|
||||
parser.setApplicationDescription(QObject::tr("Qv2ray - A cross-platform Qt frontend for V2ray."));
|
||||
parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
|
||||
//
|
||||
parser.addOption(noAPIOption);
|
||||
parser.addOption(runAsRootOption);
|
||||
parser.addOption(debugOption);
|
||||
parser.addOption(withToolbarOption);
|
||||
helpOption = parser.addHelpOption();
|
||||
versionOption = parser.addVersionOption();
|
||||
}
|
||||
|
||||
CommandLineParseResult QvCommandArgParser::ParseCommandLine(QString *errorMessage)
|
||||
{
|
||||
if (!parser.parse(QCoreApplication::arguments())) {
|
||||
*errorMessage = parser.errorText();
|
||||
return CommandLineError;
|
||||
}
|
||||
|
||||
if (parser.isSet(versionOption))
|
||||
return CommandLineVersionRequested;
|
||||
|
||||
if (parser.isSet(helpOption))
|
||||
return CommandLineHelpRequested;
|
||||
|
||||
if (parser.isSet(noAPIOption)) {
|
||||
DEBUG(MODULE_INIT, "noAPIOption is set.")
|
||||
StartupOption.noAPI = true;
|
||||
}
|
||||
|
||||
if (parser.isSet(runAsRootOption)) {
|
||||
DEBUG(MODULE_INIT, "runAsRootOption is set.")
|
||||
StartupOption.forceRunAsRootUser = true;
|
||||
}
|
||||
|
||||
if (parser.isSet(debugOption)) {
|
||||
DEBUG(MODULE_INIT, "debugOption is set.")
|
||||
StartupOption.debugLog = true;
|
||||
}
|
||||
|
||||
if (parser.isSet(withToolbarOption)) {
|
||||
DEBUG(MODULE_INIT, "withToolbarOption is set.")
|
||||
StartupOption.enableToolbarPlguin = true;
|
||||
}
|
||||
|
||||
return CommandLineOk;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
#ifndef QVCOMMANDLINEARGS_HPP
|
||||
#define QVCOMMANDLINEARGS_HPP
|
||||
|
||||
#include "Qv2rayBase.hpp"
|
||||
|
||||
namespace Qv2ray
|
||||
{
|
||||
namespace CommandArgOperations
|
||||
{
|
||||
struct QvStartupOptions {
|
||||
/// No API subsystem
|
||||
bool noAPI;
|
||||
/// Explicitly run as root user.
|
||||
bool forceRunAsRootUser;
|
||||
/// Enable Debug Log.
|
||||
bool debugLog;
|
||||
/// Enable Network toolbar plugin.
|
||||
bool enableToolbarPlguin;
|
||||
};
|
||||
enum CommandLineParseResult {
|
||||
CommandLineOk,
|
||||
CommandLineError,
|
||||
CommandLineVersionRequested,
|
||||
CommandLineHelpRequested
|
||||
};
|
||||
//
|
||||
extern QvStartupOptions StartupOption;
|
||||
|
||||
class QvCommandArgParser : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QvCommandArgParser();
|
||||
CommandLineParseResult ParseCommandLine(QString *errorMessage);
|
||||
const QCommandLineParser *Parser()
|
||||
{
|
||||
return &parser;
|
||||
}
|
||||
|
||||
private:
|
||||
QCommandLineParser parser;
|
||||
QCommandLineOption noAPIOption;
|
||||
QCommandLineOption runAsRootOption;
|
||||
QCommandLineOption debugOption;
|
||||
QCommandLineOption withToolbarOption;
|
||||
QCommandLineOption helpOption;
|
||||
QCommandLineOption versionOption;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
using namespace Qv2ray::CommandArgOperations;
|
||||
#endif
|
@ -1,136 +0,0 @@
|
||||
/* ORIGINAL LICENSE: Do What The F*ck You Want To Public License
|
||||
* AUTHOR: LBYPatrick
|
||||
*
|
||||
* MODIFIED BY Leroy.H.Y @lhy0403 re-licenced under GPLv3
|
||||
*/
|
||||
|
||||
#include "QvUtils.hpp"
|
||||
|
||||
namespace Qv2ray
|
||||
{
|
||||
namespace Utils
|
||||
{
|
||||
// Private function
|
||||
string getRawDomain(string originLine)
|
||||
{
|
||||
size_t startPosition = 0;
|
||||
size_t endPosition = originLine.size();
|
||||
string returnBuffer;
|
||||
bool skipRule1 = originLine.find("[") != string::npos; // [Auto xxxx...
|
||||
bool skipRule2 = originLine.find("!") != string::npos; // Comments
|
||||
bool skipRule3 = originLine.find("@") != string::npos; // Non-proxy Lines
|
||||
bool skipRule4 = originLine.find("*") != string::npos;
|
||||
bool passRule1 = originLine.find("|") != string::npos; // Proxy Lines
|
||||
bool passRule2 = originLine.find(".") != string::npos; // Link-Contained Lines
|
||||
|
||||
if (originLine[endPosition] == '\n') {
|
||||
endPosition -= 1;
|
||||
}
|
||||
|
||||
if (originLine.find("http://") != string::npos) {
|
||||
startPosition += 8;
|
||||
} else if (originLine.find("https://") != string::npos) {
|
||||
startPosition += 9;
|
||||
}
|
||||
|
||||
// Skip unrelated lines
|
||||
if (skipRule1 || skipRule2 || skipRule3 || skipRule4) {
|
||||
return "";
|
||||
} else if (passRule2) {
|
||||
if (passRule1) {
|
||||
startPosition += originLine.find_last_of("|") + 1;
|
||||
}
|
||||
|
||||
if (originLine[startPosition] == '\n') startPosition += 1;
|
||||
|
||||
for (size_t i = startPosition; i < endPosition; ++i) {
|
||||
returnBuffer += originLine[i];
|
||||
}
|
||||
}
|
||||
|
||||
return returnBuffer;
|
||||
}
|
||||
|
||||
QString ConvertGFWToPAC(const QString &rawContent, const QString &customProxyString)
|
||||
{
|
||||
auto rawFileContent = Base64Decode(rawContent).toStdString();
|
||||
string readBuffer = ""; //cleanup
|
||||
string writeBuffer;
|
||||
string domainListCache = "";
|
||||
|
||||
for (size_t i = 0; i < rawFileContent.size(); ++i) {
|
||||
readBuffer += rawFileContent[i];
|
||||
|
||||
if (rawFileContent[i + 1] == '\n') {
|
||||
writeBuffer = getRawDomain(readBuffer);
|
||||
|
||||
if (writeBuffer != "") {
|
||||
domainListCache += writeBuffer + "\n";
|
||||
}
|
||||
|
||||
readBuffer = "";
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
size_t rotatorTwo = 0;
|
||||
string readDomainBuffer = "";
|
||||
bool isFirstLine = true;
|
||||
string outputContent = "";
|
||||
//Header
|
||||
outputContent += "var domains = {\n";
|
||||
|
||||
//Read and process output content line by line
|
||||
while (rotatorTwo < domainListCache.size()) {
|
||||
while (true) {
|
||||
//Get Domain
|
||||
readDomainBuffer += domainListCache[rotatorTwo];
|
||||
|
||||
if (domainListCache[rotatorTwo + 1] == '\n') {
|
||||
rotatorTwo += 2;
|
||||
break;
|
||||
}
|
||||
|
||||
rotatorTwo++;
|
||||
}
|
||||
|
||||
//Format
|
||||
if (!isFirstLine) outputContent += ",\n";
|
||||
else isFirstLine = false;
|
||||
|
||||
outputContent += "\t\"";
|
||||
outputContent += readDomainBuffer;
|
||||
outputContent += "\" : 1";
|
||||
readDomainBuffer = "";
|
||||
}
|
||||
|
||||
//End Message
|
||||
outputContent +=
|
||||
NEWLINE "};"
|
||||
NEWLINE ""
|
||||
NEWLINE " var proxy = \"" + customProxyString.toStdString() + ";\";" +
|
||||
NEWLINE " var direct = 'DIRECT;';"
|
||||
NEWLINE " function FindProxyForURL(url, host) {"
|
||||
NEWLINE " var suffix;"
|
||||
NEWLINE " var pos = host.lastIndexOf('.');"
|
||||
NEWLINE " pos = host.lastIndexOf('.', pos - 1);"
|
||||
NEWLINE " //"
|
||||
NEWLINE " while (1) {"
|
||||
NEWLINE " if (domains[host] != undefined) {"
|
||||
NEWLINE " return proxy;"
|
||||
NEWLINE " }"
|
||||
NEWLINE " else if (pos <= 0) {"
|
||||
NEWLINE " return domains['.' + host] != undefined ? proxy : direct;"
|
||||
NEWLINE " }"
|
||||
NEWLINE " suffix = host.substring(pos);"
|
||||
NEWLINE " if (domains[suffix] != undefined) {"
|
||||
NEWLINE " return proxy;"
|
||||
NEWLINE " }"
|
||||
NEWLINE " pos = host.lastIndexOf('.', pos - 1);"
|
||||
NEWLINE " }"
|
||||
NEWLINE " }";
|
||||
//
|
||||
return QString::fromStdString(outputContent);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,361 +0,0 @@
|
||||
#include <QObject>
|
||||
#include <QWidget>
|
||||
#include <QDesktopServices>
|
||||
#include "QvKernelInteractions.hpp"
|
||||
#include "QvCoreConfigOperations.hpp"
|
||||
#include "QvCore/QvCommandLineArgs.hpp"
|
||||
|
||||
#ifdef WITH_LIB_GRPCPP
|
||||
using namespace v2ray::core::app::stats::command;
|
||||
using grpc::Channel;
|
||||
using grpc::ClientContext;
|
||||
using grpc::Status;
|
||||
#else
|
||||
#include "libs/libqvb/build/libqvb.h"
|
||||
#endif
|
||||
|
||||
// Check 10 times before telling user that API has failed.
|
||||
#define QV2RAY_API_CALL_FAILEDCHECK_THRESHOLD 10
|
||||
|
||||
namespace Qv2ray
|
||||
{
|
||||
namespace QvKernelInterations
|
||||
{
|
||||
bool V2rayKernelInstance::ValidateKernel(const QString &vCorePath, const QString &vAssetsPath, QString *message)
|
||||
{
|
||||
QFile coreFile(vCorePath);
|
||||
|
||||
if (!coreFile.exists()) {
|
||||
DEBUG(MODULE_VCORE, "V2ray core file cannot be found.")
|
||||
*message = tr("V2ray core executable not found.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Use open() here to prevent `executing` a folder, which may have the same name as the V2ray core.
|
||||
if (!coreFile.open(QFile::ReadOnly)) {
|
||||
DEBUG(MODULE_VCORE, "V2ray core file cannot be opened, possibly be a folder?")
|
||||
*message = tr("V2ray core file cannot be opened, please ensure there's a file instead of a folder.");
|
||||
return false;
|
||||
}
|
||||
|
||||
coreFile.close();
|
||||
//
|
||||
// Check file existance.
|
||||
// From: https://www.v2fly.org/chapter_02/env.html#asset-location
|
||||
//
|
||||
bool hasGeoIP = FileExistsIn(QDir(vAssetsPath), "geoip.dat");
|
||||
bool hasGeoSite = FileExistsIn(QDir(vAssetsPath), "geosite.dat");
|
||||
|
||||
if (!hasGeoIP && !hasGeoSite) {
|
||||
DEBUG(MODULE_VCORE, "V2ray assets path contains none of those two files.")
|
||||
*message = tr("V2ray assets path is not valid.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!hasGeoIP) {
|
||||
DEBUG(MODULE_VCORE, "No geoip.dat in assets path, aborting.")
|
||||
*message = tr("No geoip.dat in assets path.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!hasGeoSite) {
|
||||
DEBUG(MODULE_VCORE, "No geosite.dat in assets path, aborting.")
|
||||
*message = tr("No geosite.dat in assets path.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if V2ray core returns a version number correctly.
|
||||
QProcess proc;
|
||||
#ifdef Q_OS_WIN32
|
||||
// nativeArguments are required for Windows platform, without a reason...
|
||||
proc.setProcessChannelMode(QProcess::MergedChannels);
|
||||
proc.setProgram(vCorePath);
|
||||
proc.setNativeArguments("--version");
|
||||
proc.start();
|
||||
#else
|
||||
proc.start(vCorePath + " --version");
|
||||
#endif
|
||||
proc.waitForStarted();
|
||||
proc.waitForFinished();
|
||||
auto exitCode = proc.exitCode();
|
||||
|
||||
if (exitCode != 0) {
|
||||
DEBUG(MODULE_VCORE, "VCore failed with an exit code: " + QSTRN(exitCode))
|
||||
*message = tr("V2ray core failed with an exit code: ") + QSTRN(exitCode);
|
||||
return false;
|
||||
}
|
||||
|
||||
QString output = proc.readAllStandardOutput();
|
||||
LOG(MODULE_VCORE, "V2ray output: " + SplitLines(output).join(";"))
|
||||
|
||||
if (SplitLines(output).isEmpty()) {
|
||||
*message = tr("V2ray core returns empty string.");
|
||||
return false;
|
||||
}
|
||||
|
||||
*message = SplitLines(output).first();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool V2rayKernelInstance::ValidateConfig(const QString &path)
|
||||
{
|
||||
auto conf = GetGlobalConfig();
|
||||
QString v2rayCheckResult;
|
||||
|
||||
if (ValidateKernel(conf.v2CorePath, conf.v2AssetsPath, &v2rayCheckResult)) {
|
||||
DEBUG(MODULE_VCORE, "V2ray version: " + v2rayCheckResult)
|
||||
// Append assets location env.
|
||||
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
||||
env.insert("V2RAY_LOCATION_ASSET", conf.v2AssetsPath);
|
||||
//
|
||||
QProcess process;
|
||||
process.setProcessEnvironment(env);
|
||||
DEBUG(MODULE_VCORE, "Starting V2ray core with test options")
|
||||
process.start(conf.v2CorePath, QStringList() << "-test" << "-config" << path, QIODevice::ReadWrite | QIODevice::Text);
|
||||
process.waitForFinished();
|
||||
|
||||
if (process.exitCode() != 0) {
|
||||
QString output = QString(process.readAllStandardOutput());
|
||||
QvMessageBoxWarn(nullptr, tr("Configuration Error"), output.mid(output.indexOf("anti-censorship.") + 17));
|
||||
return false;
|
||||
} else {
|
||||
DEBUG(MODULE_VCORE, "Config file check passed.")
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
QvMessageBoxWarn(nullptr, tr("Cannot start V2ray"),
|
||||
tr("V2ray core settings is incorrect.") + NEWLINE + NEWLINE +
|
||||
tr("The error is: ") + NEWLINE + v2rayCheckResult);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
V2rayKernelInstance::V2rayKernelInstance()
|
||||
{
|
||||
vProcess = new QProcess();
|
||||
connect(vProcess, &QProcess::readyReadStandardOutput, this, [this]() {
|
||||
emit onProcessOutputReadyRead(vProcess->readAllStandardOutput().trimmed());
|
||||
});
|
||||
connect(vProcess, &QProcess::stateChanged, [this](QProcess::ProcessState state) {
|
||||
DEBUG(MODULE_VCORE, "V2ray kernel process status changed: " + QVariant::fromValue(state).toString())
|
||||
|
||||
// If V2ray crashed AFTER we start it.
|
||||
if (KernelStarted && state == QProcess::NotRunning) {
|
||||
LOG(MODULE_VCORE, "V2ray kernel crashed.")
|
||||
emit onProcessErrored();
|
||||
}
|
||||
});
|
||||
KernelStarted = false;
|
||||
}
|
||||
|
||||
bool V2rayKernelInstance::StartConnection(CONFIGROOT root)
|
||||
{
|
||||
if (KernelStarted) {
|
||||
LOG(MODULE_VCORE, "Status is invalid, expect STOPPED when calling StartConnection")
|
||||
return false;
|
||||
}
|
||||
|
||||
inboundTags.clear();
|
||||
|
||||
for (auto item : root["inbounds"].toArray()) {
|
||||
auto tag = item.toObject()["tag"].toString("");
|
||||
|
||||
if (tag.isEmpty() || tag == API_TAG_INBOUND) {
|
||||
// Ignore API tag and empty tags.
|
||||
continue;
|
||||
}
|
||||
|
||||
inboundTags.append(tag);
|
||||
}
|
||||
|
||||
DEBUG(MODULE_VCORE, "Found inbound tags: " + inboundTags.join(";"))
|
||||
// Write the final configuration to the disk.
|
||||
QString json = JsonToString(root);
|
||||
StringToFile(&json, new QFile(QV2RAY_GENERATED_FILE_PATH));
|
||||
//
|
||||
auto filePath = QV2RAY_GENERATED_FILE_PATH;
|
||||
|
||||
if (ValidateConfig(filePath)) {
|
||||
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
||||
env.insert("V2RAY_LOCATION_ASSET", GetGlobalConfig().v2AssetsPath);
|
||||
vProcess->setProcessEnvironment(env);
|
||||
vProcess->start(GetGlobalConfig().v2CorePath, QStringList() << "-config" << filePath, QIODevice::ReadWrite | QIODevice::Text);
|
||||
vProcess->waitForStarted();
|
||||
DEBUG(MODULE_VCORE, "V2ray core started.")
|
||||
KernelStarted = true;
|
||||
auto conf = GetGlobalConfig();
|
||||
|
||||
if (StartupOption.noAPI) {
|
||||
LOG(MODULE_VCORE, "API has been disabled by the command line argument \"-noAPI\"")
|
||||
} else if (!conf.apiConfig.enableAPI) {
|
||||
LOG(MODULE_VCORE, "API has been disabled by the global config option")
|
||||
} else if (inboundTags.isEmpty()) {
|
||||
LOG(MODULE_VCORE, "API is disabled since no inbound tags configured. This is probably caused by a bad complex config.")
|
||||
} else {
|
||||
// Config API
|
||||
apiFailedCounter = 0;
|
||||
this->apiPort = conf.apiConfig.statsPort;
|
||||
auto channelAddress = "127.0.0.1:" + QString::number(apiPort);
|
||||
#ifdef WITH_LIB_GRPCPP
|
||||
Channel = grpc::CreateChannel(channelAddress.toStdString(), grpc::InsecureChannelCredentials());
|
||||
StatsService service;
|
||||
Stub = service.NewStub(Channel);
|
||||
#else
|
||||
auto str = Dial(const_cast<char *>(channelAddress.toStdString().c_str()), 10000);
|
||||
LOG(MODULE_VCORE, str)
|
||||
#endif
|
||||
apiTimerId = startTimer(1000);
|
||||
DEBUG(MODULE_VCORE, "API Worker started.")
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
KernelStarted = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void V2rayKernelInstance::timerEvent(QTimerEvent *event)
|
||||
{
|
||||
QObject::timerEvent(event);
|
||||
|
||||
if (event->timerId() == apiTimerId) {
|
||||
// Call API
|
||||
for (auto tag : inboundTags) {
|
||||
// Upload
|
||||
auto valup = CallStatsAPIByName("inbound>>>" + tag + ">>>traffic>>>uplink");
|
||||
auto dataup = valup - transferData[tag + "_up"];
|
||||
transferData[tag + "_up"] = valup;
|
||||
transferSpeed[tag + "_up"] = dataup;
|
||||
// Download
|
||||
auto valdown = CallStatsAPIByName("inbound>>>" + tag + ">>>traffic>>>downlink");
|
||||
auto datadown = valdown - transferData[tag + "_down"];
|
||||
transferData[tag + "_down"] = valdown;
|
||||
transferSpeed[tag + "_down"] = datadown;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void V2rayKernelInstance::StopConnection()
|
||||
{
|
||||
KernelStarted = false;
|
||||
vProcess->close();
|
||||
killTimer(apiTimerId);
|
||||
apiFailedCounter = 0;
|
||||
transferData.clear();
|
||||
transferSpeed.clear();
|
||||
}
|
||||
|
||||
V2rayKernelInstance::~V2rayKernelInstance()
|
||||
{
|
||||
if (KernelStarted) {
|
||||
StopConnection();
|
||||
}
|
||||
|
||||
delete vProcess;
|
||||
}
|
||||
|
||||
long V2rayKernelInstance::CallStatsAPIByName(QString name)
|
||||
{
|
||||
if (!KernelStarted) {
|
||||
LOG(MODULE_VCORE, "Invalid connection status when calling API")
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (apiFailedCounter == QV2RAY_API_CALL_FAILEDCHECK_THRESHOLD) {
|
||||
LOG(MODULE_VCORE, "API call failure threshold reached, cancelling further API aclls.")
|
||||
QvMessageBoxWarn(nullptr, tr("API Call Failed"), tr("Failed to get statistics data, please check if V2ray is running properly"));
|
||||
transferData.clear();
|
||||
transferSpeed.clear();
|
||||
apiFailedCounter++;
|
||||
return 0;
|
||||
} else if (apiFailedCounter > QV2RAY_API_CALL_FAILEDCHECK_THRESHOLD) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef WITH_LIB_GRPCPP
|
||||
GetStatsRequest request;
|
||||
request.set_name(name.toStdString());
|
||||
request.set_reset(false);
|
||||
GetStatsResponse response;
|
||||
ClientContext context;
|
||||
Status status = Stub->GetStats(&context, request, &response);
|
||||
|
||||
if (!status.ok()) {
|
||||
LOG(MODULE_VCORE, "API call returns: " + QSTRN(status.error_code()) + " (" + QString::fromStdString(status.error_message()) + ")")
|
||||
apiFailedCounter++;
|
||||
}
|
||||
|
||||
auto data = response.stat().value();
|
||||
#else
|
||||
auto data = GetStats(const_cast<char *>(name.toStdString().c_str()), 1000);
|
||||
#endif
|
||||
|
||||
if (data < 0) {
|
||||
LOG(MODULE_VCORE, "API call returns: " + QSTRN(data))
|
||||
apiFailedCounter++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
// ------------------------------------------------------------- API FUNCTIONS --------------------------
|
||||
long V2rayKernelInstance::getTagSpeedUp(const QString &tag)
|
||||
{
|
||||
return transferSpeed[tag + "_up"];
|
||||
}
|
||||
long V2rayKernelInstance::getTagSpeedDown(const QString &tag)
|
||||
{
|
||||
return transferSpeed[tag + "_down"];
|
||||
}
|
||||
long V2rayKernelInstance::getTagDataUp(const QString &tag)
|
||||
{
|
||||
return transferData[tag + "_up"];
|
||||
}
|
||||
long V2rayKernelInstance::getTagDataDown(const QString &tag)
|
||||
{
|
||||
return transferData[tag + "_down"];
|
||||
}
|
||||
long V2rayKernelInstance::getAllDataUp()
|
||||
{
|
||||
long val = 0;
|
||||
|
||||
for (auto tag : inboundTags) {
|
||||
val += transferData[tag + "_up"];
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
long V2rayKernelInstance::getAllDataDown()
|
||||
{
|
||||
long val = 0;
|
||||
|
||||
for (auto tag : inboundTags) {
|
||||
val += transferData[tag + "_down"];
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
long V2rayKernelInstance::getAllSpeedUp()
|
||||
{
|
||||
long val = 0;
|
||||
|
||||
for (auto tag : inboundTags) {
|
||||
val += transferSpeed[tag + "_up"];
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
long V2rayKernelInstance::getAllSpeedDown()
|
||||
{
|
||||
long val = 0;
|
||||
|
||||
for (auto tag : inboundTags) {
|
||||
val += transferSpeed[tag + "_down"];
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,66 +0,0 @@
|
||||
#ifndef VINTERACT_H
|
||||
#define VINTERACT_H
|
||||
#include <QProcess>
|
||||
#include "QvUtils.hpp"
|
||||
#ifdef WITH_LIB_GRPCPP
|
||||
#include <grpc++/grpc++.h>
|
||||
#include "v2ray_api_commands.pb.h"
|
||||
#include "v2ray_api_commands.grpc.pb.h"
|
||||
#endif
|
||||
|
||||
namespace Qv2ray
|
||||
{
|
||||
namespace QvKernelInterations
|
||||
{
|
||||
class V2rayKernelInstance : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit V2rayKernelInstance();
|
||||
~V2rayKernelInstance() override;
|
||||
//
|
||||
// Speed
|
||||
long getTagSpeedUp(const QString &tag);
|
||||
long getTagSpeedDown(const QString &tag);
|
||||
long getTagDataUp(const QString &tag);
|
||||
long getTagDataDown(const QString &tag);
|
||||
long getAllDataUp();
|
||||
long getAllDataDown();
|
||||
long getAllSpeedUp();
|
||||
long getAllSpeedDown();
|
||||
//
|
||||
bool StartConnection(CONFIGROOT root);
|
||||
void StopConnection();
|
||||
bool KernelStarted = false;
|
||||
//
|
||||
static bool ValidateConfig(const QString &path);
|
||||
static bool ValidateKernel(const QString &vCorePath, const QString &vAssetsPath, QString *message);
|
||||
|
||||
signals:
|
||||
void onProcessErrored();
|
||||
void onProcessOutputReadyRead(QString);
|
||||
|
||||
private:
|
||||
void timerEvent(QTimerEvent *event) override;
|
||||
QStringList inboundTags;
|
||||
int apiTimerId = -1;
|
||||
int apiPort;
|
||||
//
|
||||
int apiFailedCounter;
|
||||
long CallStatsAPIByName(QString name);
|
||||
QProcess *vProcess;
|
||||
//
|
||||
QMap<QString, long> transferData;
|
||||
QMap<QString, long> transferSpeed;
|
||||
//
|
||||
#ifdef WITH_LIB_GRPCPP
|
||||
std::shared_ptr<::grpc::Channel> Channel;
|
||||
std::unique_ptr<::v2ray::core::app::stats::command::StatsService::Stub> Stub;
|
||||
#endif
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
using namespace Qv2ray::QvKernelInterations;
|
||||
|
||||
#endif // VINTERACT_H
|
@ -1,189 +0,0 @@
|
||||
#include "QvLaunchAtLoginConfigurator.hpp"
|
||||
#include <QSettings>
|
||||
#include <QApplication>
|
||||
#include <QDir>
|
||||
#include <QStandardPaths>
|
||||
#include <QTextStream>
|
||||
// macOS headers (possibly OBJ-c)
|
||||
#if defined(Q_OS_MAC)
|
||||
#include <CoreServices/CoreServices.h>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#endif
|
||||
namespace Qv2ray
|
||||
{
|
||||
namespace Components
|
||||
{
|
||||
//
|
||||
// launchatlogin.cpp
|
||||
// ShadowClash
|
||||
//
|
||||
// Created by TheWanderingCoel on 2018/6/12.
|
||||
// Copyright © 2019 Coel Wu. All rights reserved.
|
||||
//
|
||||
QString getUserAutostartDir_private()
|
||||
{
|
||||
QString config = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation);
|
||||
config += QLatin1String("/autostart/");
|
||||
return config;
|
||||
}
|
||||
|
||||
bool GetLaunchAtLoginStatus()
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
QString appName = QApplication::applicationName();
|
||||
QSettings reg("HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", QSettings::NativeFormat);
|
||||
return reg.contains(appName);
|
||||
}
|
||||
|
||||
#elif defined Q_OS_MAC
|
||||
// From https://github.com/nextcloud/desktop/blob/master/src/common/utility_mac.cpp
|
||||
// this is quite some duplicate code with setLaunchOnStartup, at some point we should fix this FIXME.
|
||||
bool returnValue = false;
|
||||
QString filePath = QDir(QCoreApplication::applicationDirPath() + QLatin1String("/../..")).absolutePath();
|
||||
CFStringRef folderCFStr = CFStringCreateWithCString(0, filePath.toUtf8().data(), kCFStringEncodingUTF8);
|
||||
CFURLRef urlRef = CFURLCreateWithFileSystemPath(0, folderCFStr, kCFURLPOSIXPathStyle, true);
|
||||
LSSharedFileListRef loginItems = LSSharedFileListCreate(0, kLSSharedFileListSessionLoginItems, 0);
|
||||
|
||||
if (loginItems)
|
||||
{
|
||||
// We need to iterate over the items and check which one is "ours".
|
||||
UInt32 seedValue;
|
||||
CFArrayRef itemsArray = LSSharedFileListCopySnapshot(loginItems, &seedValue);
|
||||
CFStringRef appUrlRefString = CFURLGetString(urlRef); // no need for release
|
||||
|
||||
for (int i = 0; i < CFArrayGetCount(itemsArray); i++) {
|
||||
LSSharedFileListItemRef item = (LSSharedFileListItemRef)CFArrayGetValueAtIndex(itemsArray, i);
|
||||
CFURLRef itemUrlRef = NULL;
|
||||
|
||||
if (LSSharedFileListItemResolve(item, 0, &itemUrlRef, NULL) == noErr && itemUrlRef) {
|
||||
CFStringRef itemUrlString = CFURLGetString(itemUrlRef);
|
||||
|
||||
if (CFStringCompare(itemUrlString, appUrlRefString, 0) == kCFCompareEqualTo) {
|
||||
returnValue = true;
|
||||
}
|
||||
|
||||
CFRelease(itemUrlRef);
|
||||
}
|
||||
}
|
||||
|
||||
CFRelease(itemsArray);
|
||||
}
|
||||
|
||||
CFRelease(loginItems);
|
||||
CFRelease(folderCFStr);
|
||||
CFRelease(urlRef);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
#elif defined Q_OS_LINUX
|
||||
QString appName = QApplication::applicationName();
|
||||
QString desktopFileLocation = getUserAutostartDir_private() + appName + QLatin1String(".desktop");
|
||||
return QFile::exists(desktopFileLocation);
|
||||
}
|
||||
#endif
|
||||
|
||||
void SetLaunchAtLoginStatus(bool enable)
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
QString appName = QApplication::applicationName();
|
||||
QSettings reg("HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", QSettings::NativeFormat);
|
||||
|
||||
if (enable) {
|
||||
QString strAppPath = QDir::toNativeSeparators(QCoreApplication::applicationFilePath());
|
||||
reg.setValue(appName, strAppPath);
|
||||
} else {
|
||||
reg.remove(appName);
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined Q_OS_MAC
|
||||
// From https://github.com/nextcloud/desktop/blob/master/src/common/utility_mac.cpp
|
||||
QString filePath = QDir(QCoreApplication::applicationDirPath() + QLatin1String("/../..")).absolutePath();
|
||||
CFStringRef folderCFStr = CFStringCreateWithCString(0, filePath.toUtf8().data(), kCFStringEncodingUTF8);
|
||||
CFURLRef urlRef = CFURLCreateWithFileSystemPath(0, folderCFStr, kCFURLPOSIXPathStyle, true);
|
||||
LSSharedFileListRef loginItems = LSSharedFileListCreate(0, kLSSharedFileListSessionLoginItems, 0);
|
||||
|
||||
if (loginItems && enable)
|
||||
{
|
||||
//Insert an item to the list.
|
||||
LSSharedFileListItemRef item = LSSharedFileListInsertItemURL(loginItems,
|
||||
kLSSharedFileListItemLast, 0, 0,
|
||||
urlRef, 0, 0);
|
||||
|
||||
if (item)
|
||||
CFRelease(item);
|
||||
|
||||
CFRelease(loginItems);
|
||||
} else if (loginItems && !enable)
|
||||
{
|
||||
// We need to iterate over the items and check which one is "ours".
|
||||
UInt32 seedValue;
|
||||
CFArrayRef itemsArray = LSSharedFileListCopySnapshot(loginItems, &seedValue);
|
||||
CFStringRef appUrlRefString = CFURLGetString(urlRef);
|
||||
|
||||
for (int i = 0; i < CFArrayGetCount(itemsArray); i++) {
|
||||
LSSharedFileListItemRef item = (LSSharedFileListItemRef)CFArrayGetValueAtIndex(itemsArray, i);
|
||||
CFURLRef itemUrlRef = NULL;
|
||||
|
||||
if (LSSharedFileListItemResolve(item, 0, &itemUrlRef, NULL) == noErr && itemUrlRef) {
|
||||
CFStringRef itemUrlString = CFURLGetString(itemUrlRef);
|
||||
|
||||
if (CFStringCompare(itemUrlString, appUrlRefString, 0) == kCFCompareEqualTo) {
|
||||
LSSharedFileListItemRemove(loginItems, item); // remove it!
|
||||
}
|
||||
|
||||
CFRelease(itemUrlRef);
|
||||
}
|
||||
}
|
||||
|
||||
CFRelease(itemsArray);
|
||||
CFRelease(loginItems);
|
||||
}
|
||||
|
||||
CFRelease(folderCFStr);
|
||||
CFRelease(urlRef);
|
||||
}
|
||||
|
||||
#elif defined Q_OS_LINUX
|
||||
// From https://github.com/nextcloud/desktop/blob/master/src/common/utility_unix.cpp
|
||||
QString appName = QApplication::applicationName();
|
||||
QString userAutoStartPath = getUserAutostartDir_private();
|
||||
QString desktopFileLocation = userAutoStartPath + appName + QLatin1String(".desktop");
|
||||
|
||||
if (enable)
|
||||
{
|
||||
if (!QDir().exists(userAutoStartPath) && !QDir().mkpath(userAutoStartPath)) {
|
||||
// qCWarning(lcUtility) << "Could not create autostart folder" << userAutoStartPath;
|
||||
return;
|
||||
}
|
||||
|
||||
QFile iniFile(desktopFileLocation);
|
||||
|
||||
if (!iniFile.open(QIODevice::WriteOnly)) {
|
||||
// qCWarning(lcUtility) << "Could not write auto start entry" << desktopFileLocation;
|
||||
return;
|
||||
}
|
||||
|
||||
QTextStream ts(&iniFile);
|
||||
ts.setCodec("UTF-8");
|
||||
ts << QLatin1String("[Desktop Entry]") << endl
|
||||
<< QLatin1String("Name=") << QApplication::applicationName() << endl
|
||||
<< QLatin1String("GenericName=") << QLatin1String("File Synchronizer") << endl
|
||||
<< QLatin1String("Exec=") << QCoreApplication::applicationFilePath() << endl
|
||||
<< QLatin1String("Terminal=") << "false" << endl
|
||||
<< QLatin1String("Icon=") << "qv2ray" << endl // always use lowercase for icons
|
||||
<< QLatin1String("Categories=") << "Network" << endl
|
||||
<< QLatin1String("Type=") << "Application" << endl
|
||||
<< QLatin1String("StartupNotify=") << "false" << endl
|
||||
<< QLatin1String("X-GNOME-Autostart-enabled=") << "true" << endl;
|
||||
} else
|
||||
{
|
||||
if (!QFile::remove(desktopFileLocation)) {
|
||||
// qCWarning(lcUtility) << "Could not remove autostart desktop file";
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +0,0 @@
|
||||
#ifndef QVLAUNCH_AT_LOGINCONFIGURATOR_H
|
||||
#define QVLAUNCH_AT_LOGINCONFIGURATOR_H
|
||||
|
||||
#include "QvUtils.hpp"
|
||||
|
||||
namespace Qv2ray
|
||||
{
|
||||
namespace Components
|
||||
{
|
||||
bool GetLaunchAtLoginStatus();
|
||||
void SetLaunchAtLoginStatus(bool enable);
|
||||
}
|
||||
}
|
||||
|
||||
using namespace Qv2ray::Components;
|
||||
|
||||
#endif // QVLAUNCH_AT_LOGINCONFIGURATOR_H
|
@ -1,136 +0,0 @@
|
||||
#include "QvLogHighlighter.hpp"
|
||||
|
||||
#define REGEX_PORT_NUMBER "([0-9]|[1-9]\\d{1,3}|[1-5]\\d{4}|6[0-5]{2}[0-3][0-5])*"
|
||||
#define TO_EOL "(([\\s\\S]*)|([\\d\\D]*)|([\\w\\W]*))$"
|
||||
|
||||
namespace Qv2ray
|
||||
{
|
||||
namespace Components
|
||||
{
|
||||
SyntaxHighlighter::SyntaxHighlighter(bool darkMode, QTextDocument *parent)
|
||||
: QSyntaxHighlighter(parent)
|
||||
{
|
||||
HighlightingRule rule;
|
||||
keywordFormat.setForeground(darkMode ? Qt::darkMagenta : Qt::magenta);
|
||||
keywordFormat.setFontWeight(QFont::Bold);
|
||||
const QString keywordPatterns[] = {
|
||||
"tcp", "udp"
|
||||
};
|
||||
|
||||
for (const QString &pattern : keywordPatterns) {
|
||||
rule.pattern = QRegularExpression(pattern);
|
||||
rule.format = keywordFormat;
|
||||
highlightingRules.append(rule);
|
||||
}
|
||||
|
||||
if (darkMode) {
|
||||
ipHostFormat.setForeground(Qt::yellow);
|
||||
warningFormat.setForeground(QColor(230, 180, 0));
|
||||
} else {
|
||||
ipHostFormat.setForeground(Qt::black);
|
||||
ipHostFormat.setFontWeight(QFont::Bold);
|
||||
warningFormat.setForeground(Qt::white);
|
||||
warningFormat.setBackground(QColor(255, 128, 30));
|
||||
}
|
||||
|
||||
//
|
||||
dateFormat.setFontWeight(QFont::Bold);
|
||||
dateFormat.setForeground(darkMode ? Qt::cyan : Qt::darkCyan);
|
||||
rule.pattern = QRegularExpression("\\d\\d\\d\\d/\\d\\d/\\d\\d");
|
||||
rule.format = dateFormat;
|
||||
highlightingRules.append(rule);
|
||||
//
|
||||
timeFormat.setFontWeight(QFont::Bold);
|
||||
timeFormat.setForeground(darkMode ? Qt::cyan : Qt::darkCyan);
|
||||
rule.pattern = QRegularExpression("\\d\\d:\\d\\d:\\d\\d");
|
||||
rule.format = timeFormat;
|
||||
highlightingRules.append(rule);
|
||||
//
|
||||
debugFormat.setForeground(Qt::darkGray);
|
||||
rule.pattern = QRegularExpression("\\[[Dd]ebug\\]" TO_EOL);
|
||||
rule.format = debugFormat;
|
||||
highlightingRules.append(rule);
|
||||
//
|
||||
infoFormat.setForeground(darkMode ? Qt::lightGray : Qt::darkCyan);
|
||||
rule.pattern = QRegularExpression("\\[[Ii]nfo\\]" TO_EOL);
|
||||
rule.format = infoFormat;
|
||||
highlightingRules.append(rule);
|
||||
//
|
||||
//
|
||||
{
|
||||
// IP IPv6 Host;
|
||||
rule.pattern = QRegularExpression("(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])\\:" REGEX_PORT_NUMBER);
|
||||
rule.pattern.setPatternOptions(QRegularExpression::PatternOption::ExtendedPatternSyntaxOption);
|
||||
rule.format = ipHostFormat;
|
||||
highlightingRules.append(rule);
|
||||
//
|
||||
rule.pattern = QRegularExpression(REGEX_IPV6_ADDR ":" REGEX_PORT_NUMBER);
|
||||
rule.pattern.setPatternOptions(QRegularExpression::PatternOption::ExtendedPatternSyntaxOption);
|
||||
rule.format = ipHostFormat;
|
||||
highlightingRules.append(rule);
|
||||
//
|
||||
rule.pattern = QRegularExpression("([a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,6}(/|):" REGEX_PORT_NUMBER);
|
||||
rule.pattern.setPatternOptions(QRegularExpression::PatternOption::ExtendedPatternSyntaxOption);
|
||||
rule.format = ipHostFormat;
|
||||
highlightingRules.append(rule);
|
||||
}
|
||||
//
|
||||
//
|
||||
acceptedFormat.setForeground(Qt::darkGreen);
|
||||
rule.pattern = QRegularExpression("\\saccepted\\s");
|
||||
rule.format = acceptedFormat;
|
||||
highlightingRules.append(rule);
|
||||
//
|
||||
rejectedFormat.setFontWeight(QFont::Bold);
|
||||
rejectedFormat.setBackground(Qt::red);
|
||||
rejectedFormat.setForeground(Qt::white);
|
||||
rule.pattern = QRegularExpression("\\srejected\\s" TO_EOL);
|
||||
rule.format = rejectedFormat;
|
||||
highlightingRules.append(rule);
|
||||
//
|
||||
v2rayComponentFormat.setFontWeight(QFont::Bold);
|
||||
v2rayComponentFormat.setForeground(darkMode ? Qt::darkGreen : Qt::darkYellow);
|
||||
rule.pattern = QRegularExpression(" v2ray.com(/(\\w*))*: ");
|
||||
rule.format = v2rayComponentFormat;
|
||||
highlightingRules.append(rule);
|
||||
//
|
||||
warningFormat.setFontWeight(QFont::Bold);
|
||||
rule.pattern = QRegularExpression("\\[[Ww]arning\\]" TO_EOL);
|
||||
rule.format = warningFormat;
|
||||
highlightingRules.append(rule);
|
||||
//
|
||||
failedFormat.setFontWeight(QFont::Bold);
|
||||
failedFormat.setBackground(Qt::red);
|
||||
failedFormat.setForeground(Qt::white);
|
||||
rule.pattern = QRegularExpression("failed");
|
||||
rule.format = failedFormat;
|
||||
highlightingRules.append(rule);
|
||||
//
|
||||
qvAppLogFormat.setFontWeight(QFont::Bold);
|
||||
qvAppLogFormat.setForeground(darkMode ? Qt::cyan : Qt::darkCyan);
|
||||
rule.pattern = QRegularExpression("\\[[A-Z]*\\]:");
|
||||
rule.format = qvAppLogFormat;
|
||||
highlightingRules.append(rule);
|
||||
//
|
||||
qvAppDebugLogFormat.setFontWeight(QFont::Bold);
|
||||
qvAppDebugLogFormat.setForeground(darkMode ? Qt::yellow : Qt::darkYellow);
|
||||
rule.pattern = QRegularExpression("\\[\\[DEBUG\\] - [A-Z]*\\]:");
|
||||
rule.format = qvAppDebugLogFormat;
|
||||
highlightingRules.append(rule);
|
||||
}
|
||||
|
||||
void SyntaxHighlighter::highlightBlock(const QString &text)
|
||||
{
|
||||
for (const HighlightingRule &rule : qAsConst(highlightingRules)) {
|
||||
QRegularExpressionMatchIterator matchIterator = rule.pattern.globalMatch(text);
|
||||
|
||||
while (matchIterator.hasNext()) {
|
||||
QRegularExpressionMatch match = matchIterator.next();
|
||||
setFormat(match.capturedStart(), match.capturedLength(), rule.format);
|
||||
}
|
||||
}
|
||||
|
||||
setCurrentBlockState(0);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,66 +0,0 @@
|
||||
#ifndef QVNETSPEEDBARJSON_H
|
||||
#define QVNETSPEEDBARJSON_H
|
||||
|
||||
#include "Qv2rayBase.hpp"
|
||||
//
|
||||
#define QV2RAY_NETSPEED_PLUGIN_PIPE_NAME_LINUX "Qv2ray_NetSpeed_Widget_LocalSocket"
|
||||
#define QV2RAY_NETSPEED_PLUGIN_PIPE_NAME_WIN "\\\\.\\pipe\\qv2ray_desktop_netspeed_toolbar_pipe"
|
||||
//
|
||||
namespace Qv2ray
|
||||
{
|
||||
namespace Components
|
||||
{
|
||||
namespace NetSpeedPlugin
|
||||
{
|
||||
/// NO NOT CHANGE THE ORDER
|
||||
static const QMap<int, QString> NetSpeedPluginMessages {
|
||||
{ 0, QObject::tr("Custom Text")},
|
||||
// Current Status
|
||||
{ 101, QObject::tr("Current Time") },
|
||||
{ 102, QObject::tr("Current Date") },
|
||||
{ 103, QObject::tr("Current Qv2ray Version") },
|
||||
{ 104, QObject::tr("Current Connection Name") },
|
||||
{ 105, QObject::tr("Current Connection Status") },
|
||||
// Speeds
|
||||
{ 201, QObject::tr("Total Upload Speed") },
|
||||
{ 202, QObject::tr("Total Download Speed") },
|
||||
{ 203, QObject::tr("Upload Speed for Specific Tag") },
|
||||
{ 204, QObject::tr("Download Speed for Specific Tag") },
|
||||
// Datas
|
||||
{ 301, QObject::tr("Total Uploaded Data") },
|
||||
{ 302, QObject::tr("Total Downloaded Data") },
|
||||
{ 303, QObject::tr("Uploaded Data for Specific Tag") },
|
||||
{ 304, QObject::tr("Downloaded Data for Specific Tag") }
|
||||
};
|
||||
void StartProcessingPlugins();
|
||||
void StopProcessingPlugins();
|
||||
#ifdef Q_OS_WIN
|
||||
namespace _win
|
||||
{
|
||||
void StartNamedPipeThread();
|
||||
void KillNamedPipeThread();
|
||||
}
|
||||
#endif
|
||||
#ifdef Q_OS_LINUX
|
||||
namespace _linux
|
||||
{
|
||||
// This function is called within a QThread
|
||||
// Actually it should the entrypoint of a thread.
|
||||
void StartMessageQThread();
|
||||
void StopMessageQThread();
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
QString GetAnswerToRequest(const QString &pchRequest);
|
||||
|
||||
}
|
||||
} // namespace Utils
|
||||
} // namespace Qv2ray
|
||||
//
|
||||
//
|
||||
|
||||
//
|
||||
//
|
||||
using namespace Qv2ray::Components::NetSpeedPlugin;
|
||||
#endif // QVNETSPEEDBARJSON_H
|
@ -1,83 +0,0 @@
|
||||
#include "QvPACHandler.hpp"
|
||||
#include "qhttprequest.h"
|
||||
#include "qhttpresponse.h"
|
||||
|
||||
namespace Qv2ray
|
||||
{
|
||||
namespace Components
|
||||
{
|
||||
PACServer::PACServer() : QObject()
|
||||
{
|
||||
pacServer = new QHttpServer();
|
||||
connect(pacServer, &QHttpServer::newRequest, this, &PACServer::onNewRequest);
|
||||
}
|
||||
PACServer::~PACServer()
|
||||
{
|
||||
pacServer->close();
|
||||
delete pacServer;
|
||||
}
|
||||
void PACServer::SetProxyString(const QString &proxyString)
|
||||
{
|
||||
DEBUG(MODULE_PROXY, "Setting new PAC proxy string: " + proxyString)
|
||||
this->proxyString = proxyString;
|
||||
}
|
||||
void PACServer::StartListen()
|
||||
{
|
||||
LOG(MODULE_PROXY, "Starting PAC listener")
|
||||
pacServer = new QHttpServer();
|
||||
connect(pacServer, &QHttpServer::newRequest, this, &PACServer::onNewRequest);
|
||||
//
|
||||
auto conf = GetGlobalConfig();
|
||||
auto address = conf.inboundConfig.listenip;
|
||||
auto port = conf.inboundConfig.pacConfig.port;
|
||||
//
|
||||
DEBUG(MODULE_PROXY, "PAC Listening local endpoint: " + address + ":" + QSTRN(port))
|
||||
//
|
||||
QString gfwContent = StringFromFile(new QFile(QV2RAY_RULES_GFWLIST_PATH));
|
||||
pacContent = ConvertGFWToPAC(gfwContent, proxyString);
|
||||
//
|
||||
auto result = pacServer->listen(QHostAddress(address), static_cast<ushort>(port));
|
||||
|
||||
if (result) {
|
||||
isStarted = true;
|
||||
DEBUG(MODULE_PROXY, "Started PAC handler")
|
||||
} else {
|
||||
LOG(MODULE_PROXY, "Failed to listen on port " + QSTRN(port) + ", possible permission denied.")
|
||||
QvMessageBoxWarn(nullptr, tr("PAC Handler"), tr("Failed to listen PAC request on this port, please verify the permissions"));
|
||||
}
|
||||
}
|
||||
|
||||
void PACServer::StopServer()
|
||||
{
|
||||
if (isStarted) {
|
||||
pacServer->close();
|
||||
DEBUG(MODULE_PROXY, "PAC Handler stopped.")
|
||||
isStarted = false;
|
||||
delete pacServer;
|
||||
}
|
||||
}
|
||||
|
||||
void PACServer::onNewRequest(QHttpRequest *req, QHttpResponse *rsp)
|
||||
{
|
||||
rsp->setHeader("Server", "Qv2ray/" QV2RAY_VERSION_STRING " PAC_Handler");
|
||||
|
||||
if (req->method() == QHttpRequest::HTTP_GET) {
|
||||
//
|
||||
if (req->path() == "/pac") {
|
||||
DEBUG(MODULE_PROXY, "Serving PAC file request.")
|
||||
//
|
||||
rsp->setHeader("Content-Type", "application/javascript; charset=utf-8");
|
||||
rsp->writeHead(QHttpResponse::StatusCode::STATUS_OK);
|
||||
rsp->end(pacContent.toUtf8());
|
||||
DEBUG(MODULE_PROXY, "Serving a pac file...")
|
||||
} else {
|
||||
rsp->writeHead(QHttpResponse::StatusCode::STATUS_NOT_FOUND);
|
||||
rsp->end("NOT FOUND");
|
||||
}
|
||||
} else {
|
||||
rsp->writeHead(QHttpResponse::StatusCode::STATUS_METHOD_NOT_ALLOWED);
|
||||
rsp->end("PAC ONLY SUPPORT GET");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
#ifndef QVPACHANDLER_H
|
||||
#define QVPACHANDLER_H
|
||||
#include "QvUtils.hpp"
|
||||
#include "qhttpserver.h"
|
||||
#include <QObject>
|
||||
namespace Qv2ray
|
||||
{
|
||||
namespace Components
|
||||
{
|
||||
class PACServer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit PACServer();
|
||||
~PACServer();
|
||||
void SetProxyString(const QString &proxyString);
|
||||
void StartListen();
|
||||
void StopServer();
|
||||
|
||||
QString gfwFilePath;
|
||||
|
||||
public slots:
|
||||
void onNewRequest(QHttpRequest *request, QHttpResponse *response);
|
||||
|
||||
private:
|
||||
bool isStarted;
|
||||
QHttpServer *pacServer;
|
||||
QString pacContent;
|
||||
QString proxyString;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
using namespace Qv2ray::Components;
|
||||
#endif // QVPACHANDLER_H
|
@ -1,280 +0,0 @@
|
||||
#include "QvSystemProxyConfigurator.hpp"
|
||||
#ifdef Q_OS_WIN
|
||||
#include "wininet.h"
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
namespace Qv2ray
|
||||
{
|
||||
namespace Components
|
||||
{
|
||||
|
||||
#ifdef Q_OS_MACOS
|
||||
QStringList macOSgetNetworkServices()
|
||||
{
|
||||
QProcess p;
|
||||
p.start("/usr/sbin/networksetup -listallnetworkservices");
|
||||
LOG(MODULE_PROXY, p.errorString())
|
||||
auto str = p.readAllStandardOutput();
|
||||
auto lines = SplitLines(str);
|
||||
QStringList result;
|
||||
|
||||
// Start from 1 since first line is unneeded.
|
||||
for (auto i = 1; i < lines.count(); i++) {
|
||||
// * means disabled.
|
||||
if (!lines[i].contains("*")) {
|
||||
result << (lines[i].contains(" ") ? "\"" + lines[i] + "\"" : lines[i]);
|
||||
}
|
||||
}
|
||||
|
||||
LOG(MODULE_PROXY, "Found " + QSTRN(result.size()) + " network services: " + result.join(";"))
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
#ifdef Q_OS_WIN
|
||||
#define NO_CONST(expr) const_cast<wchar_t *>(expr)
|
||||
//static auto DEFAULT_CONNECTION_NAME = NO_CONST(L"DefaultConnectionSettings");
|
||||
///
|
||||
/// INTERNAL FUNCTION
|
||||
bool __QueryProxyOptions()
|
||||
{
|
||||
INTERNET_PER_CONN_OPTION_LIST List;
|
||||
INTERNET_PER_CONN_OPTION Option[5];
|
||||
//
|
||||
unsigned long nSize = sizeof(INTERNET_PER_CONN_OPTION_LIST);
|
||||
Option[0].dwOption = INTERNET_PER_CONN_AUTOCONFIG_URL;
|
||||
Option[1].dwOption = INTERNET_PER_CONN_AUTODISCOVERY_FLAGS;
|
||||
Option[2].dwOption = INTERNET_PER_CONN_FLAGS;
|
||||
Option[3].dwOption = INTERNET_PER_CONN_PROXY_BYPASS;
|
||||
Option[4].dwOption = INTERNET_PER_CONN_PROXY_SERVER;
|
||||
//
|
||||
List.dwSize = sizeof(INTERNET_PER_CONN_OPTION_LIST);
|
||||
List.pszConnection = nullptr;// NO_CONST(DEFAULT_CONNECTION_NAME);
|
||||
List.dwOptionCount = 5;
|
||||
List.dwOptionError = 0;
|
||||
List.pOptions = Option;
|
||||
|
||||
if (!InternetQueryOption(nullptr, INTERNET_OPTION_PER_CONNECTION_OPTION, &List, &nSize)) {
|
||||
LOG(MODULE_PROXY, "InternetQueryOption failed, GLE=" + QSTRN(GetLastError()))
|
||||
}
|
||||
|
||||
LOG(MODULE_PROXY, "System default proxy info:")
|
||||
|
||||
if (Option[0].Value.pszValue != nullptr) {
|
||||
LOG(MODULE_PROXY, QString::fromWCharArray(Option[0].Value.pszValue))
|
||||
}
|
||||
|
||||
if ((Option[2].Value.dwValue & PROXY_TYPE_AUTO_PROXY_URL) == PROXY_TYPE_AUTO_PROXY_URL) {
|
||||
LOG(MODULE_PROXY, "PROXY_TYPE_AUTO_PROXY_URL")
|
||||
}
|
||||
|
||||
if ((Option[2].Value.dwValue & PROXY_TYPE_AUTO_DETECT) == PROXY_TYPE_AUTO_DETECT) {
|
||||
LOG(MODULE_PROXY, "PROXY_TYPE_AUTO_DETECT")
|
||||
}
|
||||
|
||||
if ((Option[2].Value.dwValue & PROXY_TYPE_DIRECT) == PROXY_TYPE_DIRECT) {
|
||||
LOG(MODULE_PROXY, "PROXY_TYPE_DIRECT")
|
||||
}
|
||||
|
||||
if ((Option[2].Value.dwValue & PROXY_TYPE_PROXY) == PROXY_TYPE_PROXY) {
|
||||
LOG(MODULE_PROXY, "PROXY_TYPE_PROXY")
|
||||
}
|
||||
|
||||
if (!InternetQueryOption(nullptr, INTERNET_OPTION_PER_CONNECTION_OPTION, &List, &nSize)) {
|
||||
LOG(MODULE_PROXY, "InternetQueryOption failed,GLE=" + QSTRN(GetLastError()))
|
||||
}
|
||||
|
||||
if (Option[4].Value.pszValue != nullptr) {
|
||||
LOG(MODULE_PROXY, QString::fromStdWString(Option[4].Value.pszValue))
|
||||
}
|
||||
|
||||
INTERNET_VERSION_INFO Version;
|
||||
nSize = sizeof(INTERNET_VERSION_INFO);
|
||||
InternetQueryOption(nullptr, INTERNET_OPTION_VERSION, &Version, &nSize);
|
||||
|
||||
if (Option[0].Value.pszValue != nullptr) {
|
||||
GlobalFree(Option[0].Value.pszValue);
|
||||
}
|
||||
|
||||
if (Option[3].Value.pszValue != nullptr) {
|
||||
GlobalFree(Option[3].Value.pszValue);
|
||||
}
|
||||
|
||||
if (Option[4].Value.pszValue != nullptr) {
|
||||
GlobalFree(Option[4].Value.pszValue);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
bool __SetProxyOptions(LPWSTR proxy_full_addr, bool isPAC)
|
||||
{
|
||||
INTERNET_PER_CONN_OPTION_LIST list;
|
||||
BOOL bReturn;
|
||||
DWORD dwBufSize = sizeof(list);
|
||||
// Fill the list structure.
|
||||
list.dwSize = sizeof(list);
|
||||
// NULL == LAN, otherwise connectoid name.
|
||||
list.pszConnection = nullptr;
|
||||
|
||||
if (isPAC) {
|
||||
LOG(MODULE_PROXY, "Setting system proxy for PAC")
|
||||
//
|
||||
list.dwOptionCount = 2;
|
||||
list.pOptions = new INTERNET_PER_CONN_OPTION[2];
|
||||
|
||||
// Ensure that the memory was allocated.
|
||||
if (nullptr == list.pOptions) {
|
||||
// Return FALSE if the memory wasn't allocated.
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Set flags.
|
||||
list.pOptions[0].dwOption = INTERNET_PER_CONN_FLAGS;
|
||||
list.pOptions[0].Value.dwValue = PROXY_TYPE_DIRECT | PROXY_TYPE_AUTO_PROXY_URL;
|
||||
// Set proxy name.
|
||||
list.pOptions[1].dwOption = INTERNET_PER_CONN_AUTOCONFIG_URL;
|
||||
list.pOptions[1].Value.pszValue = proxy_full_addr;
|
||||
} else {
|
||||
LOG(MODULE_PROXY, "Setting system proxy for Global Proxy")
|
||||
//
|
||||
list.dwOptionCount = 3;
|
||||
list.pOptions = new INTERNET_PER_CONN_OPTION[3];
|
||||
|
||||
if (nullptr == list.pOptions) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set flags.
|
||||
list.pOptions[0].dwOption = INTERNET_PER_CONN_FLAGS;
|
||||
list.pOptions[0].Value.dwValue = PROXY_TYPE_DIRECT | PROXY_TYPE_PROXY;
|
||||
// Set proxy name.
|
||||
list.pOptions[1].dwOption = INTERNET_PER_CONN_PROXY_SERVER;
|
||||
list.pOptions[1].Value.pszValue = proxy_full_addr;
|
||||
// Set proxy override.
|
||||
list.pOptions[2].dwOption = INTERNET_PER_CONN_PROXY_BYPASS;
|
||||
auto localhost = L"localhost";
|
||||
list.pOptions[2].Value.pszValue = NO_CONST(localhost);
|
||||
}
|
||||
|
||||
// Set the options on the connection.
|
||||
bReturn = InternetSetOption(nullptr, INTERNET_OPTION_PER_CONNECTION_OPTION, &list, dwBufSize);
|
||||
delete [] list.pOptions;
|
||||
InternetSetOption(nullptr, INTERNET_OPTION_SETTINGS_CHANGED, nullptr, 0);
|
||||
InternetSetOption(nullptr, INTERNET_OPTION_REFRESH, nullptr, 0);
|
||||
return bReturn;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool SetSystemProxy(const QString &address, int port, bool usePAC)
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
QString __a;
|
||||
|
||||
if (usePAC) {
|
||||
__a = address;
|
||||
} else {
|
||||
__a = "http://" + address + ":" + QSTRN(port);
|
||||
}
|
||||
|
||||
auto proxyStrW = new WCHAR[__a.length() + 1];
|
||||
wcscpy(proxyStrW, __a.toStdWString().c_str());
|
||||
//
|
||||
__QueryProxyOptions();
|
||||
|
||||
if (!__SetProxyOptions(proxyStrW, usePAC)) {
|
||||
LOG(MODULE_PROXY, "Failed to set proxy.")
|
||||
return false;
|
||||
}
|
||||
|
||||
__QueryProxyOptions();
|
||||
return true;
|
||||
#elif defined(Q_OS_LINUX)
|
||||
bool result = true;
|
||||
|
||||
if (usePAC) {
|
||||
result = result && QProcess::execute("gsettings set org.gnome.system.proxy mode 'auto'") == QProcess::NormalExit;
|
||||
result = result && QProcess::execute("gsettings set org.gnome.system.proxy autoconfig-url '" + address + "'") == QProcess::NormalExit;
|
||||
} else {
|
||||
result = result && QProcess::execute("gsettings set org.gnome.system.proxy mode 'manual'") == QProcess::NormalExit;
|
||||
result = result && QProcess::execute("gsettings set org.gnome.system.proxy.http host '" + address + "'") == QProcess::NormalExit;
|
||||
result = result && QProcess::execute("gsettings set org.gnome.system.proxy.http port " + QSTRN(port)) == QProcess::NormalExit;
|
||||
result = result && QProcess::execute("gsettings set org.gnome.system.proxy.https host '" + address + "'") == QProcess::NormalExit;
|
||||
result = result && QProcess::execute("gsettings set org.gnome.system.proxy.https port " + QSTRN(port)) == QProcess::NormalExit;
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
LOG(MODULE_PROXY, "Something wrong happens when setting system proxy -> Gnome ONLY.")
|
||||
LOG(MODULE_PROXY, "If you are using KDE Plasma and receiving this message, just simply ignore this.")
|
||||
}
|
||||
|
||||
return result;
|
||||
#else
|
||||
bool result = true;
|
||||
|
||||
for (auto service : macOSgetNetworkServices()) {
|
||||
LOG(MODULE_PROXY, "Setting proxy for interface: " + service)
|
||||
|
||||
if (usePAC) {
|
||||
result = result && QProcess::execute("/usr/sbin/networksetup -setautoproxystate " + service + " on") == QProcess::NormalExit;
|
||||
result = result && QProcess::execute("/usr/sbin/networksetup -setautoproxyurl " + service + " " + address) == QProcess::NormalExit;
|
||||
} else {
|
||||
result = result && QProcess::execute("/usr/sbin/networksetup -setwebproxystate " + service + " on") == QProcess::NormalExit;
|
||||
result = result && QProcess::execute("/usr/sbin/networksetup -setsecurewebproxystate " + service + " on") == QProcess::NormalExit;
|
||||
result = result && QProcess::execute("/usr/sbin/networksetup -setwebproxy " + service + " " + address + " " + QSTRN(port)) == QProcess::NormalExit;
|
||||
result = result && QProcess::execute("/usr/sbin/networksetup -setsecurewebproxy " + service + " " + address + " " + QSTRN(port)) == QProcess::NormalExit;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ClearSystemProxy()
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
LOG(MODULE_PROXY, "Cleaning system proxy settings.")
|
||||
INTERNET_PER_CONN_OPTION_LIST list;
|
||||
BOOL bReturn;
|
||||
DWORD dwBufSize = sizeof(list);
|
||||
// Fill out list struct.
|
||||
list.dwSize = sizeof(list);
|
||||
// nullptr == LAN, otherwise connectoid name.
|
||||
list.pszConnection = nullptr;
|
||||
// Set three options.
|
||||
list.dwOptionCount = 1;
|
||||
list.pOptions = new INTERNET_PER_CONN_OPTION[list.dwOptionCount];
|
||||
|
||||
// Make sure the memory was allocated.
|
||||
if (nullptr == list.pOptions) {
|
||||
// Return FALSE if the memory wasn't allocated.
|
||||
LOG(MODULE_PROXY, "Failed to allocat memory in DisableConnectionProxy()")
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Set flags.
|
||||
list.pOptions[0].dwOption = INTERNET_PER_CONN_FLAGS;
|
||||
list.pOptions[0].Value.dwValue = PROXY_TYPE_DIRECT;
|
||||
//
|
||||
// Set the options on the connection.
|
||||
bReturn = InternetSetOption(nullptr, INTERNET_OPTION_PER_CONNECTION_OPTION, &list, dwBufSize);
|
||||
delete [] list.pOptions;
|
||||
InternetSetOption(nullptr, INTERNET_OPTION_SETTINGS_CHANGED, nullptr, 0);
|
||||
InternetSetOption(nullptr, INTERNET_OPTION_REFRESH, nullptr, 0);
|
||||
return bReturn;
|
||||
#elif defined(Q_OS_LINUX)
|
||||
return QProcess::execute("gsettings set org.gnome.system.proxy mode 'none'") == QProcess::ExitStatus::NormalExit;
|
||||
#else
|
||||
bool result = true;
|
||||
|
||||
for (auto service : macOSgetNetworkServices()) {
|
||||
result = result && QProcess::execute("/usr/sbin/networksetup -setautoproxystate " + service + " off") == QProcess::NormalExit;
|
||||
result = result && QProcess::execute("/usr/sbin/networksetup -setwebproxystate " + service + " off") == QProcess::NormalExit;
|
||||
result = result && QProcess::execute("/usr/sbin/networksetup -setsecurewebproxystate " + service + " off") == QProcess::NormalExit;
|
||||
result = result && QProcess::execute("/usr/sbin/networksetup -setsocksfirewallproxystate " + service + " off") == QProcess::NormalExit;
|
||||
}
|
||||
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
#ifndef QVSYSTEMPROXYCONFIGURATOR_H
|
||||
#define QVSYSTEMPROXYCONFIGURATOR_H
|
||||
#include "QvUtils.hpp"
|
||||
#include <QObject>
|
||||
//
|
||||
namespace Qv2ray
|
||||
{
|
||||
namespace Components
|
||||
{
|
||||
bool ClearSystemProxy();
|
||||
bool SetSystemProxy(const QString &address, int port, bool usePAC);
|
||||
}
|
||||
}
|
||||
|
||||
using namespace Qv2ray::Components;
|
||||
|
||||
#endif // QVSYSTEMPROXYCONFIGURATOR_H
|
@ -1,175 +0,0 @@
|
||||
#include "QvTCPing.hpp"
|
||||
#include "QtConcurrent/QtConcurrent"
|
||||
|
||||
namespace Qv2ray
|
||||
{
|
||||
namespace Components
|
||||
{
|
||||
QvTCPingModel::QvTCPingModel(int defaultCount, QObject *parent) : QObject(parent)
|
||||
{
|
||||
count = defaultCount;
|
||||
}
|
||||
void QvTCPingModel::StopAllPing()
|
||||
{
|
||||
while (!pingWorkingThreads.isEmpty()) {
|
||||
auto worker = pingWorkingThreads.dequeue();
|
||||
worker->future().cancel();
|
||||
worker->cancel();
|
||||
}
|
||||
}
|
||||
void QvTCPingModel::StartPing(const QvConfigIdentifier &connectionName, const QString &hostName, int port)
|
||||
{
|
||||
QvTCPingData data;
|
||||
data.hostName = hostName;
|
||||
data.port = port;
|
||||
data.connectionIdentifier = connectionName;
|
||||
auto watcher = new QFutureWatcher<QvTCPingData>(this);
|
||||
DEBUG(MODULE_NETWORK, "Start Ping: " + hostName + ":" + QSTRN(port))
|
||||
watcher->setFuture(QtConcurrent::run(&QvTCPingModel::startTestLatency, data, count));
|
||||
pingWorkingThreads.enqueue(watcher);
|
||||
connect(watcher, &QFutureWatcher<void>::finished, this, [this, watcher]() {
|
||||
this->pingWorkingThreads.removeOne(watcher);
|
||||
auto result = watcher->result();
|
||||
DEBUG(MODULE_NETWORK, "Ping finished: " + result.hostName + ":" + QSTRN(result.port) + " --> " + QSTRN(result.avg) + "ms")
|
||||
|
||||
if (!result.errorMessage.isEmpty()) {
|
||||
LOG(MODULE_NETWORK, "Ping --> " + result.errorMessage)
|
||||
}
|
||||
|
||||
emit this->PingFinished(result);
|
||||
});
|
||||
}
|
||||
|
||||
QvTCPingData QvTCPingModel::startTestLatency(QvTCPingData data, const int count)
|
||||
{
|
||||
if (isExiting()) return QvTCPingData();
|
||||
|
||||
double successCount = 0, errorCount = 0;
|
||||
addrinfo *resolved;
|
||||
int errcode;
|
||||
|
||||
if ((errcode = resolveHost(data.hostName.toStdString(), data.port, &resolved)) != 0) {
|
||||
#if defined (__WIN32) && defined (UNICODE)
|
||||
data.errorMessage = QString::fromStdWString(gai_strerror(errcode));
|
||||
#else
|
||||
data.errorMessage = gai_strerror(errcode);
|
||||
#endif
|
||||
return data;
|
||||
}
|
||||
|
||||
bool noAddress = false;
|
||||
int currentCount = 0;
|
||||
|
||||
while (currentCount < count) {
|
||||
if (isExiting()) return QvTCPingData();
|
||||
|
||||
timeval rtt;
|
||||
|
||||
if ((errcode = testLatency(resolved, &rtt)) != 0) {
|
||||
if (errcode != -EADDRNOTAVAIL) {
|
||||
LOG(MODULE_NETWORK, "Error connecting to host: " + data.hostName + ":" + QSTRN(data.port) + " " + strerror(-errcode))
|
||||
errorCount++;
|
||||
} else {
|
||||
if (noAddress) {
|
||||
LOG(MODULE_NETWORK, ".")
|
||||
} else {
|
||||
LOG(MODULE_NETWORK, "error connecting to host: " + QSTRN(-errcode) + " " + strerror(-errcode))
|
||||
}
|
||||
|
||||
noAddress = true;
|
||||
}
|
||||
} else {
|
||||
noAddress = false;
|
||||
successCount++;
|
||||
double ms = (rtt.tv_sec * 1000.0) + (rtt.tv_usec / 1000.0);
|
||||
data.avg += ms;
|
||||
data.min = min(data.min, ms);
|
||||
data.max = max(data.max, ms);
|
||||
|
||||
if (ms > 1000) {
|
||||
break; /* Stop the test on the first long connect() */
|
||||
}
|
||||
}
|
||||
|
||||
currentCount++;
|
||||
QThread::msleep(500);
|
||||
}
|
||||
|
||||
data.avg = data.avg / successCount;
|
||||
freeaddrinfo(resolved);
|
||||
return data;
|
||||
}
|
||||
|
||||
int QvTCPingModel::resolveHost(const string &host, int port, addrinfo **res)
|
||||
{
|
||||
if (isExiting()) return 0;
|
||||
|
||||
addrinfo hints;
|
||||
#ifdef _WIN32
|
||||
WSADATA wsadata;
|
||||
WSAStartup(0x0202, &wsadata);
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
#else
|
||||
hints.ai_flags = AI_NUMERICSERV;
|
||||
#endif
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = 0;
|
||||
return getaddrinfo(host.c_str(), to_string(port).c_str(), &hints, res);
|
||||
}
|
||||
|
||||
int QvTCPingModel::testLatency(struct addrinfo *addr, struct timeval *rtt)
|
||||
{
|
||||
if (isExiting()) return 0;
|
||||
|
||||
int fd;
|
||||
struct timeval start;
|
||||
int connect_result;
|
||||
const int on = 1;
|
||||
/* int flags; */
|
||||
int rv = 0;
|
||||
|
||||
/* try to connect for each of the entries: */
|
||||
while (addr != nullptr) {
|
||||
if (isExiting()) return 0;
|
||||
|
||||
/* create socket */
|
||||
if ((fd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol)) == -1)
|
||||
goto next_addr0;
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
// Windows needs special conversion.
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on)) < 0)
|
||||
#else
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
|
||||
#endif
|
||||
goto next_addr1;
|
||||
|
||||
if (gettimeofday(&start, nullptr) == -1)
|
||||
goto next_addr1;
|
||||
|
||||
/* connect to peer */
|
||||
// Qt has its own connect() function in QObject....
|
||||
// So we add "::" here
|
||||
if ((connect_result = ::connect(fd, addr->ai_addr, addr->ai_addrlen)) == 0) {
|
||||
if (gettimeofday(rtt, nullptr) == -1)
|
||||
goto next_addr1;
|
||||
|
||||
rtt->tv_sec = rtt->tv_sec - start.tv_sec;
|
||||
rtt->tv_usec = rtt->tv_usec - start.tv_usec;
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
next_addr1:
|
||||
close(fd);
|
||||
next_addr0:
|
||||
addr = addr->ai_next;
|
||||
}
|
||||
|
||||
rv = rv ? rv : -errno;
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
#ifndef QVTCPING_H
|
||||
#define QVTCPING_H
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2def.h>
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#endif
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include "QvUtils.hpp"
|
||||
|
||||
|
||||
namespace Qv2ray
|
||||
{
|
||||
namespace Components
|
||||
{
|
||||
struct QvTCPingData {
|
||||
QvConfigIdentifier connectionIdentifier;
|
||||
QString hostName;
|
||||
int port;
|
||||
QString errorMessage;
|
||||
int total, succeed, failed;
|
||||
double min = 999999999999999.0, max = 0.0, avg = 0.0;
|
||||
};
|
||||
//
|
||||
|
||||
class QvTCPingModel : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit QvTCPingModel(int defaultCount = 5, QObject *parent = nullptr);
|
||||
void StartPing(const QvConfigIdentifier &connectionName, const QString &hostName, int port);
|
||||
void StopAllPing();
|
||||
signals:
|
||||
void PingFinished(QvTCPingData data);
|
||||
private:
|
||||
static int resolveHost(const string &host, int portnr, struct addrinfo **res);
|
||||
static int testLatency(struct addrinfo *addr, struct timeval *rtt);
|
||||
static QvTCPingData startTestLatency(QvTCPingData data, const int count);
|
||||
int count;
|
||||
QQueue<QFutureWatcher<QvTCPingData>*> pingWorkingThreads;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
using namespace Qv2ray;
|
||||
using namespace Qv2ray::Components;
|
||||
#endif
|
186
src/components/autolaunch/QvAutoLaunch.cpp
Normal file
186
src/components/autolaunch/QvAutoLaunch.cpp
Normal file
@ -0,0 +1,186 @@
|
||||
#include "QvAutoLaunch.hpp"
|
||||
#include <QSettings>
|
||||
#include <QApplication>
|
||||
#include <QDir>
|
||||
#include <QStandardPaths>
|
||||
#include <QTextStream>
|
||||
// macOS headers (possibly OBJ-c)
|
||||
#if defined(Q_OS_MAC)
|
||||
#include <CoreServices/CoreServices.h>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#endif
|
||||
namespace Qv2ray::components::autolaunch
|
||||
{
|
||||
//
|
||||
// launchatlogin.cpp
|
||||
// ShadowClash
|
||||
//
|
||||
// Created by TheWanderingCoel on 2018/6/12.
|
||||
// Copyright © 2019 Coel Wu. All rights reserved.
|
||||
//
|
||||
QString getUserAutostartDir_private()
|
||||
{
|
||||
QString config = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation);
|
||||
config += QLatin1String("/autostart/");
|
||||
return config;
|
||||
}
|
||||
|
||||
bool GetLaunchAtLoginStatus()
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
QString appName = QApplication::applicationName();
|
||||
QSettings reg("HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", QSettings::NativeFormat);
|
||||
return reg.contains(appName);
|
||||
}
|
||||
|
||||
#elif defined Q_OS_MAC
|
||||
// From https://github.com/nextcloud/desktop/blob/master/src/common/utility_mac.cpp
|
||||
// this is quite some duplicate code with setLaunchOnStartup, at some point we should fix this FIXME.
|
||||
bool returnValue = false;
|
||||
QString filePath = QDir(QCoreApplication::applicationDirPath() + QLatin1String("/../..")).absolutePath();
|
||||
CFStringRef folderCFStr = CFStringCreateWithCString(0, filePath.toUtf8().data(), kCFStringEncodingUTF8);
|
||||
CFURLRef urlRef = CFURLCreateWithFileSystemPath(0, folderCFStr, kCFURLPOSIXPathStyle, true);
|
||||
LSSharedFileListRef loginItems = LSSharedFileListCreate(0, kLSSharedFileListSessionLoginItems, 0);
|
||||
|
||||
if (loginItems)
|
||||
{
|
||||
// We need to iterate over the items and check which one is "ours".
|
||||
UInt32 seedValue;
|
||||
CFArrayRef itemsArray = LSSharedFileListCopySnapshot(loginItems, &seedValue);
|
||||
CFStringRef appUrlRefString = CFURLGetString(urlRef); // no need for release
|
||||
|
||||
for (int i = 0; i < CFArrayGetCount(itemsArray); i++) {
|
||||
LSSharedFileListItemRef item = (LSSharedFileListItemRef)CFArrayGetValueAtIndex(itemsArray, i);
|
||||
CFURLRef itemUrlRef = NULL;
|
||||
|
||||
if (LSSharedFileListItemResolve(item, 0, &itemUrlRef, NULL) == noErr && itemUrlRef) {
|
||||
CFStringRef itemUrlString = CFURLGetString(itemUrlRef);
|
||||
|
||||
if (CFStringCompare(itemUrlString, appUrlRefString, 0) == kCFCompareEqualTo) {
|
||||
returnValue = true;
|
||||
}
|
||||
|
||||
CFRelease(itemUrlRef);
|
||||
}
|
||||
}
|
||||
|
||||
CFRelease(itemsArray);
|
||||
}
|
||||
|
||||
CFRelease(loginItems);
|
||||
CFRelease(folderCFStr);
|
||||
CFRelease(urlRef);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
#elif defined Q_OS_LINUX
|
||||
QString appName = QApplication::applicationName();
|
||||
QString desktopFileLocation = getUserAutostartDir_private() + appName + QLatin1String(".desktop");
|
||||
return QFile::exists(desktopFileLocation);
|
||||
}
|
||||
#endif
|
||||
|
||||
void SetLaunchAtLoginStatus(bool enable)
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
QString appName = QApplication::applicationName();
|
||||
QSettings reg("HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", QSettings::NativeFormat);
|
||||
|
||||
if (enable) {
|
||||
QString strAppPath = QDir::toNativeSeparators(QCoreApplication::applicationFilePath());
|
||||
reg.setValue(appName, strAppPath);
|
||||
} else {
|
||||
reg.remove(appName);
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined Q_OS_MAC
|
||||
// From https://github.com/nextcloud/desktop/blob/master/src/common/utility_mac.cpp
|
||||
QString filePath = QDir(QCoreApplication::applicationDirPath() + QLatin1String("/../..")).absolutePath();
|
||||
CFStringRef folderCFStr = CFStringCreateWithCString(0, filePath.toUtf8().data(), kCFStringEncodingUTF8);
|
||||
CFURLRef urlRef = CFURLCreateWithFileSystemPath(0, folderCFStr, kCFURLPOSIXPathStyle, true);
|
||||
LSSharedFileListRef loginItems = LSSharedFileListCreate(0, kLSSharedFileListSessionLoginItems, 0);
|
||||
|
||||
if (loginItems && enable)
|
||||
{
|
||||
//Insert an item to the list.
|
||||
LSSharedFileListItemRef item = LSSharedFileListInsertItemURL(loginItems,
|
||||
kLSSharedFileListItemLast, 0, 0,
|
||||
urlRef, 0, 0);
|
||||
|
||||
if (item)
|
||||
CFRelease(item);
|
||||
|
||||
CFRelease(loginItems);
|
||||
} else if (loginItems && !enable)
|
||||
{
|
||||
// We need to iterate over the items and check which one is "ours".
|
||||
UInt32 seedValue;
|
||||
CFArrayRef itemsArray = LSSharedFileListCopySnapshot(loginItems, &seedValue);
|
||||
CFStringRef appUrlRefString = CFURLGetString(urlRef);
|
||||
|
||||
for (int i = 0; i < CFArrayGetCount(itemsArray); i++) {
|
||||
LSSharedFileListItemRef item = (LSSharedFileListItemRef)CFArrayGetValueAtIndex(itemsArray, i);
|
||||
CFURLRef itemUrlRef = NULL;
|
||||
|
||||
if (LSSharedFileListItemResolve(item, 0, &itemUrlRef, NULL) == noErr && itemUrlRef) {
|
||||
CFStringRef itemUrlString = CFURLGetString(itemUrlRef);
|
||||
|
||||
if (CFStringCompare(itemUrlString, appUrlRefString, 0) == kCFCompareEqualTo) {
|
||||
LSSharedFileListItemRemove(loginItems, item); // remove it!
|
||||
}
|
||||
|
||||
CFRelease(itemUrlRef);
|
||||
}
|
||||
}
|
||||
|
||||
CFRelease(itemsArray);
|
||||
CFRelease(loginItems);
|
||||
}
|
||||
|
||||
CFRelease(folderCFStr);
|
||||
CFRelease(urlRef);
|
||||
}
|
||||
|
||||
#elif defined Q_OS_LINUX
|
||||
// From https://github.com/nextcloud/desktop/blob/master/src/common/utility_unix.cpp
|
||||
QString appName = QApplication::applicationName();
|
||||
QString userAutoStartPath = getUserAutostartDir_private();
|
||||
QString desktopFileLocation = userAutoStartPath + appName + QLatin1String(".desktop");
|
||||
|
||||
if (enable)
|
||||
{
|
||||
if (!QDir().exists(userAutoStartPath) && !QDir().mkpath(userAutoStartPath)) {
|
||||
// qCWarning(lcUtility) << "Could not create autostart folder" << userAutoStartPath;
|
||||
return;
|
||||
}
|
||||
|
||||
QFile iniFile(desktopFileLocation);
|
||||
|
||||
if (!iniFile.open(QIODevice::WriteOnly)) {
|
||||
// qCWarning(lcUtility) << "Could not write auto start entry" << desktopFileLocation;
|
||||
return;
|
||||
}
|
||||
|
||||
QTextStream ts(&iniFile);
|
||||
ts.setCodec("UTF-8");
|
||||
ts << QLatin1String("[Desktop Entry]") << endl
|
||||
<< QLatin1String("Name=") << QApplication::applicationName() << endl
|
||||
<< QLatin1String("GenericName=") << QLatin1String("V2ray Frontend") << endl
|
||||
<< QLatin1String("Exec=") << QCoreApplication::applicationFilePath() << endl
|
||||
<< QLatin1String("Terminal=") << "false" << endl
|
||||
<< QLatin1String("Icon=") << "qv2ray" << endl // always use lowercase for icons
|
||||
<< QLatin1String("Categories=") << "Network" << endl
|
||||
<< QLatin1String("Type=") << "Application" << endl
|
||||
<< QLatin1String("StartupNotify=") << "false" << endl
|
||||
<< QLatin1String("X-GNOME-Autostart-enabled=") << "true" << endl;
|
||||
} else
|
||||
{
|
||||
if (!QFile::remove(desktopFileLocation)) {
|
||||
// qCWarning(lcUtility) << "Could not remove autostart desktop file";
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
10
src/components/autolaunch/QvAutoLaunch.hpp
Normal file
10
src/components/autolaunch/QvAutoLaunch.hpp
Normal file
@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
namespace Qv2ray::components::autolaunch
|
||||
{
|
||||
bool GetLaunchAtLoginStatus();
|
||||
void SetLaunchAtLoginStatus(bool enable);
|
||||
}
|
||||
|
||||
using namespace Qv2ray::components;
|
||||
using namespace Qv2ray::components::autolaunch;
|
37
src/components/geosite/QvGeositeReader.cpp
Normal file
37
src/components/geosite/QvGeositeReader.cpp
Normal file
@ -0,0 +1,37 @@
|
||||
#include "QvGeositeReader.hpp"
|
||||
#include "libs/gen/v2ray_geosite.pb.h"
|
||||
|
||||
namespace Qv2ray::components::geosite
|
||||
{
|
||||
QStringList ReadGeoSiteFromFile(QString filepath)
|
||||
{
|
||||
QStringList list;
|
||||
LOG(FILEIO, "Reading geosites from: " + filepath)
|
||||
//
|
||||
GOOGLE_PROTOBUF_VERIFY_VERSION;
|
||||
//
|
||||
QFile f(filepath);
|
||||
bool opened = f.open(QFile::OpenModeFlag::ReadOnly);
|
||||
|
||||
if (!opened) {
|
||||
LOG(FILEIO, "File cannot be opened: " + filepath)
|
||||
return list;
|
||||
}
|
||||
|
||||
auto content = f.readAll();
|
||||
f.close();
|
||||
//
|
||||
GeoSiteList sites;
|
||||
sites.ParseFromArray(content.data(), content.size());
|
||||
|
||||
for (auto e : sites.entry()) {
|
||||
// We want to use lower string.
|
||||
list << QString::fromStdString(e.country_code()).toLower();
|
||||
}
|
||||
|
||||
LOG(FILEIO, "Loaded " + QSTRN(list.count()) + " geosite entries from data file.")
|
||||
// Optional: Delete all global objects allocated by libprotobuf.
|
||||
google::protobuf::ShutdownProtobufLibrary();
|
||||
return list;
|
||||
}
|
||||
}
|
10
src/components/geosite/QvGeositeReader.hpp
Normal file
10
src/components/geosite/QvGeositeReader.hpp
Normal file
@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
#include "base/Qv2rayBase.hpp"
|
||||
|
||||
namespace Qv2ray::components::geosite
|
||||
{
|
||||
QStringList ReadGeoSiteFromFile(QString filepath);
|
||||
}
|
||||
|
||||
using namespace Qv2ray::components;
|
||||
using namespace Qv2ray::components::geosite;
|
133
src/components/pac/QvGFWPACConverter.cpp
Normal file
133
src/components/pac/QvGFWPACConverter.cpp
Normal file
@ -0,0 +1,133 @@
|
||||
/* ORIGINAL LICENSE: Do What The F*ck You Want To Public License
|
||||
* AUTHOR: LBYPatrick
|
||||
*
|
||||
* MODIFIED BY Leroy.H.Y @lhy0403 re-licenced under GPLv3
|
||||
*/
|
||||
|
||||
#include "common/QvHelpers.hpp"
|
||||
|
||||
namespace Qv2ray::components::pac
|
||||
{
|
||||
// Private function
|
||||
string getRawDomain(string originLine)
|
||||
{
|
||||
size_t startPosition = 0;
|
||||
size_t endPosition = originLine.size();
|
||||
string returnBuffer;
|
||||
bool skipRule1 = originLine.find("[") != string::npos; // [Auto xxxx...
|
||||
bool skipRule2 = originLine.find("!") != string::npos; // Comments
|
||||
bool skipRule3 = originLine.find("@") != string::npos; // Non-proxy Lines
|
||||
bool skipRule4 = originLine.find("*") != string::npos;
|
||||
bool passRule1 = originLine.find("|") != string::npos; // Proxy Lines
|
||||
bool passRule2 = originLine.find(".") != string::npos; // Link-Contained Lines
|
||||
|
||||
if (originLine[endPosition] == '\n') {
|
||||
endPosition -= 1;
|
||||
}
|
||||
|
||||
if (originLine.find("http://") != string::npos) {
|
||||
startPosition += 8;
|
||||
} else if (originLine.find("https://") != string::npos) {
|
||||
startPosition += 9;
|
||||
}
|
||||
|
||||
// Skip unrelated lines
|
||||
if (skipRule1 || skipRule2 || skipRule3 || skipRule4) {
|
||||
return "";
|
||||
} else if (passRule2) {
|
||||
if (passRule1) {
|
||||
startPosition += originLine.find_last_of("|") + 1;
|
||||
}
|
||||
|
||||
if (originLine[startPosition] == '\n') startPosition += 1;
|
||||
|
||||
for (size_t i = startPosition; i < endPosition; ++i) {
|
||||
returnBuffer += originLine[i];
|
||||
}
|
||||
}
|
||||
|
||||
return returnBuffer;
|
||||
}
|
||||
|
||||
QString ConvertGFWToPAC(const QString &rawContent, const QString &customProxyString)
|
||||
{
|
||||
auto rawFileContent = Base64Decode(rawContent).toStdString();
|
||||
string readBuffer = ""; //cleanup
|
||||
string writeBuffer;
|
||||
string domainListCache = "";
|
||||
|
||||
for (size_t i = 0; i < rawFileContent.size(); ++i) {
|
||||
readBuffer += rawFileContent[i];
|
||||
|
||||
if (rawFileContent[i + 1] == '\n') {
|
||||
writeBuffer = getRawDomain(readBuffer);
|
||||
|
||||
if (writeBuffer != "") {
|
||||
domainListCache += writeBuffer + "\n";
|
||||
}
|
||||
|
||||
readBuffer = "";
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
size_t rotatorTwo = 0;
|
||||
string readDomainBuffer = "";
|
||||
bool isFirstLine = true;
|
||||
string outputContent = "";
|
||||
//Header
|
||||
outputContent += "var domains = {\n";
|
||||
|
||||
//Read and process output content line by line
|
||||
while (rotatorTwo < domainListCache.size()) {
|
||||
while (true) {
|
||||
//Get Domain
|
||||
readDomainBuffer += domainListCache[rotatorTwo];
|
||||
|
||||
if (domainListCache[rotatorTwo + 1] == '\n') {
|
||||
rotatorTwo += 2;
|
||||
break;
|
||||
}
|
||||
|
||||
rotatorTwo++;
|
||||
}
|
||||
|
||||
//Format
|
||||
if (!isFirstLine) outputContent += ",\n";
|
||||
else isFirstLine = false;
|
||||
|
||||
outputContent += "\t\"";
|
||||
outputContent += readDomainBuffer;
|
||||
outputContent += "\" : 1";
|
||||
readDomainBuffer = "";
|
||||
}
|
||||
|
||||
//End Message
|
||||
outputContent +=
|
||||
NEWLINE "};"
|
||||
NEWLINE ""
|
||||
NEWLINE " var proxy = \"" + customProxyString.toStdString() + ";\";" +
|
||||
NEWLINE " var direct = 'DIRECT;';"
|
||||
NEWLINE " function FindProxyForURL(url, host) {"
|
||||
NEWLINE " var suffix;"
|
||||
NEWLINE " var pos = host.lastIndexOf('.');"
|
||||
NEWLINE " pos = host.lastIndexOf('.', pos - 1);"
|
||||
NEWLINE " //"
|
||||
NEWLINE " while (1) {"
|
||||
NEWLINE " if (domains[host] != undefined) {"
|
||||
NEWLINE " return proxy;"
|
||||
NEWLINE " }"
|
||||
NEWLINE " else if (pos <= 0) {"
|
||||
NEWLINE " return domains['.' + host] != undefined ? proxy : direct;"
|
||||
NEWLINE " }"
|
||||
NEWLINE " suffix = host.substring(pos);"
|
||||
NEWLINE " if (domains[suffix] != undefined) {"
|
||||
NEWLINE " return proxy;"
|
||||
NEWLINE " }"
|
||||
NEWLINE " pos = host.lastIndexOf('.', pos - 1);"
|
||||
NEWLINE " }"
|
||||
NEWLINE " }";
|
||||
//
|
||||
return QString::fromStdString(outputContent);
|
||||
}
|
||||
}
|
79
src/components/pac/QvPACHandler.cpp
Normal file
79
src/components/pac/QvPACHandler.cpp
Normal file
@ -0,0 +1,79 @@
|
||||
#include "QvPACHandler.hpp"
|
||||
#include "qhttprequest.h"
|
||||
#include "qhttpresponse.h"
|
||||
#include "core/CoreUtils.hpp"
|
||||
#include "common/QvHelpers.hpp"
|
||||
|
||||
namespace Qv2ray::components::pac
|
||||
{
|
||||
|
||||
PACServer::PACServer() : QObject(), pacServer(this)
|
||||
{
|
||||
connect(&pacServer, &QHttpServer::newRequest, this, &PACServer::onNewRequest);
|
||||
}
|
||||
PACServer::~PACServer()
|
||||
{
|
||||
if (isStarted) {
|
||||
pacServer.close();
|
||||
}
|
||||
}
|
||||
void PACServer::SetProxyString(const QString &proxyString)
|
||||
{
|
||||
DEBUG(PROXY, "Setting new PAC proxy string: " + proxyString)
|
||||
this->proxyString = proxyString;
|
||||
}
|
||||
void PACServer::StartListen()
|
||||
{
|
||||
LOG(PROXY, "Starting PAC listener")
|
||||
//
|
||||
auto address = GlobalConfig.inboundConfig.listenip;
|
||||
auto port = GlobalConfig.inboundConfig.pacConfig.port;
|
||||
//
|
||||
DEBUG(PROXY, "PAC Listening local endpoint: " + address + ":" + QSTRN(port))
|
||||
//
|
||||
QString gfwContent = StringFromFile(QV2RAY_RULES_GFWLIST_PATH);
|
||||
pacContent = ConvertGFWToPAC(gfwContent, proxyString);
|
||||
//
|
||||
auto result = pacServer.listen(QHostAddress(address), static_cast<ushort>(port));
|
||||
|
||||
if (result) {
|
||||
isStarted = true;
|
||||
DEBUG(PROXY, "Started PAC handler")
|
||||
} else {
|
||||
LOG(PROXY, "Failed to listen on port " + QSTRN(port) + ", possible permission denied.")
|
||||
QvMessageBoxWarn(nullptr, tr("PAC Handler"), tr("Failed to listen PAC request on this port, please verify the permissions"));
|
||||
}
|
||||
}
|
||||
|
||||
void PACServer::StopServer()
|
||||
{
|
||||
if (isStarted) {
|
||||
pacServer.close();
|
||||
DEBUG(PROXY, "PAC Handler stopped.")
|
||||
isStarted = false;
|
||||
}
|
||||
}
|
||||
|
||||
void PACServer::onNewRequest(QHttpRequest *req, QHttpResponse *rsp)
|
||||
{
|
||||
rsp->setHeader("Server", "Qv2ray/" QV2RAY_VERSION_STRING " PAC_Handler");
|
||||
|
||||
if (req->method() == QHttpRequest::HTTP_GET) {
|
||||
//
|
||||
if (req->path() == "/pac") {
|
||||
DEBUG(PROXY, "Serving PAC file request.")
|
||||
//
|
||||
rsp->setHeader("Content-Type", "application/javascript; charset=utf-8");
|
||||
rsp->writeHead(QHttpResponse::StatusCode::STATUS_OK);
|
||||
rsp->end(pacContent.toUtf8());
|
||||
DEBUG(PROXY, "Serving a pac file...")
|
||||
} else {
|
||||
rsp->writeHead(QHttpResponse::StatusCode::STATUS_NOT_FOUND);
|
||||
rsp->end("NOT FOUND");
|
||||
}
|
||||
} else {
|
||||
rsp->writeHead(QHttpResponse::StatusCode::STATUS_METHOD_NOT_ALLOWED);
|
||||
rsp->end("PAC ONLY SUPPORT GET");
|
||||
}
|
||||
}
|
||||
}
|
33
src/components/pac/QvPACHandler.hpp
Normal file
33
src/components/pac/QvPACHandler.hpp
Normal file
@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
#include "qhttpserver.h"
|
||||
#include <QObject>
|
||||
#include <memory>
|
||||
|
||||
namespace Qv2ray::components::pac
|
||||
{
|
||||
QString ConvertGFWToPAC(const QString &rawContent, const QString &customProxyString);
|
||||
class PACServer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit PACServer();
|
||||
~PACServer();
|
||||
void SetProxyString(const QString &proxyString);
|
||||
void StartListen();
|
||||
void StopServer();
|
||||
|
||||
QString gfwFilePath;
|
||||
|
||||
public slots:
|
||||
void onNewRequest(QHttpRequest *request, QHttpResponse *response);
|
||||
|
||||
private:
|
||||
bool isStarted;
|
||||
QHttpServer pacServer;
|
||||
QString pacContent;
|
||||
QString proxyString;
|
||||
};
|
||||
}
|
||||
|
||||
using namespace Qv2ray::components;
|
||||
using namespace Qv2ray::components::pac;
|
158
src/components/plugins/toolbar/QvToolbar.cpp
Normal file
158
src/components/plugins/toolbar/QvToolbar.cpp
Normal file
@ -0,0 +1,158 @@
|
||||
#include <QThread>
|
||||
#include "ui/w_MainWindow.hpp"
|
||||
#include "components/plugins/toolbar/QvToolbar.hpp"
|
||||
#include "common/QvHelpers.hpp"
|
||||
|
||||
namespace Qv2ray::components::plugins
|
||||
{
|
||||
namespace Toolbar
|
||||
{
|
||||
void StopProcessingPlugins()
|
||||
{
|
||||
#ifdef Q_OS_LINUX
|
||||
_linux::StopMessageQThread();
|
||||
#endif
|
||||
#ifdef Q_OS_WIN
|
||||
_win::KillNamedPipeThread();
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Public Function - CALL ONLY ONCE -
|
||||
/// To start processing plugins' command.
|
||||
void StartProcessingPlugins()
|
||||
{
|
||||
#ifdef Q_OS_LINUX
|
||||
_linux::StartMessageQThread();
|
||||
#endif
|
||||
#ifdef Q_OS_WIN
|
||||
_win::StartNamedPipeThread();
|
||||
#endif
|
||||
}
|
||||
QString GetAnswerToRequest(const QString &pchRequest)
|
||||
{
|
||||
auto instance = MainWindow::mwInstance;
|
||||
|
||||
if (instance == nullptr || instance->vinstance == nullptr) {
|
||||
LOG(PLUGIN, "MainWindow != nullptr Assertion failed!")
|
||||
return "{}";
|
||||
}
|
||||
|
||||
auto vinstance = instance->vinstance;
|
||||
//
|
||||
auto req = pchRequest.trimmed();
|
||||
QString reply = "{}";
|
||||
|
||||
if (req == "START") {
|
||||
emit instance->Connect();
|
||||
} else if (req == "STOP") {
|
||||
emit instance->DisConnect();
|
||||
} else if (req == "RESTART") {
|
||||
emit instance->ReConnect();
|
||||
}
|
||||
|
||||
auto BarConfig = GlobalConfig.toolBarConfig;
|
||||
|
||||
for (auto i = 0; i < BarConfig.Pages.size(); i++) {
|
||||
for (auto j = 0; j < BarConfig.Pages[i].Lines.size(); j++) {
|
||||
#define CL BarConfig.Pages[i].Lines[j]
|
||||
|
||||
switch (CL.ContentType) {
|
||||
case 0: {
|
||||
// Custom Text
|
||||
// We do nothing...
|
||||
break;
|
||||
}
|
||||
|
||||
case 101: {
|
||||
// Current Time
|
||||
CL.Message = QTime().currentTime().toString("hh:mm:ss");
|
||||
break;
|
||||
}
|
||||
|
||||
case 102: {
|
||||
// Current Date
|
||||
CL.Message = QDate().currentDate().toString("yyyy-MM-dd");
|
||||
break;
|
||||
}
|
||||
|
||||
case 103: {
|
||||
// Current Qv2ray Version
|
||||
CL.Message = QV2RAY_VERSION_STRING;
|
||||
break;
|
||||
}
|
||||
|
||||
case 104: {
|
||||
// Current Connection Name
|
||||
CL.Message = instance->GetCurrentConnectedConfigName();
|
||||
break;
|
||||
}
|
||||
|
||||
case 105: {
|
||||
// Current Connection Status
|
||||
CL.Message = instance->vinstance->KernelStarted
|
||||
? QObject::tr("Connected")
|
||||
: QObject::tr("Disconnected");
|
||||
break;
|
||||
}
|
||||
|
||||
case 201: {
|
||||
// Total upload speed;
|
||||
CL.Message = FormatBytes(vinstance->getAllSpeedUp()) + "/s";
|
||||
break;
|
||||
}
|
||||
|
||||
case 202: {
|
||||
// Total download speed;
|
||||
CL.Message = FormatBytes(vinstance->getAllSpeedDown()) + "/s";
|
||||
break;
|
||||
}
|
||||
|
||||
case 203: {
|
||||
// Upload speed for tag
|
||||
CL.Message = FormatBytes(vinstance->getTagSpeedUp(CL.Message)) + "/s";
|
||||
break;
|
||||
}
|
||||
|
||||
case 204: {
|
||||
// Download speed for tag
|
||||
CL.Message = FormatBytes(vinstance->getTagSpeedDown(CL.Message)) + "/s";
|
||||
break;
|
||||
}
|
||||
|
||||
case 301: {
|
||||
// Total Upload
|
||||
CL.Message = FormatBytes(vinstance->getAllDataUp());
|
||||
break;
|
||||
}
|
||||
|
||||
case 302: {
|
||||
// Total download
|
||||
CL.Message = FormatBytes(vinstance->getAllDataDown());
|
||||
break;
|
||||
}
|
||||
|
||||
case 303: {
|
||||
// Upload for tag
|
||||
CL.Message = FormatBytes(vinstance->getTagDataUp(CL.Message));
|
||||
break;
|
||||
}
|
||||
|
||||
case 304: {
|
||||
// Download for tag
|
||||
CL.Message = FormatBytes(vinstance->getTagDataDown(CL.Message));
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
CL.Message = "Not Supported?";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reply = StructToJsonString(BarConfig);
|
||||
return reply;
|
||||
}
|
||||
}
|
||||
}
|
57
src/components/plugins/toolbar/QvToolbar.hpp
Normal file
57
src/components/plugins/toolbar/QvToolbar.hpp
Normal file
@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
#include "base/Qv2rayBase.hpp"
|
||||
#define QV2RAY_NETSPEED_PLUGIN_PIPE_NAME_LINUX "Qv2ray_NetSpeed_Widget_LocalSocket"
|
||||
#define QV2RAY_NETSPEED_PLUGIN_PIPE_NAME_WIN "\\\\.\\pipe\\qv2ray_desktop_netspeed_toolbar_pipe"
|
||||
|
||||
namespace Qv2ray::components::plugins
|
||||
{
|
||||
namespace Toolbar
|
||||
{
|
||||
/// NO NOT CHANGE THE ORDER
|
||||
static const QMap<int, QString> NetSpeedPluginMessages {
|
||||
{ 0, QObject::tr("Custom Text")},
|
||||
// Current Status
|
||||
{ 101, QObject::tr("Current Time") },
|
||||
{ 102, QObject::tr("Current Date") },
|
||||
{ 103, QObject::tr("Current Qv2ray Version") },
|
||||
{ 104, QObject::tr("Current Connection Name") },
|
||||
{ 105, QObject::tr("Current Connection Status") },
|
||||
// Speeds
|
||||
{ 201, QObject::tr("Total Upload Speed") },
|
||||
{ 202, QObject::tr("Total Download Speed") },
|
||||
{ 203, QObject::tr("Upload Speed for Specific Tag") },
|
||||
{ 204, QObject::tr("Download Speed for Specific Tag") },
|
||||
// Datas
|
||||
{ 301, QObject::tr("Total Uploaded Data") },
|
||||
{ 302, QObject::tr("Total Downloaded Data") },
|
||||
{ 303, QObject::tr("Uploaded Data for Specific Tag") },
|
||||
{ 304, QObject::tr("Downloaded Data for Specific Tag") }
|
||||
};
|
||||
void StartProcessingPlugins();
|
||||
void StopProcessingPlugins();
|
||||
#ifdef Q_OS_WIN
|
||||
namespace _win
|
||||
{
|
||||
void StartNamedPipeThread();
|
||||
void KillNamedPipeThread();
|
||||
}
|
||||
#endif
|
||||
#ifdef Q_OS_LINUX
|
||||
namespace _linux
|
||||
{
|
||||
// This function is called within a QThread
|
||||
// Actually it should the entrypoint of a thread.
|
||||
void StartMessageQThread();
|
||||
void StopMessageQThread();
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
QString GetAnswerToRequest(const QString &pchRequest);
|
||||
}
|
||||
}
|
||||
|
||||
using namespace Qv2ray::components;
|
||||
using namespace Qv2ray::components::plugins::Toolbar;
|
||||
|
84
src/components/plugins/toolbar/QvToolbar_linux.cpp
Normal file
84
src/components/plugins/toolbar/QvToolbar_linux.cpp
Normal file
@ -0,0 +1,84 @@
|
||||
#include <QtCore>
|
||||
#ifdef Q_OS_LINUX
|
||||
#include "components/plugins/toolbar/QvToolbar.hpp"
|
||||
#include "common/QvHelpers.hpp"
|
||||
#include <QLocalSocket>
|
||||
#include <QLocalServer>
|
||||
namespace Qv2ray::components::plugins::Toolbar
|
||||
{
|
||||
namespace _linux
|
||||
{
|
||||
static QThread *linuxWorkerThread;
|
||||
static QLocalServer *server;
|
||||
static volatile bool isExiting = false;
|
||||
|
||||
void qobject_proxy()
|
||||
{
|
||||
QLocalSocket *socket = server->nextPendingConnection();
|
||||
|
||||
if (!socket->waitForConnected() || !socket->waitForReadyRead())
|
||||
return;
|
||||
|
||||
try {
|
||||
while (!isExiting && socket->isOpen() && socket->isValid() && socket->waitForReadyRead()) {
|
||||
// CANNOT PROPERLY READ...
|
||||
// Temp-ly fixed (but why and how?)
|
||||
auto in = QString(socket->readAll());
|
||||
|
||||
if (!isExiting && !in.isEmpty()) {
|
||||
auto out = GetAnswerToRequest(in);
|
||||
//
|
||||
socket->write(out.toUtf8());
|
||||
socket->flush();
|
||||
} else {
|
||||
QThread::msleep(200);
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
LOG(PLUGIN, "Closing a broken socket.")
|
||||
}
|
||||
}
|
||||
void DataMessageQThread()
|
||||
{
|
||||
server = new QLocalServer();
|
||||
// BUG Sometimes failed to listen due to improper close of last session.
|
||||
bool listening = server->listen(QV2RAY_NETSPEED_PLUGIN_PIPE_NAME_LINUX);
|
||||
|
||||
while (!isExiting && !listening) {
|
||||
QThread::msleep(500);
|
||||
listening = server->listen(QV2RAY_NETSPEED_PLUGIN_PIPE_NAME_LINUX);
|
||||
}
|
||||
|
||||
bool timeOut = false;
|
||||
server->setSocketOptions(QLocalServer::WorldAccessOption);
|
||||
QObject::connect(server, &QLocalServer::newConnection, &qobject_proxy);
|
||||
|
||||
while (!isExiting) {
|
||||
bool result = server->waitForNewConnection(5000, &timeOut);
|
||||
DEBUG(PLUGIN, "Plugin thread listening failed: " + server->errorString())
|
||||
DEBUG(PLUGIN, "waitForNewConnection: " + QString(result ? "true" : "false") + ", " + QString(timeOut ? "true" : "false"))
|
||||
}
|
||||
|
||||
server->close();
|
||||
delete server;
|
||||
}
|
||||
void StartMessageQThread()
|
||||
{
|
||||
linuxWorkerThread = QThread::create(_linux::DataMessageQThread);
|
||||
linuxWorkerThread->start();
|
||||
}
|
||||
|
||||
void StopMessageQThread()
|
||||
{
|
||||
isExiting = true;
|
||||
|
||||
if (linuxWorkerThread->isRunning()) {
|
||||
LOG(PLUGIN, "Waiting for linuxWorkerThread to stop.")
|
||||
linuxWorkerThread->wait();
|
||||
}
|
||||
|
||||
delete _linux::linuxWorkerThread;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
114
src/components/plugins/toolbar/QvToolbar_win.cpp
Normal file
114
src/components/plugins/toolbar/QvToolbar_win.cpp
Normal file
@ -0,0 +1,114 @@
|
||||
#include <QtCore>
|
||||
#ifdef Q_OS_WIN
|
||||
#include "components/plugins/toolbar/QvToolbar.hpp"
|
||||
#include "common/QvHelpers.hpp"
|
||||
#include <windows.h>
|
||||
namespace Qv2ray::components::plugins::Toolbar
|
||||
{
|
||||
namespace _win
|
||||
{
|
||||
// Private Headers
|
||||
constexpr int BUFSIZE = 10240;
|
||||
DWORD WINAPI NamedPipeMasterThread(LPVOID lpvParam);
|
||||
DWORD WINAPI InstanceThread(LPVOID);
|
||||
static LPVOID ThreadHandle;
|
||||
static bool isExiting = false;
|
||||
|
||||
//
|
||||
void KillNamedPipeThread()
|
||||
{
|
||||
isExiting = true;
|
||||
TerminateThread(ThreadHandle, 0);
|
||||
}
|
||||
//
|
||||
void StartNamedPipeThread()
|
||||
{
|
||||
auto hThread = CreateThread(nullptr, 0, NamedPipeMasterThread, nullptr, 0, nullptr);
|
||||
|
||||
if (hThread == nullptr) {
|
||||
LOG(PLUGIN, "CreateThread failed, GLE=" + QSTRN(GetLastError()))
|
||||
return;
|
||||
} else CloseHandle(hThread);
|
||||
}
|
||||
|
||||
DWORD WINAPI NamedPipeMasterThread(LPVOID lpvParam)
|
||||
{
|
||||
Q_UNUSED(lpvParam)
|
||||
BOOL fConnected = FALSE;
|
||||
DWORD dwThreadId = 0;
|
||||
HANDLE hPipe = INVALID_HANDLE_VALUE;
|
||||
auto lpszPipename = QString(QV2RAY_NETSPEED_PLUGIN_PIPE_NAME_WIN).toStdWString();
|
||||
|
||||
while (!isExiting) {
|
||||
//printf("Pipe Server: Main thread awaiting client connection on %s\n", lpszPipename.c_str());
|
||||
hPipe = CreateNamedPipe(lpszPipename.c_str(), PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, BUFSIZE, BUFSIZE, 0, nullptr);
|
||||
|
||||
if (hPipe == INVALID_HANDLE_VALUE) {
|
||||
LOG(PLUGIN, "CreateNamedPipe failed, GLE=" + QSTRN(GetLastError()))
|
||||
return static_cast<DWORD>(-1);
|
||||
}
|
||||
|
||||
fConnected = ConnectNamedPipe(hPipe, nullptr) ? true : (GetLastError() == ERROR_PIPE_CONNECTED);
|
||||
|
||||
if (fConnected) {
|
||||
LOG(PLUGIN, "Client connected, creating a processing thread")
|
||||
ThreadHandle = CreateThread(nullptr, 0, InstanceThread, hPipe, 0, &dwThreadId);
|
||||
|
||||
if (ThreadHandle == nullptr) {
|
||||
LOG(PLUGIN, "CreateThread failed, GLE=" + QSTRN(GetLastError()))
|
||||
return static_cast<DWORD>(-1);
|
||||
} else CloseHandle(ThreadHandle);
|
||||
} else CloseHandle(hPipe);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DWORD WINAPI InstanceThread(LPVOID lpvParam)
|
||||
{
|
||||
DWORD cbBytesRead = 0, cbReplyBytes = 0, cbWritten = 0;
|
||||
BOOL fSuccess = false;
|
||||
HANDLE hPipe = static_cast<HANDLE>(lpvParam);
|
||||
TCHAR pchRequest[BUFSIZE] = { 0 };
|
||||
|
||||
while (!isExiting) {
|
||||
fSuccess = ReadFile(hPipe, pchRequest, BUFSIZE * sizeof(TCHAR), &cbBytesRead, nullptr);
|
||||
|
||||
if (!fSuccess || cbBytesRead == 0) {
|
||||
if (GetLastError() == ERROR_BROKEN_PIPE) {
|
||||
LOG(PLUGIN, "InstanceThread: client disconnected, GLE=" + QSTRN(GetLastError()))
|
||||
} else {
|
||||
LOG(PLUGIN, "InstanceThread ReadFile failed, GLE=" + QSTRN(GetLastError()))
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
auto req = QString::fromStdWString(pchRequest);
|
||||
QString replyQString = "{}";
|
||||
|
||||
if (!isExiting) {
|
||||
replyQString = GetAnswerToRequest(req);
|
||||
//
|
||||
// REPLY as std::string
|
||||
std::string pchReply = replyQString.toUtf8().constData();
|
||||
cbReplyBytes = static_cast<DWORD>(pchReply.length() + 1) * sizeof(CHAR);
|
||||
//cbReplyBytes = static_cast<DWORD>(replyQString.length() + 1) * sizeof(TCHAR);
|
||||
//
|
||||
fSuccess = WriteFile(hPipe, pchReply.c_str(), cbReplyBytes, &cbWritten, nullptr);
|
||||
|
||||
if (!fSuccess || cbReplyBytes != cbWritten) {
|
||||
LOG(PLUGIN, "InstanceThread WriteFile failed, GLE=" + QSTRN(GetLastError()))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FlushFileBuffers(hPipe);
|
||||
DisconnectNamedPipe(hPipe);
|
||||
CloseHandle(hPipe);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
318
src/components/proxy/QvProxyConfigurator.cpp
Normal file
318
src/components/proxy/QvProxyConfigurator.cpp
Normal file
@ -0,0 +1,318 @@
|
||||
#include "QvProxyConfigurator.hpp"
|
||||
#include "common/QvHelpers.hpp"
|
||||
#ifdef Q_OS_WIN
|
||||
#include "wininet.h"
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
namespace Qv2ray::components::proxy
|
||||
{
|
||||
|
||||
#ifdef Q_OS_MACOS
|
||||
QStringList macOSgetNetworkServices()
|
||||
{
|
||||
QProcess p;
|
||||
p.setProgram("/usr/sbin/networksetup");
|
||||
p.setArguments(QStringList() << "-listallnetworkservices");
|
||||
p.start();
|
||||
p.waitForStarted();
|
||||
p.waitForFinished();
|
||||
LOG(PROXY, p.errorString())
|
||||
auto str = p.readAllStandardOutput();
|
||||
auto lines = SplitLines(str);
|
||||
QStringList result;
|
||||
|
||||
// Start from 1 since first line is unneeded.
|
||||
for (auto i = 1; i < lines.count(); i++) {
|
||||
// * means disabled.
|
||||
if (!lines[i].contains("*")) {
|
||||
result << (lines[i].contains(" ") ? "\"" + lines[i] + "\"" : lines[i]);
|
||||
}
|
||||
}
|
||||
|
||||
LOG(PROXY, "Found " + QSTRN(result.size()) + " network services: " + result.join(";"))
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
#ifdef Q_OS_WIN
|
||||
#define NO_CONST(expr) const_cast<wchar_t *>(expr)
|
||||
//static auto DEFAULT_CONNECTION_NAME = NO_CONST(L"DefaultConnectionSettings");
|
||||
///
|
||||
/// INTERNAL FUNCTION
|
||||
bool __QueryProxyOptions()
|
||||
{
|
||||
INTERNET_PER_CONN_OPTION_LIST List;
|
||||
INTERNET_PER_CONN_OPTION Option[5];
|
||||
//
|
||||
unsigned long nSize = sizeof(INTERNET_PER_CONN_OPTION_LIST);
|
||||
Option[0].dwOption = INTERNET_PER_CONN_AUTOCONFIG_URL;
|
||||
Option[1].dwOption = INTERNET_PER_CONN_AUTODISCOVERY_FLAGS;
|
||||
Option[2].dwOption = INTERNET_PER_CONN_FLAGS;
|
||||
Option[3].dwOption = INTERNET_PER_CONN_PROXY_BYPASS;
|
||||
Option[4].dwOption = INTERNET_PER_CONN_PROXY_SERVER;
|
||||
//
|
||||
List.dwSize = sizeof(INTERNET_PER_CONN_OPTION_LIST);
|
||||
List.pszConnection = nullptr;// NO_CONST(DEFAULT_CONNECTION_NAME);
|
||||
List.dwOptionCount = 5;
|
||||
List.dwOptionError = 0;
|
||||
List.pOptions = Option;
|
||||
|
||||
if (!InternetQueryOption(nullptr, INTERNET_OPTION_PER_CONNECTION_OPTION, &List, &nSize)) {
|
||||
LOG(PROXY, "InternetQueryOption failed, GLE=" + QSTRN(GetLastError()))
|
||||
}
|
||||
|
||||
LOG(PROXY, "System default proxy info:")
|
||||
|
||||
if (Option[0].Value.pszValue != nullptr) {
|
||||
LOG(PROXY, QString::fromWCharArray(Option[0].Value.pszValue))
|
||||
}
|
||||
|
||||
if ((Option[2].Value.dwValue & PROXY_TYPE_AUTO_PROXY_URL) == PROXY_TYPE_AUTO_PROXY_URL) {
|
||||
LOG(PROXY, "PROXY_TYPE_AUTO_PROXY_URL")
|
||||
}
|
||||
|
||||
if ((Option[2].Value.dwValue & PROXY_TYPE_AUTO_DETECT) == PROXY_TYPE_AUTO_DETECT) {
|
||||
LOG(PROXY, "PROXY_TYPE_AUTO_DETECT")
|
||||
}
|
||||
|
||||
if ((Option[2].Value.dwValue & PROXY_TYPE_DIRECT) == PROXY_TYPE_DIRECT) {
|
||||
LOG(PROXY, "PROXY_TYPE_DIRECT")
|
||||
}
|
||||
|
||||
if ((Option[2].Value.dwValue & PROXY_TYPE_PROXY) == PROXY_TYPE_PROXY) {
|
||||
LOG(PROXY, "PROXY_TYPE_PROXY")
|
||||
}
|
||||
|
||||
if (!InternetQueryOption(nullptr, INTERNET_OPTION_PER_CONNECTION_OPTION, &List, &nSize)) {
|
||||
LOG(PROXY, "InternetQueryOption failed,GLE=" + QSTRN(GetLastError()))
|
||||
}
|
||||
|
||||
if (Option[4].Value.pszValue != nullptr) {
|
||||
LOG(PROXY, QString::fromStdWString(Option[4].Value.pszValue))
|
||||
}
|
||||
|
||||
INTERNET_VERSION_INFO Version;
|
||||
nSize = sizeof(INTERNET_VERSION_INFO);
|
||||
InternetQueryOption(nullptr, INTERNET_OPTION_VERSION, &Version, &nSize);
|
||||
|
||||
if (Option[0].Value.pszValue != nullptr) {
|
||||
GlobalFree(Option[0].Value.pszValue);
|
||||
}
|
||||
|
||||
if (Option[3].Value.pszValue != nullptr) {
|
||||
GlobalFree(Option[3].Value.pszValue);
|
||||
}
|
||||
|
||||
if (Option[4].Value.pszValue != nullptr) {
|
||||
GlobalFree(Option[4].Value.pszValue);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
bool __SetProxyOptions(LPWSTR proxy_full_addr, bool isPAC)
|
||||
{
|
||||
INTERNET_PER_CONN_OPTION_LIST list;
|
||||
BOOL bReturn;
|
||||
DWORD dwBufSize = sizeof(list);
|
||||
// Fill the list structure.
|
||||
list.dwSize = sizeof(list);
|
||||
// NULL == LAN, otherwise connectoid name.
|
||||
list.pszConnection = nullptr;
|
||||
|
||||
if (isPAC) {
|
||||
LOG(PROXY, "Setting system proxy for PAC")
|
||||
//
|
||||
list.dwOptionCount = 2;
|
||||
list.pOptions = new INTERNET_PER_CONN_OPTION[2];
|
||||
|
||||
// Ensure that the memory was allocated.
|
||||
if (nullptr == list.pOptions) {
|
||||
// Return FALSE if the memory wasn't allocated.
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Set flags.
|
||||
list.pOptions[0].dwOption = INTERNET_PER_CONN_FLAGS;
|
||||
list.pOptions[0].Value.dwValue = PROXY_TYPE_DIRECT | PROXY_TYPE_AUTO_PROXY_URL;
|
||||
// Set proxy name.
|
||||
list.pOptions[1].dwOption = INTERNET_PER_CONN_AUTOCONFIG_URL;
|
||||
list.pOptions[1].Value.pszValue = proxy_full_addr;
|
||||
} else {
|
||||
LOG(PROXY, "Setting system proxy for Global Proxy")
|
||||
//
|
||||
list.dwOptionCount = 3;
|
||||
list.pOptions = new INTERNET_PER_CONN_OPTION[3];
|
||||
|
||||
if (nullptr == list.pOptions) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set flags.
|
||||
list.pOptions[0].dwOption = INTERNET_PER_CONN_FLAGS;
|
||||
list.pOptions[0].Value.dwValue = PROXY_TYPE_DIRECT | PROXY_TYPE_PROXY;
|
||||
// Set proxy name.
|
||||
list.pOptions[1].dwOption = INTERNET_PER_CONN_PROXY_SERVER;
|
||||
list.pOptions[1].Value.pszValue = proxy_full_addr;
|
||||
// Set proxy override.
|
||||
list.pOptions[2].dwOption = INTERNET_PER_CONN_PROXY_BYPASS;
|
||||
auto localhost = L"localhost";
|
||||
list.pOptions[2].Value.pszValue = NO_CONST(localhost);
|
||||
}
|
||||
|
||||
// Set the options on the connection.
|
||||
bReturn = InternetSetOption(nullptr, INTERNET_OPTION_PER_CONNECTION_OPTION, &list, dwBufSize);
|
||||
delete [] list.pOptions;
|
||||
InternetSetOption(nullptr, INTERNET_OPTION_SETTINGS_CHANGED, nullptr, 0);
|
||||
InternetSetOption(nullptr, INTERNET_OPTION_REFRESH, nullptr, 0);
|
||||
return bReturn;
|
||||
}
|
||||
#endif
|
||||
|
||||
void SetSystemProxy(const QString &address, int httpPort, int socksPort, bool usePAC)
|
||||
{
|
||||
bool hasHTTP = (httpPort != 0);
|
||||
bool hasSOCKS = (socksPort != 0);
|
||||
|
||||
if (!(hasHTTP || hasSOCKS || usePAC)) {
|
||||
LOG(PROXY, "Nothing?")
|
||||
return;
|
||||
}
|
||||
|
||||
if (usePAC) {
|
||||
LOG(PROXY, "Qv2ray will set system proxy to use PAC file")
|
||||
} else {
|
||||
if (hasHTTP) {
|
||||
LOG(PROXY, "Qv2ray will set system proxy to use HTTP")
|
||||
}
|
||||
|
||||
if (hasSOCKS) {
|
||||
LOG(PROXY, "Qv2ray will set system proxy to use SOCKS")
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
QString __a;
|
||||
|
||||
if (usePAC) {
|
||||
__a = address;
|
||||
} else {
|
||||
__a = (hasHTTP ? "http://" : "socks5://") + address + ":" + QSTRN(httpPort);
|
||||
}
|
||||
|
||||
LOG(PROXY, "Windows proxy string: " + __a)
|
||||
auto proxyStrW = new WCHAR[__a.length() + 1];
|
||||
wcscpy(proxyStrW, __a.toStdWString().c_str());
|
||||
//
|
||||
__QueryProxyOptions();
|
||||
|
||||
if (!__SetProxyOptions(proxyStrW, usePAC)) {
|
||||
LOG(PROXY, "Failed to set proxy.")
|
||||
}
|
||||
|
||||
__QueryProxyOptions();
|
||||
#elif defined(Q_OS_LINUX)
|
||||
QStringList actions;
|
||||
auto proxyMode = usePAC ? "auto" : "manual";
|
||||
actions << QString("gsettings set org.gnome.system.proxy mode '%1'").arg(proxyMode);
|
||||
|
||||
if (usePAC) {
|
||||
actions << QString("gsettings set org.gnome.system.proxy autoconfig-url '%1'").arg(address);
|
||||
} else {
|
||||
if (hasHTTP) {
|
||||
actions << QString("gsettings set org.gnome.system.proxy.http host '%1'").arg(address);
|
||||
actions << QString("gsettings set org.gnome.system.proxy.http port %1").arg(httpPort);
|
||||
//
|
||||
actions << QString("gsettings set org.gnome.system.proxy.https host '%1'").arg(address);
|
||||
actions << QString("gsettings set org.gnome.system.proxy.https port %1").arg(httpPort);;
|
||||
}
|
||||
|
||||
if (hasSOCKS) {
|
||||
actions << QString("gsettings set org.gnome.system.proxy.socks host '%1'").arg(address);
|
||||
actions << QString("gsettings set org.gnome.system.proxy.socks port %1").arg(socksPort);
|
||||
}
|
||||
}
|
||||
|
||||
// note: do not use std::all_of / any_of / none_of,
|
||||
// because those are short-circuit and cannot guarantee atomicity.
|
||||
auto result = std::count_if(actions.cbegin(), actions.cend(), [](const QString & action) {
|
||||
DEBUG(PROXY, action)
|
||||
return QProcess::execute(action) == QProcess::NormalExit;
|
||||
}) == actions.size();
|
||||
|
||||
if (!result) {
|
||||
LOG(PROXY, "Something wrong happens when setting system proxy -> Gnome ONLY.")
|
||||
LOG(PROXY, "If you are using KDE Plasma and receiving this message, just simply ignore this.")
|
||||
}
|
||||
|
||||
Q_UNUSED(result);
|
||||
#else
|
||||
|
||||
for (auto service : macOSgetNetworkServices()) {
|
||||
LOG(PROXY, "Setting proxy for interface: " + service)
|
||||
|
||||
if (usePAC) {
|
||||
QProcess::execute("/usr/sbin/networksetup -setautoproxystate " + service + " on");
|
||||
QProcess::execute("/usr/sbin/networksetup -setautoproxyurl " + service + " " + address);
|
||||
} else {
|
||||
if (hasHTTP) {
|
||||
QProcess::execute("/usr/sbin/networksetup -setwebproxystate " + service + " on");
|
||||
QProcess::execute("/usr/sbin/networksetup -setsecurewebproxystate " + service + " on");
|
||||
QProcess::execute("/usr/sbin/networksetup -setwebproxy " + service + " " + address + " " + QSTRN(httpPort));
|
||||
QProcess::execute("/usr/sbin/networksetup -setsecurewebproxy " + service + " " + address + " " + QSTRN(httpPort));
|
||||
}
|
||||
|
||||
if (hasSOCKS) {
|
||||
QProcess::execute("/usr/sbin/networksetup -setsocksfirewallproxystate " + service + " on");
|
||||
QProcess::execute("/usr/sbin/networksetup -setsocksfirewallproxy " + service + " " + address + " " + QSTRN(socksPort));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void ClearSystemProxy()
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
LOG(PROXY, "Cleaning system proxy settings.")
|
||||
INTERNET_PER_CONN_OPTION_LIST list;
|
||||
BOOL bReturn;
|
||||
DWORD dwBufSize = sizeof(list);
|
||||
// Fill out list struct.
|
||||
list.dwSize = sizeof(list);
|
||||
// nullptr == LAN, otherwise connectoid name.
|
||||
list.pszConnection = nullptr;
|
||||
// Set three options.
|
||||
list.dwOptionCount = 1;
|
||||
list.pOptions = new INTERNET_PER_CONN_OPTION[list.dwOptionCount];
|
||||
|
||||
// Make sure the memory was allocated.
|
||||
if (nullptr == list.pOptions) {
|
||||
// Return FALSE if the memory wasn't allocated.
|
||||
LOG(PROXY, "Failed to allocat memory in DisableConnectionProxy()")
|
||||
}
|
||||
|
||||
// Set flags.
|
||||
list.pOptions[0].dwOption = INTERNET_PER_CONN_FLAGS;
|
||||
list.pOptions[0].Value.dwValue = PROXY_TYPE_DIRECT;
|
||||
//
|
||||
// Set the options on the connection.
|
||||
bReturn = InternetSetOption(nullptr, INTERNET_OPTION_PER_CONNECTION_OPTION, &list, dwBufSize);
|
||||
delete [] list.pOptions;
|
||||
InternetSetOption(nullptr, INTERNET_OPTION_SETTINGS_CHANGED, nullptr, 0);
|
||||
InternetSetOption(nullptr, INTERNET_OPTION_REFRESH, nullptr, 0);
|
||||
#elif defined(Q_OS_LINUX)
|
||||
QProcess::execute("gsettings set org.gnome.system.proxy mode 'none'");
|
||||
#else
|
||||
|
||||
for (auto service : macOSgetNetworkServices()) {
|
||||
LOG(PROXY, "Clearing proxy for interface: " + service)
|
||||
QProcess::execute("/usr/sbin/networksetup -setautoproxystate " + service + " off");
|
||||
QProcess::execute("/usr/sbin/networksetup -setwebproxystate " + service + " off");
|
||||
QProcess::execute("/usr/sbin/networksetup -setsecurewebproxystate " + service + " off");
|
||||
QProcess::execute("/usr/sbin/networksetup -setsocksfirewallproxystate " + service + " off");
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
12
src/components/proxy/QvProxyConfigurator.hpp
Normal file
12
src/components/proxy/QvProxyConfigurator.hpp
Normal file
@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include <QString>
|
||||
#include <QObject>
|
||||
//
|
||||
namespace Qv2ray::components::proxy
|
||||
{
|
||||
void ClearSystemProxy();
|
||||
void SetSystemProxy(const QString &address, int http_port, int socks_port, bool usePAC);
|
||||
}
|
||||
|
||||
using namespace Qv2ray::components;
|
||||
using namespace Qv2ray::components::proxy;
|
352
src/components/speedchart/speedplotview.cpp
Normal file
352
src/components/speedchart/speedplotview.cpp
Normal file
@ -0,0 +1,352 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015 Anton Lashkov <lenton_91@mail.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#include "speedplotview.hpp"
|
||||
|
||||
#include <QLocale>
|
||||
#include <QPainter>
|
||||
#include <QPen>
|
||||
#include <list>
|
||||
|
||||
#include <QCoreApplication>
|
||||
|
||||
#define VIEWABLE 60
|
||||
|
||||
// use binary prefix standards from IEC 60027-2
|
||||
// see http://en.wikipedia.org/wiki/Kilobyte
|
||||
enum class SizeUnit {
|
||||
Byte, // 1024^0,
|
||||
KibiByte, // 1024^1,
|
||||
MebiByte, // 1024^2,
|
||||
GibiByte, // 1024^3,
|
||||
TebiByte, // 1024^4,
|
||||
PebiByte, // 1024^5,
|
||||
ExbiByte // 1024^6,
|
||||
// int64 is used for sizes and thus the next units can not be handled
|
||||
// ZebiByte, // 1024^7,
|
||||
// YobiByte, // 1024^8
|
||||
};
|
||||
|
||||
namespace
|
||||
{
|
||||
const struct {
|
||||
const char *source;
|
||||
const char *comment;
|
||||
} units[] = {
|
||||
QT_TRANSLATE_NOOP3("misc", "B", "bytes"),
|
||||
QT_TRANSLATE_NOOP3("misc", "KiB", "kibibytes (1024 bytes)"),
|
||||
QT_TRANSLATE_NOOP3("misc", "MiB", "mebibytes (1024 kibibytes)"),
|
||||
QT_TRANSLATE_NOOP3("misc", "GiB", "gibibytes (1024 mibibytes)"),
|
||||
QT_TRANSLATE_NOOP3("misc", "TiB", "tebibytes (1024 gibibytes)"),
|
||||
QT_TRANSLATE_NOOP3("misc", "PiB", "pebibytes (1024 tebibytes)"),
|
||||
QT_TRANSLATE_NOOP3("misc", "EiB", "exbibytes (1024 pebibytes)")
|
||||
};
|
||||
}
|
||||
|
||||
QString unitString(const SizeUnit unit, const bool isSpeed)
|
||||
{
|
||||
const auto &unitString = units[static_cast<int>(unit)];
|
||||
QString ret = QCoreApplication::translate("misc", unitString.source, unitString.comment);
|
||||
|
||||
if (isSpeed)
|
||||
ret += QCoreApplication::translate("misc", "/s", "per second");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int friendlyUnitPrecision(const SizeUnit unit)
|
||||
{
|
||||
// friendlyUnit's number of digits after the decimal point
|
||||
switch (unit) {
|
||||
case SizeUnit::Byte:
|
||||
return 0;
|
||||
|
||||
case SizeUnit::KibiByte:
|
||||
case SizeUnit::MebiByte:
|
||||
return 1;
|
||||
|
||||
case SizeUnit::GibiByte:
|
||||
return 2;
|
||||
|
||||
default:
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
qlonglong sizeInBytes(qreal size, const SizeUnit unit)
|
||||
{
|
||||
for (int i = 0; i < static_cast<int>(unit); ++i)
|
||||
size *= 1024;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr typename std::add_const<T>::type &asConst(T &t) noexcept
|
||||
{
|
||||
return t;
|
||||
}
|
||||
|
||||
// Forward rvalue as const
|
||||
template <typename T>
|
||||
constexpr typename std::add_const<T>::type asConst(T &&t) noexcept
|
||||
{
|
||||
return std::move(t);
|
||||
}
|
||||
|
||||
// Prevent const rvalue arguments
|
||||
template <typename T>
|
||||
void asConst(const T &&) = delete;
|
||||
|
||||
namespace
|
||||
{
|
||||
// table of supposed nice steps for grid marks to get nice looking quarters of scale
|
||||
const double roundingTable[] = {1.2, 1.6, 2, 2.4, 2.8, 3.2, 4, 6, 8};
|
||||
|
||||
struct SplittedValue {
|
||||
double arg;
|
||||
SizeUnit unit;
|
||||
qint64 sizeInBytes() const
|
||||
{
|
||||
return ::sizeInBytes(arg, unit);
|
||||
}
|
||||
};
|
||||
|
||||
SplittedValue getRoundedYScale(double value)
|
||||
{
|
||||
if (value == 0.0) return {0, SizeUnit::Byte};
|
||||
|
||||
if (value <= 12.0) return {12, SizeUnit::Byte};
|
||||
|
||||
SizeUnit calculatedUnit = SizeUnit::Byte;
|
||||
|
||||
while (value > 1024) {
|
||||
value /= 1024;
|
||||
calculatedUnit = static_cast<SizeUnit>(static_cast<int>(calculatedUnit) + 1);
|
||||
}
|
||||
|
||||
if (value > 100.0) {
|
||||
int roundedValue = static_cast<int>(value / 40) * 40;
|
||||
|
||||
while (roundedValue < value)
|
||||
roundedValue += 40;
|
||||
|
||||
return {static_cast<double>(roundedValue), calculatedUnit};
|
||||
}
|
||||
|
||||
if (value > 10.0) {
|
||||
int roundedValue = static_cast<int>(value / 4) * 4;
|
||||
|
||||
while (roundedValue < value)
|
||||
roundedValue += 4;
|
||||
|
||||
return {static_cast<double>(roundedValue), calculatedUnit};
|
||||
}
|
||||
|
||||
for (const auto &roundedValue : roundingTable) {
|
||||
if (value <= roundedValue)
|
||||
return {roundedValue, calculatedUnit};
|
||||
}
|
||||
|
||||
return {10.0, calculatedUnit};
|
||||
}
|
||||
|
||||
QString formatLabel(const double argValue, const SizeUnit unit)
|
||||
{
|
||||
// check is there need for digits after decimal separator
|
||||
const int precision = (argValue < 10) ? friendlyUnitPrecision(unit) : 0;
|
||||
return QLocale::system().toString(argValue, 'f', precision)
|
||||
+ QString::fromUtf8(" ")
|
||||
+ unitString(unit, true);
|
||||
}
|
||||
}
|
||||
|
||||
SpeedPlotView::SpeedPlotView(QWidget *parent)
|
||||
: QGraphicsView(parent)
|
||||
, m_currentData(&m_datahalfMin)
|
||||
{
|
||||
QPen greenPen;
|
||||
greenPen.setWidthF(1.5);
|
||||
greenPen.setColor(QColor(134, 196, 63));
|
||||
QPen bluePen;
|
||||
bluePen.setWidthF(1.5);
|
||||
bluePen.setColor(QColor(50, 153, 255));
|
||||
m_properties[UP] = GraphProperties(tr("Total Upload"), bluePen);
|
||||
m_properties[DOWN] = GraphProperties(tr("Total Download"), greenPen);
|
||||
}
|
||||
|
||||
void SpeedPlotView::Clear()
|
||||
{
|
||||
m_datahalfMin.clear();
|
||||
replot();
|
||||
}
|
||||
|
||||
void SpeedPlotView::pushPoint(const SpeedPlotView::PointData &point)
|
||||
{
|
||||
m_datahalfMin.push_back(point);
|
||||
|
||||
while (m_datahalfMin.length() > VIEWABLE) {
|
||||
m_datahalfMin.removeFirst();
|
||||
}
|
||||
}
|
||||
|
||||
void SpeedPlotView::replot()
|
||||
{
|
||||
viewport()->update();
|
||||
}
|
||||
|
||||
QList<SpeedPlotView::PointData> &SpeedPlotView::getCurrentData()
|
||||
{
|
||||
return *m_currentData;
|
||||
}
|
||||
|
||||
quint64 SpeedPlotView::maxYValue()
|
||||
{
|
||||
auto &queue = getCurrentData();
|
||||
quint64 maxYValue = 0;
|
||||
|
||||
for (int id = UP; id < NB_GRAPHS; ++id) {
|
||||
// 30 is half min
|
||||
for (int i = queue.size() - 1, j = 0; (i >= 0) && (j <= VIEWABLE); --i, ++j) {
|
||||
if (queue[i].y[id] > maxYValue)
|
||||
maxYValue = queue[i].y[id];
|
||||
}
|
||||
}
|
||||
|
||||
return maxYValue;
|
||||
}
|
||||
|
||||
void SpeedPlotView::paintEvent(QPaintEvent *)
|
||||
{
|
||||
QPainter painter(viewport());
|
||||
QRect fullRect = viewport()->rect();
|
||||
QRect rect = viewport()->rect();
|
||||
QFontMetrics fontMetrics = painter.fontMetrics();
|
||||
rect.adjust(4, 4, 0, -4); // Add padding
|
||||
const SplittedValue niceScale = getRoundedYScale(maxYValue());
|
||||
rect.adjust(0, fontMetrics.height(), 0, 0); // Add top padding for top speed text
|
||||
// draw Y axis speed labels
|
||||
const QVector<QString> speedLabels = {
|
||||
formatLabel(niceScale.arg, niceScale.unit),
|
||||
formatLabel((0.75 * niceScale.arg), niceScale.unit),
|
||||
formatLabel((0.50 * niceScale.arg), niceScale.unit),
|
||||
formatLabel((0.25 * niceScale.arg), niceScale.unit),
|
||||
formatLabel(0.0, niceScale.unit),
|
||||
};
|
||||
int yAxisWidth = 0;
|
||||
|
||||
for (const QString &label : speedLabels)
|
||||
if (fontMetrics.horizontalAdvance(label) > yAxisWidth)
|
||||
yAxisWidth = fontMetrics.horizontalAdvance(label);
|
||||
|
||||
int i = 0;
|
||||
|
||||
for (const QString &label : speedLabels) {
|
||||
QRectF labelRect(rect.topLeft() + QPointF(-yAxisWidth, (i++) * 0.25 * rect.height() - fontMetrics.height()),
|
||||
QSizeF(2 * yAxisWidth, fontMetrics.height()));
|
||||
painter.drawText(labelRect, label, Qt::AlignRight | Qt::AlignTop);
|
||||
}
|
||||
|
||||
// draw grid lines
|
||||
rect.adjust(yAxisWidth + 4, 0, 0, 0);
|
||||
QPen gridPen;
|
||||
gridPen.setStyle(Qt::DashLine);
|
||||
gridPen.setWidthF(1);
|
||||
gridPen.setColor(QColor(128, 128, 128, 128));
|
||||
painter.setPen(gridPen);
|
||||
painter.drawLine(fullRect.left(), rect.top(), rect.right(), rect.top());
|
||||
painter.drawLine(fullRect.left(), rect.top() + 0.25 * rect.height(), rect.right(), rect.top() + 0.25 * rect.height());
|
||||
painter.drawLine(fullRect.left(), rect.top() + 0.50 * rect.height(), rect.right(), rect.top() + 0.50 * rect.height());
|
||||
painter.drawLine(fullRect.left(), rect.top() + 0.75 * rect.height(), rect.right(), rect.top() + 0.75 * rect.height());
|
||||
painter.drawLine(fullRect.left(), rect.bottom(), rect.right(), rect.bottom());
|
||||
const int TIME_AXIS_DIVISIONS = 6;
|
||||
|
||||
for (int i = 0; i < TIME_AXIS_DIVISIONS; ++i) {
|
||||
const int x = rect.left() + (i * rect.width()) / TIME_AXIS_DIVISIONS;
|
||||
painter.drawLine(x, fullRect.top(), x, fullRect.bottom());
|
||||
}
|
||||
|
||||
// Set antialiasing for graphs
|
||||
painter.setRenderHints(QPainter::Antialiasing);
|
||||
// draw graphs
|
||||
rect.adjust(3, 0, 0, 0); // Need, else graphs cross left gridline
|
||||
const double yMultiplier = (niceScale.arg == 0.0) ? 0.0 : (static_cast<double>(rect.height()) / niceScale.sizeInBytes());
|
||||
const double xTickSize = static_cast<double>(rect.width()) / VIEWABLE;
|
||||
auto &queue = getCurrentData();
|
||||
|
||||
for (int id = UP; id < NB_GRAPHS; ++id) {
|
||||
QVector<QPoint> points;
|
||||
|
||||
for (int i = static_cast<int>(queue.size()) - 1, j = 0; (i >= 0) && (j <= VIEWABLE); --i, ++j) {
|
||||
int newX = rect.right() - j * xTickSize;
|
||||
int newY = rect.bottom() - queue[i].y[id] * yMultiplier;
|
||||
points.push_back(QPoint(newX, newY));
|
||||
}
|
||||
|
||||
painter.setPen(m_properties[static_cast<GraphID>(id)].pen);
|
||||
painter.drawPolyline(points.data(), points.size());
|
||||
}
|
||||
|
||||
// draw legend
|
||||
QPoint legendTopLeft(rect.left() + 4, fullRect.top() + 4);
|
||||
double legendHeight = 0;
|
||||
int legendWidth = 0;
|
||||
|
||||
for (const auto &property : asConst(m_properties)) {
|
||||
if (fontMetrics.horizontalAdvance(property.name) > legendWidth)
|
||||
legendWidth = fontMetrics.horizontalAdvance(property.name);
|
||||
|
||||
legendHeight += 1.5 * fontMetrics.height();
|
||||
}
|
||||
|
||||
QRectF legendBackgroundRect(QPoint(legendTopLeft.x() - 4, legendTopLeft.y() - 4), QSizeF(legendWidth + 8, legendHeight + 8));
|
||||
QColor legendBackgroundColor = QWidget::palette().color(QWidget::backgroundRole());
|
||||
legendBackgroundColor.setAlpha(128); // 50% transparent
|
||||
painter.fillRect(legendBackgroundRect, legendBackgroundColor);
|
||||
i = 0;
|
||||
|
||||
for (const auto &property : asConst(m_properties)) {
|
||||
int nameSize = fontMetrics.horizontalAdvance(property.name);
|
||||
double indent = 1.5 * (i++) * fontMetrics.height();
|
||||
painter.setPen(property.pen);
|
||||
painter.drawLine(legendTopLeft + QPointF(0, indent + fontMetrics.height()),
|
||||
legendTopLeft + QPointF(nameSize, indent + fontMetrics.height()));
|
||||
painter.drawText(QRectF(legendTopLeft + QPointF(0, indent), QSizeF(2 * nameSize, fontMetrics.height())),
|
||||
property.name, QTextOption(Qt::AlignVCenter));
|
||||
}
|
||||
}
|
||||
|
||||
SpeedPlotView::GraphProperties::GraphProperties()
|
||||
{
|
||||
}
|
||||
|
||||
SpeedPlotView::GraphProperties::GraphProperties(const QString &name, const QPen &pen)
|
||||
: name(name)
|
||||
, pen(pen)
|
||||
{
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user