diff --git a/.github/ISSUE_TEMPLATE/bug---english.md b/.github/ISSUE_TEMPLATE/bug---english.md
index e3aafaf3..1c769dc1 100644
--- a/.github/ISSUE_TEMPLATE/bug---english.md
+++ b/.github/ISSUE_TEMPLATE/bug---english.md
@@ -45,7 +45,7 @@ Please paste your Qv2ray log here:
-### Open Preferences -> Aabout, and enter the following info
+### Open Preferences -> About, and enter the following info
```
Version:
@@ -82,3 +82,5 @@ Extra build info:
## Additional Info
+
+*Please hide your server address and UUID if you wish to post the vmess string or your connection setting.*
diff --git a/.github/ISSUE_TEMPLATE/bug.md b/.github/ISSUE_TEMPLATE/bug.md
index f6ab4ae3..fef1911c 100644
--- a/.github/ISSUE_TEMPLATE/bug.md
+++ b/.github/ISSUE_TEMPLATE/bug.md
@@ -82,3 +82,6 @@ assignees: ''
## 附加信息
+
+*请注意打码隐私相关信息。*
+
diff --git a/.github/workflows/build-qv2ray-cmake.yml b/.github/workflows/build-qv2ray-cmake.yml
index af4e0b14..38cde3bd 100644
--- a/.github/workflows/build-qv2ray-cmake.yml
+++ b/.github/workflows/build-qv2ray-cmake.yml
@@ -74,7 +74,7 @@ jobs:
path: ../Qt
key: QtCache-${{ matrix.platform }}-${{ matrix.arch }}-${{ matrix.qt_version }}
- name: Installing Qt - ${{ matrix.arch }}
- uses: jurplel/install-qt-action@v2.5.0
+ uses: jurplel/install-qt-action@v2
with:
version: ${{ matrix.qt_version }}
arch: ${{ matrix.qtarch }}
@@ -118,6 +118,7 @@ jobs:
shell: bash
if: matrix.platform == 'macos-latest'
run: |
+ sudo xcode-select -s "/Applications/Xcode_10.3.app"
mkdir build
cd build
cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_DEPLOYMENT_TARGET=10.13 -DDS_STORE_SCRIPT=ON
@@ -142,8 +143,8 @@ jobs:
if: matrix.platform == 'ubuntu-16.04'
shell: bash
env:
- CC: /usr/bin/gcc-9
- CXX: /usr/bin/g++-9
+ CC: /usr/bin/gcc-7
+ CXX: /usr/bin/g++-7
run: |
mkdir build
cd build
@@ -171,12 +172,23 @@ jobs:
cd ..
squashfs-root/AppRun AppDir/usr/share/applications/qv2ray.desktop -appimage -no-strip -always-overwrite
mv ./Qv2ray*.AppImage ./Qv2ray.AppImage
+ wget https://github.com/Qv2ray/Distribution/releases/download/0.0.0/fakeDeb.tar.gz
+ tar xzf fakeDeb.tar.gz
+ cd fakeDeb
+ cp ../Qv2ray.AppImage opt/qv2ray/Qv2ray.AppImage
+ dpkg-deb -b . ../qv2ray.deb
- 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/Qv2ray.AppImage
+ - name: Linux - ${{ matrix.qt_version }} - Uploading debian package
+ if: matrix.platform == 'ubuntu-16.04'
+ uses: actions/upload-artifact@master
+ with:
+ name: Qv2ray-${{ github.sha }}.linux-${{ matrix.arch }}.qt${{ matrix.qt_version }}.deb
+ path: build/qv2ray.deb
- 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' && matrix.qt_version == '5.14.2'
diff --git a/.github/workflows/deb.yml b/.github/workflows/deb.yml
index 1802f907..d23d601c 100644
--- a/.github/workflows/deb.yml
+++ b/.github/workflows/deb.yml
@@ -21,8 +21,9 @@ jobs:
linux:
strategy:
- matrix:
- distro: [stable, unstable]
+ fail-fast: false
+ matrix:
+ distro: [stable, unstable]
needs: check_commit_msg
if: ${{ !contains( needs.check_commit_msg.outputs.commit_message, 'NO_DEB') }}
name: Debian ${{ matrix.distro }}
@@ -40,20 +41,39 @@ jobs:
submodules: 'recursive'
- name: Install build dependencies
run: |
- apt-get install -y build-essential debhelper ninja-build libgrpc++-dev libprotobuf-dev protobuf-compiler-grpc qtbase5-dev qttools5-dev cmake pkg-config
+ apt-get install -y build-essential devscripts reprepro debhelper ninja-build libgrpc++-dev libprotobuf-dev protobuf-compiler-grpc qtbase5-dev qttools5-dev cmake pkg-config qtdeclarative5-dev
- name: Build
run: |
+ dch -l${{ matrix.distro }} -m 'Build against ${{ matrix.distro }}' -D ${{ matrix.distro }}
dpkg-buildpackage -us -uc -i -b
- name: Copy binary
run: |
cp ../qv2ray_*.deb ./
+ - name: Sleep
+ if: github.event_name == 'release' && matrix.distro == 'unstable'
+ run: |
+ sleep 120
+ - name: Setup Repository
+ if: github.event_name == 'release' && !contains(github.ref, 'alpha') && !contains(github.ref, 'beta') && !contains(github.ref, 'rc') && !contains(github.ref, 'pre')
+ run: |
+ git clone https://github.com/Qv2ray/debian.git archive
+ echo ${{ secrets.DEBIAN_REPO_KEY }} | base64 -d > private.key
+ gpg --import private.key
+ cd archive
+ git config --local user.name "${{ github.actor }}"
+ git config --local user.email "${{ github.actor }}@users.noreply.github.com"
+ git remote set-url origin https://${{ github.actor }}:${{ secrets.DEBIAN_REPO_TOKEN }}@github.com/Qv2ray/debian.git
+ reprepro includedeb ${{ matrix.distro }} ../*.deb
+ git add -A
+ git commit -am 'update'
+ git push origin master
- name: Get package name
id: get_package
run: echo ::set-output name=NAME::$(basename qv2ray_*.deb)
- name: Upload artifact
uses: actions/upload-artifact@v2-preview
with:
- name: qv2ray_${{ matrix.distro }}
+ name: ${{ steps.get_package.outputs.NAME }}
path: ${{ steps.get_package.outputs.NAME }}
- name: Upload binaries to release
uses: svenstaro/upload-release-action@v1-release
diff --git a/.github/workflows/nsis.yml b/.github/workflows/nsis.yml
index 6b84868c..36f9dbc9 100644
--- a/.github/workflows/nsis.yml
+++ b/.github/workflows/nsis.yml
@@ -68,7 +68,7 @@ jobs:
path: ../Qt
key: QtCache-${{ matrix.platform }}-${{ matrix.arch }}-${{ matrix.qt_version }}
- name: Installing Qt - ${{ matrix.arch }}
- uses: jurplel/install-qt-action@v2.5.0
+ uses: jurplel/install-qt-action@v2
with:
version: ${{ matrix.qt_version }}
arch: ${{ matrix.qtarch }}
diff --git a/.gitmodules b/.gitmodules
index acd86d16..b1cabddb 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,6 +1,3 @@
-[submodule "3rdparty/x2struct"]
- path = 3rdparty/x2struct
- url = https://github.com/xyz347/x2struct
[submodule "3rdparty/SingleApplication"]
path = 3rdparty/SingleApplication
url = https://github.com/itay-grudev/SingleApplication
@@ -16,6 +13,15 @@
[submodule "3rdparty/zxing-cpp"]
path = 3rdparty/zxing-cpp
url = https://github.com/nu-book/zxing-cpp
-[submodule "src/components/plugins/interface"]
- path = src/components/plugins/interface
+[submodule "src/plugin-interface"]
+ path = src/plugin-interface
url = https://github.com/Qv2ray/QvPlugin-Interface/
+[submodule "libs/QJsonStruct"]
+ path = libs/QJsonStruct
+ url = https://github.com/Qv2ray/QJsonStruct
+[submodule "3rdparty/uistyles"]
+ path = 3rdparty/uistyles
+ url = https://github.com/Qv2ray/QvUIStyles
+[submodule "3rdparty/backward-cpp"]
+ path = 3rdparty/backward-cpp
+ url = https://github.com/bombela/backward-cpp
diff --git a/.travis.yml b/.travis.yml
index 59026963..37e70b6f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,27 +2,8 @@ language: shell
os: linux
dist: bionic
-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 apt-get autoremove lxd --purge
- - sudo /snap/bin/lxd waitready
- - sudo /snap/bin/lxd init --auto
- - sudo snapcraft --use-lxd
-
-after_failure:
- - sudo journalctl -u snapd
+ - echo Refreshing launchpad repository
deploy:
- provider: launchpad
diff --git a/3rdparty/QNodeEditor b/3rdparty/QNodeEditor
index db07dd4f..427b6fe1 160000
--- a/3rdparty/QNodeEditor
+++ b/3rdparty/QNodeEditor
@@ -1 +1 @@
-Subproject commit db07dd4ffcbfdd62431584d499928e45b5864f40
+Subproject commit 427b6fe13049aae62855b893b524f17278e8c06f
diff --git a/3rdparty/SingleApplication b/3rdparty/SingleApplication
index 4baf2e74..bae7c331 160000
--- a/3rdparty/SingleApplication
+++ b/3rdparty/SingleApplication
@@ -1 +1 @@
-Subproject commit 4baf2e74f64c9a6ce36d456491bb41d0f2ae999e
+Subproject commit bae7c331ca7203a242e4533ba859c0c6016521ba
diff --git a/3rdparty/backward-cpp b/3rdparty/backward-cpp
new file mode 160000
index 00000000..29e40614
--- /dev/null
+++ b/3rdparty/backward-cpp
@@ -0,0 +1 @@
+Subproject commit 29e4061494e1ac4e40b2b09fd3e35c310ca137fc
diff --git a/3rdparty/uistyles b/3rdparty/uistyles
new file mode 160000
index 00000000..ccbc5a7e
--- /dev/null
+++ b/3rdparty/uistyles
@@ -0,0 +1 @@
+Subproject commit ccbc5a7ec83229e18804bf6b650b181f5279ca74
diff --git a/3rdparty/x2struct b/3rdparty/x2struct
deleted file mode 160000
index 45397646..00000000
--- a/3rdparty/x2struct
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 4539764671509655370b2ff4fae90bfd8a12df40
diff --git a/3rdparty/zxing-cpp b/3rdparty/zxing-cpp
index 66cc26b2..6d40262e 160000
--- a/3rdparty/zxing-cpp
+++ b/3rdparty/zxing-cpp
@@ -1 +1 @@
-Subproject commit 66cc26b25633cb7f1e20f2bf7711960c321e3a7b
+Subproject commit 6d40262e7293666909f7325075d452637f2fca75
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5c32e6b2..0eb186c1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -5,9 +5,7 @@ file(STRINGS "${CMAKE_SOURCE_DIR}/makespec/BUILDVERSION" QV2RAY_BUILD_VERSION)
file(STRINGS "${CMAKE_SOURCE_DIR}/makespec/VERSIONSUFFIX" QV2RAY_VERSION_SUFFIX)
if(NOT CMAKE_BUILD_TYPE STREQUAL "Release")
- math(EXPR QV2RAY_BUILD_VERSION "1 + ${QV2RAY_BUILD_VERSION}")
- message("Increasing BUILDVERSION")
- file(WRITE "${CMAKE_SOURCE_DIR}/makespec/BUILDVERSION" ${QV2RAY_BUILD_VERSION})
+ add_definitions(-DNODE_DEBUG_DRAWING)
endif()
set(QV2RAY_VERSION_STRING "${QV2RAY_VERSION}${QV2RAY_VERSION_SUFFIX}")
@@ -28,12 +26,30 @@ add_definitions(-DQV2RAY_VERSION_BUGFIX=${CPACK_PACKAGE_VERSION_PATCH})
add_definitions(-DQV2RAY_VERSION_BUILD=${QV2RAY_BUILD_VERSION})
add_definitions(-DQV2RAY_VERSION_STRING="${QV2RAY_VERSION_STRING}")
-add_definitions(-DXTOSTRUCT_QT)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
+if(MSVC)
+ set(CMAKE_CXX_EXTENSIONS OFF)
+endif()
-find_package(Qt5 5.11 COMPONENTS Core Gui Widgets Network REQUIRED)
+if(ANDROID)
+ include(${ANDROID_SDK}/android_openssl/CMakeLists.txt)
+endif()
+
+set(QV2RAY_ONLY_USE_QML OFF CACHE BOOL "Only use QML when building Qv2ray")
+
+if(QV2RAY_ONLY_USE_QML)
+ find_package(Qt5 5.11 COMPONENTS Core Gui Network Qml Quick REQUIRED)
+ set(QV2RAY_QT_LIBS Qt5::Core Qt5::Network Qt5::Quick Qt5::Qml Qt5::Gui)
+ add_definitions(-DQV2RAY_QML)
+else()
+ find_package(Qt5 5.11 COMPONENTS Core Gui Widgets Network REQUIRED)
+ set(QV2RAY_QT_LIBS Qt5::Core Qt5::Network Qt5::Widgets Qt5::Gui)
+ add_definitions(-DQV2RAY_QWIDGETS)
+endif()
+
+find_package(Threads REQUIRED)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
@@ -65,7 +81,7 @@ message(" ")
if(WIN32)
add_compile_options("/std:c++17")
add_definitions(-DUNICODE -D_UNICODE)
- add_definitions(-D_HAS_STD_BYTE=0 -D_WIN32_WINNT=0x600 -D_SCL_SECURE_NO_WARNINGS -D_CRT_SECURE_NO_WARNINGS)
+ add_definitions(-D_WIN32_WINNT=0x600 -D_SCL_SECURE_NO_WARNINGS -D_CRT_SECURE_NO_WARNINGS)
set(GUI_TYPE WIN32)
if(NOT DEFINED CMAKE_TOOLCHAIN_FILE)
if(CMAKE_CL_64)
@@ -76,19 +92,68 @@ if(WIN32)
endif()
endif()
-find_package(Threads REQUIRED)
-
+# ==================================================================================
+# Qv2ray Build Arguments
+# ==================================================================================
set(QV2RAY_QNODEEDITOR_PROVIDER "module" CACHE STRING "qnodeeditor provider")
set(QV2RAY_ZXING_PROVIDER "module" CACHE STRING "zxing-cpp provider")
+if (ANDROID)
+else()
+ set(QV2RAY_SINGLEAPPLICATION_PROVIDER "module" CACHE STRING "SingleApplication provider")
+endif()
+set(QV2RAY_DEFAULT_VASSETS_PATH "unset" CACHE STRING "v2ray assets path")
+set(QV2RAY_DEFAULT_VCORE_PATH "unset" CACHE STRING "v2ray core path")
+set(QV2RAY_TRANSLATION_PATH "unset" CACHE STRING "Qv2ray translations path")
+set(QV2RAY_DISABLE_AUTO_UPDATE OFF CACHE BOOL "Disable update checker")
+set(BUILD_TESTING OFF CACHE BOOL "Build test")
+set(EMBED_TRANSLATIONS OFF CACHE BOOL "Embed translations")
+set(QV2RAY_AUTO_DEPLOY ON CACHE BOOL "Automatically run deploy command after build")
+if(QV2RAY_DEFAULT_VASSETS_PATH AND NOT QV2RAY_DEFAULT_VASSETS_PATH STREQUAL "unset")
+ add_definitions(-DQV2RAY_DEFAULT_VASSETS_PATH="${QV2RAY_DEFAULT_VASSETS_PATH}")
+endif()
+
+if(QV2RAY_DEFAULT_VCORE_PATH AND NOT QV2RAY_DEFAULT_VCORE_PATH STREQUAL "unset")
+ add_definitions(-DQV2RAY_DEFAULT_VCORE_PATH="${QV2RAY_DEFAULT_VCORE_PATH}")
+endif()
+
+if(QV2RAY_TRANSLATION_PATH AND NOT QV2RAY_TRANSLATION_PATH STREQUAL "unset")
+ add_definitions(-DQV2RAY_TRANSLATION_PATH="${QV2RAY_TRANSLATION_PATH}")
+endif()
+
+if(QV2RAY_DISABLE_AUTO_UPDATE)
+ add_definitions(-DDISABLE_AUTO_UPDATE)
+endif()
+
+if(FALL_BACK_TO_XDG_OPEN)
+ add_definitions(-DFALL_BACK_TO_XDG_OPEN)
+endif()
+
+if(EMBED_TRANSLATIONS)
+ add_definitions(-DEMBED_TRANSLATIONS)
+ configure_file(translations/translations.qrc ${CMAKE_BINARY_DIR} COPYONLY)
+ set(QV2RAY_EMBED_TRANSLATION_QRC ${CMAKE_BINARY_DIR}/translations.qrc)
+endif()
+
+
+# ==================================================================================
+# 3rdparty Sources
+# ==================================================================================
include(cmake/translations.cmake)
include(cmake/qnodeeditor.cmake)
-include(cmake/singleapplication.cmake)
-include(cmake/protobuf.cmake)
-include(cmake/cpp-httplib.cmake)
-include(cmake/backend.cmake)
+if (ANDROID)
+else()
+ include(cmake/singleapplication.cmake)
+ include(cmake/protobuf.cmake)
+ include(cmake/backend.cmake)
+endif()
include(cmake/zxing-cpp.cmake)
+include(cmake/libsemver.cmake)
+
+# ==================================================================================
+# Qv2ray Build Info
+# ==================================================================================
if(QV2RAY_BUILD_INFO)
set(_QV2RAY_BUILD_INFO_STR_ "${QV2RAY_BUILD_INFO}")
elseif(DEFINED ENV{_QV2RAY_BUILD_INFO_})
@@ -105,264 +170,130 @@ else()
set(_QV2RAY_BUILD_EXTRA_INFO_STR_ "Qv2ray ${QV2RAY_VERSION_STRING}:${QV2RAY_BUILD_VERSION}")
endif()
-set(QV2RAY_BUILD_INFO ${_QV2RAY_BUILD_INFO_STR_} CACHE STRING "Qv2ray build info")
-set(QV2RAY_BUILD_EXTRA_INFO ${_QV2RAY_BUILD_EXTRA_INFO_STR_} CACHE STRING "Qv2ray build extra info")
+set(QV2RAY_BUILD_INFO ${_QV2RAY_BUILD_INFO_STR_})
+set(QV2RAY_BUILD_EXTRA_INFO ${_QV2RAY_BUILD_EXTRA_INFO_STR_})
add_definitions(-D_QV2RAY_BUILD_INFO_STR_="${_QV2RAY_BUILD_INFO_STR_}")
add_definitions(-D_QV2RAY_BUILD_EXTRA_INFO_STR_="${_QV2RAY_BUILD_EXTRA_INFO_STR_}")
message("Qv2ray build info: ${_QV2RAY_BUILD_INFO_STR_}")
message("Qv2ray build info ex: ${_QV2RAY_BUILD_EXTRA_INFO_STR_}")
-set(QV2RAY_DEFAULT_VASSETS_PATH "unset" CACHE STRING "v2ray assets path")
-set(QV2RAY_DEFAULT_VCORE_PATH "unset" CACHE STRING "v2ray core path")
-set(QV2RAY_TRANSLATION_PATH "unset" CACHE STRING "Qv2ray translations path")
-set(QV2RAY_DISABLE_AUTO_UPDATE OFF CACHE BOOL "Disable update checker")
-if(UNIX AND NOT APPLE)
- set(QV2RAY_USE_BUILTIN_DARKTHEME OFF CACHE BOOL "Use built-in dark theme instead of followed by the system settings")
-else()
- set(QV2RAY_USE_BUILTIN_DARKTHEME ON CACHE BOOL "Use built-in dark theme instead of followed by the system settings")
-endif()
-set(EMBED_TRANSLATIONS OFF CACHE BOOL "Embed translations")
-if(QV2RAY_DEFAULT_VASSETS_PATH AND NOT QV2RAY_DEFAULT_VASSETS_PATH STREQUAL "unset")
- add_definitions(-DQV2RAY_DEFAULT_VASSETS_PATH="${QV2RAY_DEFAULT_VASSETS_PATH}")
-endif()
+# ==================================================================================
+# Plugin Interface Headers
+# ==================================================================================
+set(QVPLUGIN_INTERFACE_INCLUDE_DIR "src/plugin-interface")
+include(src/plugin-interface/QvPluginInterface.cmake)
-if(QV2RAY_DEFAULT_VCORE_PATH AND NOT QV2RAY_DEFAULT_VCORE_PATH STREQUAL "unset")
- add_definitions(-DQV2RAY_DEFAULT_VCORE_PATH="${QV2RAY_DEFAULT_VCORE_PATH}")
-endif()
-if(QV2RAY_TRANSLATION_PATH AND NOT QV2RAY_TRANSLATION_PATH STREQUAL "unset")
- add_definitions(-DQV2RAY_TRANSLATION_PATH="${QV2RAY_TRANSLATION_PATH}")
-endif()
+# ==================================================================================
+# Qv2ray Sources and Headers
+# ==================================================================================
+include(cmake/components/qv2ray-lib.cmake)
+include(cmake/components/qv2ray-ui.cmake)
+include(3rdparty/uistyles/uistyles.cmake)
-if(QV2RAY_USE_BUILTIN_DARKTHEME)
- add_definitions(-DQV2RAY_USE_BUILTIN_DARKTHEME=true)
-endif()
+set(QRC_RESOURCES
+ ${UISTYLE_QRCS}
+ ${CMAKE_SOURCE_DIR}/resources.qrc)
-if(QV2RAY_DISABLE_AUTO_UPDATE)
- add_definitions(-DDISABLE_AUTO_UPDATE)
-endif()
-
-set(QVPLUGIN_INTERFACE_INCLUDE_DIR "src/components/plugins/interface")
-include(src/components/plugins/interface/QvPluginInterface.cmake)
-
-set(QV2RAY_SOURCES
+# Qv2ray baselib
+add_library(qv2ray-baselib STATIC
+ ${QV2RAY_BASE_HEADERS}
+ ${QV2RAY_LIB_SOURCES}
${QVPLUGIN_INTERFACE_HEADERS}
- 3rdparty/libsemver/version.cpp
- src/base/Qv2rayLog.cpp
- src/common/CommandArgs.cpp
- src/common/HTTPRequestHelper.cpp
- src/common/LogHighlighter.cpp
- src/common/JsonHighlighter.cpp
- src/common/QJsonModel.cpp
- src/common/QvHelpers.cpp
- src/common/QvTranslator.cpp
- src/common/QRCodeHelper.cpp
- src/components/autolaunch/QvAutoLaunch.cpp
- src/components/geosite/QvGeositeReader.cpp
- src/components/latency/win/ICMPPinger.cpp
- src/components/latency/QvTCPing.cpp
- src/components/plugins/toolbar/QvToolbar.cpp
- src/components/plugins/toolbar/QvToolbar_linux.cpp
- src/components/plugins/toolbar/QvToolbar_win.cpp
- src/components/plugins/QvPluginHost.cpp
- src/components/proxy/QvProxyConfigurator.cpp
- src/components/route/RouteSchemeIO.cpp
- src/components/speedchart/speedplotview.cpp
- src/components/speedchart/speedwidget.cpp
- src/components/darkmode/DarkmodeDetector.cpp
- src/components/update/UpdateChecker.cpp
- src/core/connection/ConnectionIO.cpp
- src/core/connection/Generation.cpp
- src/core/connection/Serialization.cpp
- src/core/connection/Serialization_ss.cpp
- src/core/connection/Serialization_ssd.cpp
- src/core/connection/Serialization_vmess.cpp
- src/core/CoreUtils.cpp
- src/core/handler/ConfigHandler.cpp
- src/core/handler/KernelInstanceHandler.cpp
- src/core/kernel/APIBackend.cpp
- src/core/kernel/V2rayKernelInteractions.cpp
- src/core/kernel/PluginKernelInteractions.cpp
- src/core/kernel/QvKernelABIChecker.cpp
- src/core/settings/SettingsBackend.cpp
- src/core/settings/SettingsUpgrade.cpp
- src/main.cpp
- src/ui/editors/w_InboundEditor.cpp
- src/ui/editors/w_JsonEditor.cpp
- src/ui/editors/w_OutboundEditor.cpp
- src/ui/editors/w_RoutesEditor.cpp
- src/ui/editors/w_RoutesEditor_extra.cpp
- src/ui/messaging/QvMessageBus.cpp
- src/ui/models/InboundNodeModel.cpp
- src/ui/models/OutboundNodeModel.cpp
- src/ui/models/RuleNodeModel.cpp
- src/ui/widgets/ConnectionInfoWidget.cpp
- src/ui/widgets/ConnectionItemWidget.cpp
- src/ui/widgets/QvAutoCompleteTextEdit.cpp
- src/ui/widgets/StreamSettingsWidget.cpp
- src/ui/widgets/RouteSettingsMatrix.cpp
- src/ui/w_ImportConfig.cpp
- src/ui/w_MainWindow.cpp
- src/ui/w_MainWindow_extra.cpp
- src/ui/w_PreferencesWindow.cpp
- src/ui/w_PluginManager.cpp
- src/ui/w_ScreenShot_Core.cpp
- src/ui/w_SubscriptionManager.cpp
- # ui files
- src/ui/w_SubscriptionManager.ui
- src/ui/editors/w_OutboundEditor.ui
- src/ui/editors/w_InboundEditor.ui
- src/ui/editors/w_JsonEditor.ui
- src/ui/editors/w_RoutesEditor.ui
- src/ui/w_ImportConfig.ui
- src/ui/widgets/StreamSettingsWidget.ui
- src/ui/widgets/ConnectionInfoWidget.ui
- src/ui/widgets/ConnectionItemWidget.ui
- src/ui/widgets/RouteSettingsMatrix.ui
- src/ui/w_MainWindow.ui
- src/ui/w_PreferencesWindow.ui
- src/ui/w_PluginManager.ui
- src/ui/w_ScreenShot_Core.ui
- # headers
- 3rdparty/libsemver/version.hpp
- src/base/JsonHelpers.hpp
- src/base/models/CoreObjectModels.hpp
- src/base/models/QvConfigIdentifier.hpp
- src/base/models/QvRuntimeConfig.hpp
- src/base/models/QvSafeType.hpp
- src/base/models/QvSettingsObject.hpp
- src/base/models/QvStartupConfig.hpp
- src/base/Qv2rayBase.hpp
- src/base/Qv2rayFeatures.hpp
- src/base/Qv2rayLog.hpp
- src/common/CommandArgs.hpp
- src/common/HTTPRequestHelper.hpp
- src/common/JsonHighlighter.hpp
- src/common/LogHighlighter.hpp
- src/common/QJsonModel.hpp
- src/common/QvHelpers.hpp
- src/common/QvTranslator.hpp
- src/common/QRCodeHelper.hpp
- src/components/autolaunch/QvAutoLaunch.hpp
- src/components/darkmode/DarkmodeDetector.hpp
- src/components/geosite/QvGeositeReader.hpp
- src/components/latency/win/ICMPPinger.hpp
- src/components/latency/QvTCPing.hpp
- src/components/plugins/toolbar/QvToolbar.hpp
- src/components/plugins/QvPluginHost.hpp
- src/components/proxy/QvProxyConfigurator.hpp
- src/components/route/RouteSchemeIO.hpp
- src/components/route/presets/RouteScheme_V2rayN.hpp
- src/components/speedchart/speedplotview.hpp
- src/components/speedchart/speedwidget.hpp
- src/components/update/UpdateChecker.hpp
- src/core/connection/ConnectionIO.hpp
- src/core/connection/Generation.hpp
- src/core/connection/Serialization.hpp
- src/core/CoreSafeTypes.hpp
- src/core/CoreUtils.hpp
- src/core/handler/ConfigHandler.hpp
- src/core/handler/KernelInstanceHandler.hpp
- src/core/kernel/APIBackend.hpp
- src/core/kernel/V2rayKernelInteractions.hpp
- src/core/kernel/PluginKernelInteractions.hpp
- src/core/kernel/QvKernelABIChecker.hpp
- src/core/settings/SettingsBackend.hpp
- src/ui/editors/w_InboundEditor.hpp
- src/ui/editors/w_JsonEditor.hpp
- src/ui/editors/w_OutboundEditor.hpp
- src/ui/editors/w_RoutesEditor.hpp
- src/ui/messaging/QvMessageBus.hpp
- src/ui/models/InboundNodeModel.hpp
- src/ui/models/NodeModelsBase.hpp
- src/ui/models/OutboundNodeModel.hpp
- src/ui/models/RuleNodeModel.hpp
- src/ui/widgets/ConnectionInfoWidget.hpp
- src/ui/widgets/ConnectionItemWidget.hpp
- src/ui/widgets/QvAutoCompleteTextEdit.hpp
- src/ui/widgets/StreamSettingsWidget.hpp
- src/ui/widgets/RouteSettingsMatrix.hpp
- src/ui/w_ImportConfig.hpp
- src/ui/w_MainWindow.hpp
- src/ui/w_PreferencesWindow.hpp
- src/ui/w_PluginManager.hpp
- src/ui/w_ScreenShot_Core.hpp
- src/ui/w_SubscriptionManager.hpp
- assets/qv2ray.rc
- )
-
-if(EMBED_TRANSLATIONS)
- add_definitions(-DEMBED_TRANSLATIONS)
- configure_file(translations/translations.qrc ${CMAKE_BINARY_DIR} COPYONLY)
- list(APPEND QV2RAY_SOURCES ${CMAKE_BINARY_DIR}/translations.qrc)
-endif()
-
-add_custom_target(lupdate
- COMMAND lupdate ${QV2RAY_SOURCES} -ts ${TRANSLATIONS_TS} -locations none
- WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
-
-set_target_properties(lupdate PROPERTIES EXCLUDE_FROM_ALL TRUE)
-
-set(QRC_RESOURCES ${CMAKE_SOURCE_DIR}/resources.qrc)
-
-set(QT_LIBRARY
- Qt5::Core
- Qt5::Gui
- Qt5::Widgets
- Qt5::Network
- )
-
-add_executable(${PROJECT_NAME}
- ${GUI_TYPE}
- ${QV2RAY_SOURCES}
- ${QNODEEDITOR_SOURCES}
- ${QNODEEDITOR_QRC_RESOURCES}
- ${SINGLEAPPLICATION_SOURCES}
- ${ZXING_SOURCES}
+ ${LIBSEMVER_SOURCES}
${PROTO_SRCS}
${PROTO_HDRS}
${API_GRPC_SRCS}
${API_PROTO_SRCS}
- ${QRC_RESOURCES}
- ${QM_FILES}
)
-target_link_libraries(${PROJECT_NAME}
- ${QT_LIBRARY}
+set(QV2RAY_FULL_SOURCES
+ src/main.cpp
+ assets/qv2ray.rc
+ ${QV2RAY_UI_FORMS}
+ ${QV2RAY_UI_SOURCES}
+ ${SINGLEAPPLICATION_SOURCES}
+ ${QNODEEDITOR_QRC_RESOURCES}
+ ${QV2RAY_EMBED_TRANSLATION_QRC}
+ ${QRC_RESOURCES}
+ ${QM_FILES})
+
+if(ANDROID)
+ add_library(qv2ray SHARED ${QV2RAY_FULL_SOURCES})
+ target_link_libraries(qv2ray -llog -landroid)
+else()
+ if (WIN32)
+ add_executable(qv2ray ${GUI_TYPE} ${QV2RAY_FULL_SOURCES})
+ else()
+ include(cmake/backward-cpp.cmake)
+ add_executable(qv2ray ${GUI_TYPE} ${QV2RAY_FULL_SOURCES})
+ add_backward(qv2ray)
+ endif()
+endif()
+
+
+target_link_libraries(qv2ray-baselib
${QV2RAY_PROTOBUF_LIBRARY}
${QV2RAY_BACKEND_LIBRARIES}
- ${QNODEEDITOR_LIBRARY}
${ZXING_LIBRARY}
Threads::Threads
+ ${QV2RAY_QT_LIBS}
)
+target_link_libraries(qv2ray
+ qv2ray-baselib
+ ${QNODEEDITOR_LIBRARY}
+ ${SINGLEAPPLICATION_LIBRARY}
+ )
-target_include_directories(${PROJECT_NAME} PRIVATE
+target_include_directories(qv2ray PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/src
${CMAKE_BINARY_DIR}
- ${QHTTPSERVER_DIR}
${QNODEEDITOR_INCLUDE_PATH}
- ${ZXING_INCLUDE_PATH}
${SINGLEAPPLICATION_DIR}
${Protobuf_INCLUDE_DIRS}
- ${cpp-httplib_INCLUDE_DIRS}
)
+target_include_directories(qv2ray-baselib PUBLIC
+ ${CMAKE_CURRENT_SOURCE_DIR}/src
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_BINARY_DIR}
+ ${ZXING_INCLUDE_PATH}
+ ${Protobuf_INCLUDE_DIRS}
+ )
+
+if (BUILD_TESTING)
+ include(CTest)
+ add_subdirectory(test)
+endif()
+
+# Qt language translations
+add_custom_target(lupdate
+ COMMAND lupdate ${QV2RAY_BASE_HEADERS}
+ ${QV2RAY_LIB_SOURCES}
+ ${QVPLUGIN_INTERFACE_HEADERS}
+ ${QV2RAY_UI_SOURCES}
+ ${QV2RAY_UI_FORMS}
+ ${QNODEEDITOR_QRC_RESOURCES}
+ ${SINGLEAPPLICATION_SOURCES} -ts ${TRANSLATIONS_TS} -locations none -noobsolete
+ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
+set_target_properties(lupdate PROPERTIES EXCLUDE_FROM_ALL TRUE)
+
if(APPLE)
find_package(Iconv REQUIRED)
find_library(CARBON NAMES Carbon)
find_library(COCOA NAMES Cocoa)
find_library(SECURITY NAMES Security)
- target_link_libraries(${PROJECT_NAME}
+ target_link_libraries(qv2ray
Iconv::Iconv
${CARBON}
${COCOA}
${SECURITY}
)
- target_include_directories(${PROJECT_NAME} PRIVATE
+ target_include_directories(qv2ray PRIVATE
${Iconv_INCLUDE_DIR}
)
@@ -373,11 +304,11 @@ if(APPLE)
MACOSX_PACKAGE_LOCATION Resources/lang
)
- target_sources(${PROJECT_NAME} PRIVATE
+ target_sources(qv2ray PRIVATE
${MACOSX_ICON}
)
- set_target_properties(${PROJECT_NAME}
+ set_target_properties(qv2ray
PROPERTIES
MACOSX_BUNDLE TRUE
MACOSX_BUNDLE_INFO_PLIST ${MACOSX_PLIST}
@@ -394,20 +325,22 @@ if(APPLE)
)
# Destination paths below are relative to ${CMAKE_INSTALL_PREFIX}
- install(TARGETS ${PROJECT_NAME}
+
+ install(TARGETS qv2ray
BUNDLE DESTINATION . COMPONENT Runtime
RUNTIME DESTINATION bin COMPONENT Runtime
)
-
- add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
- COMMAND macdeployqt ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.app
- )
- set(APPS "\${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}.app")
+ if(QV2RAY_AUTO_DEPLOY)
+ add_custom_command(TARGET qv2ray POST_BUILD
+ COMMAND ${Qt5_DIR}/../../../bin/macdeployqt ${CMAKE_BINARY_DIR}/qv2ray.app
+ )
+ endif()
+ set(APPS "\${CMAKE_INSTALL_PREFIX}/qv2ray.app")
include(cmake/deployment.cmake)
endif()
if(UNIX AND NOT APPLE AND NOT WIN32)
- install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION bin)
+ install(TARGETS qv2ray RUNTIME DESTINATION bin)
install(FILES assets/qv2ray.metainfo.xml DESTINATION share/metainfo)
install(FILES assets/qv2ray.desktop DESTINATION share/applications)
install(FILES assets/icons/qv2ray.png DESTINATION share/icons/hicolor/256x256/apps)
@@ -417,24 +350,18 @@ if(UNIX AND NOT APPLE AND NOT WIN32)
endif()
if(WIN32)
- target_link_libraries(${PROJECT_NAME} wininet wsock32 ws2_32 user32)
- install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION .)
+ find_package(OpenSSL REQUIRED)
+ target_link_libraries(qv2ray-baselib wininet wsock32 ws2_32 user32 Iphlpapi OpenSSL::SSL OpenSSL::Crypto Dbghelp)
+ install(TARGETS qv2ray RUNTIME DESTINATION .)
if(NOT EMBED_TRANSLATIONS)
install(FILES ${QM_FILES} DESTINATION lang)
endif()
-
- if(CMAKE_CL_64)
- install(FILES ${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/bin/libssl-1_1-x64.dll DESTINATION .)
- install(FILES ${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/bin/libcrypto-1_1-x64.dll DESTINATION .)
- else()
- install(FILES ${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/bin/libssl-1_1.dll DESTINATION .)
- install(FILES ${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/bin/libcrypto-1_1.dll DESTINATION .)
+ if(QV2RAY_AUTO_DEPLOY)
+ add_custom_command(TARGET qv2ray POST_BUILD
+ COMMAND ${Qt5_DIR}/../../../bin/windeployqt ${CMAKE_BINARY_DIR}/qv2ray.exe --compiler-runtime --verbose 2 --dir ${CMAKE_BINARY_DIR}/winqt/
+ )
endif()
-
- add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
- COMMAND windeployqt ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.exe --compiler-runtime --verbose 2 --dir ${CMAKE_BINARY_DIR}/winqt/
- )
install(DIRECTORY ${CMAKE_BINARY_DIR}/winqt/ DESTINATION .)
- set(APPS "\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}.exe")
+ set(APPS "\${CMAKE_INSTALL_PREFIX}/qv2ray.exe")
include(cmake/deployment.cmake)
endif()
diff --git a/_clang-format b/_clang-format
index 28c9f4b1..13878bbc 100644
--- a/_clang-format
+++ b/_clang-format
@@ -5,7 +5,7 @@ AlignAfterOpenBracket: Align
AllowAllArgumentsOnNextLine: false
AllowAllConstructorInitializersOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: false
-AllowShortBlocksOnASingleLine: false
+AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: true
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: Never
@@ -42,7 +42,7 @@ SpaceAfterTemplateKeyword: false
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpacesInParentheses: false
-Standard: Cpp11
+Standard: c++17
StatementMacros: [ Q_UNUSED LOG DEBUG ]
TabWidth: 4
UseTab: Never
diff --git a/assets/MacOSXBundleInfo.plist.in b/assets/MacOSXBundleInfo.plist.in
index e06b17ec..fb0c5edd 100644
--- a/assets/MacOSXBundleInfo.plist.in
+++ b/assets/MacOSXBundleInfo.plist.in
@@ -34,5 +34,20 @@
NSApplication
NSHighResolutionCapable
True
+ CFBundleURLTypes
+
+
+ CFBundleTypeRole
+ Editor
+ CFBundleURLIconFile
+ Icon
+ CFBundleURLName
+ com.qv2ray.qv2ray
+ CFBundleURLSchemes
+
+ qv2ray
+
+
+
diff --git a/assets/ProtocolSchemes.txt b/assets/ProtocolSchemes.txt
new file mode 100644
index 00000000..d5d79c4c
--- /dev/null
+++ b/assets/ProtocolSchemes.txt
@@ -0,0 +1,20 @@
+qv2ray://open/preference/general
+qv2ray://open/preference/kernel
+qv2ray://open/preference/inbound
+qv2ray://open/preference/connection
+qv2ray://open/preference/route
+qv2ray://open/preference/about
+
+qv2ray://open/group/connection
+qv2ray://open/group/subscription
+qv2ray://open/group/dns
+qv2ray://open/group/route
+
+qv2ray://open/import/link?name=
+qv2ray://open/import/qr?name=
+qv2ray://open/import/manual?name=
+qv2ray://open/import/advanced?name=
+
+qv2ray://open/plugin/plugindir
+qv2ray://open/plugin/metadata
+qv2ray://open/plugin/settings
diff --git a/QvRoute.schema.json b/assets/QvRoute.schema.json
similarity index 100%
rename from QvRoute.schema.json
rename to assets/QvRoute.schema.json
diff --git a/assets/credit.html b/assets/credit.html
index 63ed7c1d..ec52fa8e 100644
--- a/assets/credit.html
+++ b/assets/credit.html
@@ -1,21 +1,32 @@
-
-This program comes with ABSOLUTELY NO WARRANTY.
-This is free software, and you are welcome to redistribute it under certain conditions.
-Copyright (c) 2019-2020 Qv2ray Development Group.
-
-Libraries that have been used in Qv2ray are listed below (Sorted by date added):
-Copyright (c) 2020 xyz347 (xyz347): X2Struct (Apache)
-Copyright (c) 2011 SCHUTZ Sacha (@dridk): QJsonModel (MIT)
-Copyright (c) 2020 Nikolaos Ftylitakis (@ftylitak): QZXing (Apache2)
-Copyright (c) 2016 Singein (@Singein): ScreenShot (MIT)
-Copyright (c) 2020 Itay Grudev (@itay-grudev): SingleApplication (MIT)
-Copyright (c) 2020 paceholder (@paceholder): nodeeditor (Qv2ray group modified version) (BSD-3-Clause)
-Copyright (c) 2019 TheWanderingCoel (@TheWanderingCoel): ShadowClash (launchatlogin) (GPLv3)
-Copyright (c) 2020 DuckSoft (@DuckSoft): QvRPCBridge (WTFPL)
-Copyright (c) 2019 ShadowSocks (@shadowsocks): libQtShadowsocks (LGPLv3)
-Copyright (c) 2015-2020 qBittorrent (Anton Lashkov) (@qBittorrent): speedplotview (GPLv2)
-Copyright (c) 2020 Diffusions Nu-book Inc. (nu-book): zxing-cpp (Apache)
-
+
+
+
+
+
+
+
+
+ This program comes with ABSOLUTELY NO WARRANTY.
+ This is free software, and you are welcome to redistribute it under certain conditions.
+ Copyright (c) 2019-2020 Qv2ray Development Group.
+
+ Libraries that have been used in Qv2ray are listed below (Sorted by date added):
+ Copyright (c) 2011 SCHUTZ Sacha (@dridk): QJsonModel (MIT)
+ Copyright (c) 2016 Singein (@Singein): ScreenShot (MIT)
+ Copyright (c) 2020 Itay Grudev (@itay-grudev): SingleApplication (MIT)
+ Copyright (c) 2020 paceholder (@paceholder): nodeeditor (Qv2ray group modified version) (BSD-3-Clause)
+ Copyright (c) 2019 TheWanderingCoel (@TheWanderingCoel): ShadowClash (launchatlogin) (GPLv3)
+ Copyright (c) 2020 DuckSoft (@DuckSoft): QvRPCBridge (WTFPL)
+ Copyright (c) 2019 ShadowSocks (@shadowsocks): libQtShadowsocks (LGPLv3)
+ Copyright (c) 2015-2020 qBittorrent (Anton Lashkov) (@qBittorrent): speedplotview (GPLv2)
+ Copyright (c) 2020 Diffusions Nu-book Inc. (nu-book): zxing-cpp (Apache)
+ Copyright (c) 2020 feiyangqingyun: QWidgetDemo (Mulan PSL v1)
+
+
+
\ No newline at end of file
diff --git a/assets/icons/Applogo_bird_b.png b/assets/icons/Applogo_bird_b.png
new file mode 100644
index 00000000..0e266d5c
Binary files /dev/null and b/assets/icons/Applogo_bird_b.png differ
diff --git a/assets/icons/Applogo_bird_colorful.png b/assets/icons/Applogo_bird_colorful.png
new file mode 100644
index 00000000..3c07bf50
Binary files /dev/null and b/assets/icons/Applogo_bird_colorful.png differ
diff --git a/assets/icons/Applogo_bird_g.png b/assets/icons/Applogo_bird_g.png
new file mode 100644
index 00000000..861bd989
Binary files /dev/null and b/assets/icons/Applogo_bird_g.png differ
diff --git a/assets/icons/designs/Applogo_Bird_b.svg b/assets/icons/designs/Applogo_Bird_b.svg
new file mode 100644
index 00000000..19ef1386
--- /dev/null
+++ b/assets/icons/designs/Applogo_Bird_b.svg
@@ -0,0 +1,237 @@
+
+
diff --git a/assets/icons/designs/Applogo_Bird_colorful.svg b/assets/icons/designs/Applogo_Bird_colorful.svg
new file mode 100644
index 00000000..51164118
--- /dev/null
+++ b/assets/icons/designs/Applogo_Bird_colorful.svg
@@ -0,0 +1,237 @@
+
+
diff --git a/assets/package_dmg.json.in b/assets/package_dmg.json.in
index fb1b9dc8..809eb31f 100644
--- a/assets/package_dmg.json.in
+++ b/assets/package_dmg.json.in
@@ -11,7 +11,7 @@
}
},
"contents": [
- { "x": 130, "y": 205, "type": "file", "path": "@CMAKE_BINARY_DIR@/qv2ray.app" },
+ { "x": 130, "y": 205, "type": "file", "path": "@CMAKE_INSTALL_PREFIX@/qv2ray.app" },
{ "x": 375, "y": 205, "type": "link", "path": "/Applications" }
]
}
diff --git a/qv2ray-config-models.qmodel b/assets/qv2ray-config-models.qmodel
similarity index 100%
rename from qv2ray-config-models.qmodel
rename to assets/qv2ray-config-models.qmodel
diff --git a/qv2ray-new.qmodel b/assets/qv2ray-new.qmodel
similarity index 100%
rename from qv2ray-new.qmodel
rename to assets/qv2ray-new.qmodel
diff --git a/assets/qv2ray.desktop b/assets/qv2ray.desktop
index fe95a5fa..6a6a4369 100644
--- a/assets/qv2ray.desktop
+++ b/assets/qv2ray.desktop
@@ -2,25 +2,42 @@
Version=1.0
Name=Qv2ray
Type=Application
-GenericName=V2ray Graphical Frontend
-GenericName[zh_CN]=V2ray 图形前端
-Comment=A Cross-platform v2ray Qt GUI Client
-Comment[zh_CN]=跨平台的 v2ray Qt 客户端
+GenericName=V2Ray Graphical Frontend
+GenericName[zh_CN]=V2Ray 图形前端
+GenericName[ja]=V2Rayグラフィックフロントエンド
+Comment=A Cross-Platform V2Ray Qt GUI Client
+Comment[zh_CN]=跨平台的 V2Ray Qt 图形客户端
+Comment[ja]=とあるクロスプラットフォームV2Rayグラフィックフロントエンド
Keywords=Internet;VPN;Proxy;v2ray;Qt;qv;
-Keywords[zh_CN]=Internet;VPN;Proxy;v2ray;Qt;qv;代理;翻墙;梯子;
+Keywords[zh_CN]=Internet;VPN;Proxy;v2ray;Qt;qv;代理;
+Keywords[ja]=インターネット;VPN;プロキシ;Qt;v2ray;qv;
Categories=Network;Qt;
Terminal=false
Icon=qv2ray
-Exec=qv2ray
-Actions=noAPI;withToolbarPlugin;
+Exec=qv2ray %u
+MimeType=x-scheme-handler/qv2ray;
+Actions=debug;noPlugin;noScaleFactor;noAPI;
+
+[Desktop Action debug]
+Name=Start with Debug Mode
+Name[zh_CN]=以调试模式运行
+Name[ja]=デバッグモードで起動
+Exec=qv2ray --debug
+
+[Desktop Action noPlugin]
+Name=Start without Plugins
+Name[zh_CN]=不使用插件启动
+Name[ja]=プラグイン使用せずに起動
+Exec=qv2ray --noPlugin
+
+[Desktop Action noScaleFactor]
+Name=Start without Scaling Factor
+Name[zh_CN]=禁用界面缩放启动
+Name[ja]=インターフェーススケーリング使用せずに起動
+Exec=qv2ray --noScaleFactor
[Desktop Action noAPI]
Name=Start without gRPC API
Name[zh_CN]=不使用 gRPC API 启动
+Name[ja]=gRPC使用せずに起動
Exec=qv2ray --noAPI
-
-[Desktop Action withToolbarPlugin]
-Name=Start with Toolbar Plugin
-Name[zh_CN]=使用工具栏插件启动
-Exec=qv2ray --withToolbarPlugin
-
diff --git a/assets/qv2ray.metainfo.xml b/assets/qv2ray.metainfo.xml
index 033474e2..31962225 100644
--- a/assets/qv2ray.metainfo.xml
+++ b/assets/qv2ray.metainfo.xml
@@ -1,8 +1,8 @@
com.github.Qv2ray
- CC0-1.0
- GPL-3.0
+ GPL-3.0+
+ GPL-3.0+
Qv2ray
Qv2ray is a cross-platform v2ray graphical frontend written in Qt.
diff --git a/qv2ray.qmodel b/assets/qv2ray.qmodel
similarity index 100%
rename from qv2ray.qmodel
rename to assets/qv2ray.qmodel
diff --git a/assets/qv2ray.rc b/assets/qv2ray.rc
index 93479262..5e5ca991 100644
--- a/assets/qv2ray.rc
+++ b/assets/qv2ray.rc
@@ -31,7 +31,7 @@ VS_VERSION_INFO VERSIONINFO
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "Qv2ray Workgroup\0"
- VALUE "FileDescription", "Qv2ray, a cross-platform v2ray GUI client.\0"
+ VALUE "FileDescription", "Qv2ray\0"
VALUE "FileVersion", VER_FILEVERSION_STR
VALUE "LegalCopyright", "Qv2ray is being distributed under GPLv3\0"
VALUE "OriginalFilename", "qv2ray.exe\0"
diff --git a/tools/v2ray_api.proto b/assets/v2ray_api.proto
similarity index 100%
rename from tools/v2ray_api.proto
rename to assets/v2ray_api.proto
diff --git a/tools/v2ray_geosite.proto b/assets/v2ray_geosite.proto
similarity index 100%
rename from tools/v2ray_geosite.proto
rename to assets/v2ray_geosite.proto
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 66ce01c5..80ddadf9 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -15,21 +15,25 @@ pool:
steps:
- checkout: self
submodules: recursive
+- task: NodeTool@0
+ inputs:
+ versionSpec: '12.x'
- script: |
- brew install protobuf grpc ninja qt5
+ sudo xcode-select -s /Applications/Xcode_10.3.app
+ brew install protobuf grpc ninja qt5 pkg-config
+ npm install -g appdmg
displayName: Prepare dependencies
- script: |
PATH=/usr/local/opt/qt5/bin:$PATH
mkdir build
cd build
- cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_DEPLOYMENT_TARGET=10.14
- sudo cmake --build . --target package --parallel $(sysctl -n hw.logicalcpu)
+ cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_DEPLOYMENT_TARGET=10.13
+ cmake --build .
+ sudo cmake --install .
+ appdmg ../assets/package_dmg.json ../qv2ray-legacy.dmg
displayName: Build Qv2ray
env:
Qt5_DIR: /usr/local/opt/qt5/lib/cmake/Qt5
-- script: |
- cp build/qv2ray-*.dmg ./qv2ray-legacy.dmg
- displayName: Copy binary
- task: PublishBuildArtifacts@1
inputs:
PathtoPublish: 'qv2ray-legacy.dmg'
diff --git a/cmake/backend.cmake b/cmake/backend.cmake
index fb25d91f..d367dd14 100644
--- a/cmake/backend.cmake
+++ b/cmake/backend.cmake
@@ -11,13 +11,14 @@ if(NOT USE_LIBQVB)
set(QV2RAY_BACKEND_LIBRARIES ${GRPC_LIBRARIES})
else()
find_library(UPB_LIBRARY NAMES upb)
+ find_library(ADDRESS_SORTING NAMES address_sorting)
pkg_check_modules(GRPC REQUIRED grpc++ grpc gpr)
- set(QV2RAY_BACKEND_LIBRARIES ${GRPC_LINK_LIBRARIES} ${UPB_LIBRARY})
+ set(QV2RAY_BACKEND_LIBRARIES ${GRPC_LINK_LIBRARIES} ${UPB_LIBRARY} ${ADDRESS_SORTING})
endif()
endif()
- set(API_PROTO "${CMAKE_SOURCE_DIR}/tools/v2ray_api.proto")
- set(API_PROTO_PATH "${CMAKE_SOURCE_DIR}/tools")
+ set(API_PROTO "${CMAKE_SOURCE_DIR}/assets/v2ray_api.proto")
+ set(API_PROTO_PATH "${CMAKE_SOURCE_DIR}/assets")
set(API_PROTO_SRCS "${CMAKE_CURRENT_BINARY_DIR}/v2ray_api.pb.cc")
set(API_PROTO_HDRS "${CMAKE_CURRENT_BINARY_DIR}/v2ray_api.pb.h")
set(API_GRPC_SRCS "${CMAKE_CURRENT_BINARY_DIR}/v2ray_api.grpc.pb.cc")
@@ -26,12 +27,12 @@ if(NOT USE_LIBQVB)
OUTPUT "${API_GRPC_SRCS}" "${API_GRPC_HDRS}" "${API_PROTO_HDRS}" "${API_PROTO_SRCS}"
COMMAND ${Protobuf_PROTOC_EXECUTABLE}
ARGS --grpc_out "${CMAKE_CURRENT_BINARY_DIR}"
- --cpp_out "${CMAKE_CURRENT_BINARY_DIR}"
- -I "${API_PROTO_PATH}"
- --plugin=protoc-gen-grpc="${GRPC_CPP_PLUGIN}"
- "${API_PROTO}"
+ --cpp_out "${CMAKE_CURRENT_BINARY_DIR}"
+ -I "${API_PROTO_PATH}"
+ --plugin=protoc-gen-grpc="${GRPC_CPP_PLUGIN}"
+ "${API_PROTO}"
DEPENDS "${API_PROTO}"
- )
+ )
else()
add_definitions(-DBACKEND_LIBQVB)
if(UNIX AND NOT APPLE)
diff --git a/cmake/backward-cpp.cmake b/cmake/backward-cpp.cmake
new file mode 100644
index 00000000..58432b00
--- /dev/null
+++ b/cmake/backward-cpp.cmake
@@ -0,0 +1,46 @@
+include(${CMAKE_SOURCE_DIR}/3rdparty/backward-cpp/BackwardConfig.cmake)
+
+# check if compiler is nvcc or nvcc_wrapper
+set(COMPILER_IS_NVCC false)
+get_filename_component(COMPILER_NAME ${CMAKE_CXX_COMPILER} NAME)
+if (COMPILER_NAME MATCHES "^nvcc")
+ set(COMPILER_IS_NVCC true)
+endif()
+
+if (DEFINED ENV{OMPI_CXX} OR DEFINED ENV{MPICH_CXX})
+ if ( ($ENV{OMPI_CXX} MATCHES "nvcc") OR ($ENV{MPICH_CXX} MATCHES "nvcc") )
+ set(COMPILER_IS_NVCC true)
+ endif()
+endif()
+
+# set CXX standard
+set(CMAKE_CXX_STANDARD_REQUIRED True)
+if (${COMPILER_IS_NVCC})
+ # GNU CXX extensions are not supported by nvcc
+ set(CMAKE_CXX_EXTENSIONS OFF)
+endif()
+
+###############################################################################
+# COMPILER FLAGS
+###############################################################################
+
+if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_COMPILER_IS_GNUCXX)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -g")
+endif()
+
+###############################################################################
+# BACKWARD OBJECT
+###############################################################################
+
+add_library(backward_object OBJECT ${CMAKE_SOURCE_DIR}/3rdparty/backward-cpp/backward.cpp)
+target_compile_definitions(backward_object PRIVATE ${BACKWARD_DEFINITIONS})
+target_include_directories(backward_object PRIVATE ${BACKWARD_INCLUDE_DIRS})
+set(BACKWARD_ENABLE $ CACHE STRING
+ "Link with this object to setup backward automatically")
+
+###############################################################################
+# BACKWARD LIBRARY (Includes backward.cpp)
+###############################################################################
+add_library(backward ${CMAKE_SOURCE_DIR}/3rdparty/backward-cpp/backward.cpp)
+target_compile_definitions(backward PUBLIC ${BACKWARD_DEFINITIONS})
+target_include_directories(backward PUBLIC ${BACKWARD_INCLUDE_DIRS})
diff --git a/cmake/components/qv2ray-lib.cmake b/cmake/components/qv2ray-lib.cmake
new file mode 100644
index 00000000..5650901a
--- /dev/null
+++ b/cmake/components/qv2ray-lib.cmake
@@ -0,0 +1,109 @@
+set(QV2RAY_BASE_HEADERS
+ ${CMAKE_SOURCE_DIR}/src/base/JsonHelpers.hpp
+ ${CMAKE_SOURCE_DIR}/src/base/models/CoreObjectModels.hpp
+ ${CMAKE_SOURCE_DIR}/src/base/models/QvConfigIdentifier.hpp
+ ${CMAKE_SOURCE_DIR}/src/base/models/QvRuntimeConfig.hpp
+ ${CMAKE_SOURCE_DIR}/src/base/models/QvSafeType.hpp
+ ${CMAKE_SOURCE_DIR}/src/base/models/QvCoreSettings.hpp
+ ${CMAKE_SOURCE_DIR}/src/base/models/QvSettingsObject.hpp
+ ${CMAKE_SOURCE_DIR}/src/base/models/QvStartupConfig.hpp
+ ${CMAKE_SOURCE_DIR}/src/base/Qv2rayBase.hpp
+ ${CMAKE_SOURCE_DIR}/src/base/Qv2rayFeatures.hpp
+ ${CMAKE_SOURCE_DIR}/src/base/Qv2rayLog.hpp
+ )
+
+set(QV2RAY_LIB_SOURCES
+ # headers
+ ${CMAKE_SOURCE_DIR}/src/base/Qv2rayLog.cpp
+ ${CMAKE_SOURCE_DIR}/src/common/HTTPRequestHelper.cpp
+ ${CMAKE_SOURCE_DIR}/src/common/HTTPRequestHelper.hpp
+ ${CMAKE_SOURCE_DIR}/src/common/QJsonModel.cpp
+ ${CMAKE_SOURCE_DIR}/src/common/QJsonModel.hpp
+ ${CMAKE_SOURCE_DIR}/src/common/QvHelpers.cpp
+ ${CMAKE_SOURCE_DIR}/src/common/QvHelpers.hpp
+ ${CMAKE_SOURCE_DIR}/src/common/QvTranslator.cpp
+ ${CMAKE_SOURCE_DIR}/src/common/QvTranslator.hpp
+ # Components
+ ${CMAKE_SOURCE_DIR}/src/components/autolaunch/QvAutoLaunch.cpp
+ ${CMAKE_SOURCE_DIR}/src/components/autolaunch/QvAutoLaunch.hpp
+ #
+ ${CMAKE_SOURCE_DIR}/src/components/geosite/QvGeositeReader.cpp
+ ${CMAKE_SOURCE_DIR}/src/components/geosite/QvGeositeReader.hpp
+ #
+ ${CMAKE_SOURCE_DIR}/src/components/latency/LatencyTest.cpp
+ ${CMAKE_SOURCE_DIR}/src/components/latency/LatencyTest.hpp
+ ${CMAKE_SOURCE_DIR}/src/components/latency/LatencyTestThread.cpp
+ ${CMAKE_SOURCE_DIR}/src/components/latency/LatencyTestThread.hpp
+ ${CMAKE_SOURCE_DIR}/src/components/latency/TCPing.cpp
+ ${CMAKE_SOURCE_DIR}/src/components/latency/TCPing.hpp
+ ${CMAKE_SOURCE_DIR}/src/components/latency/win/ICMPPing.cpp
+ ${CMAKE_SOURCE_DIR}/src/components/latency/win/ICMPPing.hpp
+ ${CMAKE_SOURCE_DIR}/src/components/latency/unix/ICMPPing.cpp
+ ${CMAKE_SOURCE_DIR}/src/components/latency/unix/ICMPPing.hpp
+ #
+ ${CMAKE_SOURCE_DIR}/src/components/ntp/QvNTPClient.cpp
+ ${CMAKE_SOURCE_DIR}/src/components/ntp/QvNTPClient.hpp
+ #
+ ${CMAKE_SOURCE_DIR}/src/components/plugins/QvPluginHost.cpp
+ ${CMAKE_SOURCE_DIR}/src/components/plugins/QvPluginHost.hpp
+ #
+ ${CMAKE_SOURCE_DIR}/src/components/port/QvPortDetector.cpp
+ ${CMAKE_SOURCE_DIR}/src/components/port/QvPortDetector.hpp
+ #
+ ${CMAKE_SOURCE_DIR}/src/components/proxy/QvProxyConfigurator.cpp
+ ${CMAKE_SOURCE_DIR}/src/components/proxy/QvProxyConfigurator.hpp
+ #
+ ${CMAKE_SOURCE_DIR}/src/components/route/presets/RouteScheme_V2rayN.hpp
+ ${CMAKE_SOURCE_DIR}/src/components/route/RouteSchemeIO.cpp
+ ${CMAKE_SOURCE_DIR}/src/components/route/RouteSchemeIO.hpp
+ #
+ ${CMAKE_SOURCE_DIR}/src/components/update/UpdateChecker.cpp
+ ${CMAKE_SOURCE_DIR}/src/components/update/UpdateChecker.hpp
+ #
+ ${CMAKE_SOURCE_DIR}/src/components/darkmode/DarkmodeDetector.cpp
+ ${CMAKE_SOURCE_DIR}/src/components/darkmode/DarkmodeDetector.hpp
+ #
+ ${CMAKE_SOURCE_DIR}/src/components/speedchart/speedplotview.cpp
+ ${CMAKE_SOURCE_DIR}/src/components/speedchart/speedplotview.hpp
+ ${CMAKE_SOURCE_DIR}/src/components/speedchart/speedwidget.cpp
+ ${CMAKE_SOURCE_DIR}/src/components/speedchart/speedwidget.hpp
+ #
+ ${CMAKE_SOURCE_DIR}/src/core/connection/ConnectionIO.cpp
+ ${CMAKE_SOURCE_DIR}/src/core/connection/ConnectionIO.hpp
+ ${CMAKE_SOURCE_DIR}/src/core/connection/Generation.hpp
+ ${CMAKE_SOURCE_DIR}/src/core/connection/generation/final.cpp
+ ${CMAKE_SOURCE_DIR}/src/core/connection/generation/inbounds.cpp
+ ${CMAKE_SOURCE_DIR}/src/core/connection/generation/outbounds.cpp
+ ${CMAKE_SOURCE_DIR}/src/core/connection/generation/filters.cpp
+ ${CMAKE_SOURCE_DIR}/src/core/connection/generation/routing.cpp
+ ${CMAKE_SOURCE_DIR}/src/core/connection/generation/misc.cpp
+ ${CMAKE_SOURCE_DIR}/src/core/connection/Serialization.cpp
+ ${CMAKE_SOURCE_DIR}/src/core/connection/Serialization.hpp
+ ${CMAKE_SOURCE_DIR}/src/core/connection/serialization/ss.cpp
+ ${CMAKE_SOURCE_DIR}/src/core/connection/serialization/ssd.cpp
+ ${CMAKE_SOURCE_DIR}/src/core/connection/serialization/vmess.cpp
+ ${CMAKE_SOURCE_DIR}/src/core/connection/serialization/vmess_new.cpp
+ #
+ ${CMAKE_SOURCE_DIR}/src/core/CoreUtils.cpp
+ ${CMAKE_SOURCE_DIR}/src/core/CoreUtils.hpp
+ #
+ ${CMAKE_SOURCE_DIR}/src/core/handler/ConfigHandler.cpp
+ ${CMAKE_SOURCE_DIR}/src/core/handler/ConfigHandler.hpp
+ ${CMAKE_SOURCE_DIR}/src/core/handler/KernelInstanceHandler.cpp
+ ${CMAKE_SOURCE_DIR}/src/core/handler/KernelInstanceHandler.hpp
+ ${CMAKE_SOURCE_DIR}/src/core/handler/RouteHandler.cpp
+ ${CMAKE_SOURCE_DIR}/src/core/handler/RouteHandler.hpp
+ #
+ ${CMAKE_SOURCE_DIR}/src/core/kernel/APIBackend.cpp
+ ${CMAKE_SOURCE_DIR}/src/core/kernel/APIBackend.hpp
+ ${CMAKE_SOURCE_DIR}/src/core/kernel/PluginKernelInteractions.cpp
+ ${CMAKE_SOURCE_DIR}/src/core/kernel/PluginKernelInteractions.hpp
+ ${CMAKE_SOURCE_DIR}/src/core/kernel/QvKernelABIChecker.cpp
+ ${CMAKE_SOURCE_DIR}/src/core/kernel/QvKernelABIChecker.hpp
+ ${CMAKE_SOURCE_DIR}/src/core/kernel/V2rayKernelInteractions.cpp
+ ${CMAKE_SOURCE_DIR}/src/core/kernel/V2rayKernelInteractions.hpp
+ #
+ ${CMAKE_SOURCE_DIR}/src/core/settings/SettingsBackend.cpp
+ ${CMAKE_SOURCE_DIR}/src/core/settings/SettingsBackend.hpp
+ ${CMAKE_SOURCE_DIR}/src/core/settings/SettingsUpgrade.cpp
+ )
diff --git a/cmake/components/qv2ray-ui.cmake b/cmake/components/qv2ray-ui.cmake
new file mode 100644
index 00000000..0916a8f9
--- /dev/null
+++ b/cmake/components/qv2ray-ui.cmake
@@ -0,0 +1,93 @@
+set(QV2RAY_UI_FORMS
+ ${CMAKE_SOURCE_DIR}/src/ui/editors/w_OutboundEditor.ui
+ ${CMAKE_SOURCE_DIR}/src/ui/editors/w_InboundEditor.ui
+ ${CMAKE_SOURCE_DIR}/src/ui/editors/w_JsonEditor.ui
+ ${CMAKE_SOURCE_DIR}/src/ui/editors/w_RoutesEditor.ui
+ #
+ ${CMAKE_SOURCE_DIR}/src/ui/widgets/StreamSettingsWidget.ui
+ ${CMAKE_SOURCE_DIR}/src/ui/widgets/ConnectionInfoWidget.ui
+ ${CMAKE_SOURCE_DIR}/src/ui/widgets/ConnectionItemWidget.ui
+ ${CMAKE_SOURCE_DIR}/src/ui/widgets/RouteSettingsMatrix.ui
+ ${CMAKE_SOURCE_DIR}/src/ui/widgets/InboundSettingsWidget.ui
+ ${CMAKE_SOURCE_DIR}/src/ui/widgets/ConnectionSettingsWidget.ui
+ ${CMAKE_SOURCE_DIR}/src/ui/widgets/DnsSettingsWidget.ui
+ #
+ ${CMAKE_SOURCE_DIR}/src/ui/windows/w_GroupManager.ui
+ ${CMAKE_SOURCE_DIR}/src/ui/windows/w_ImportConfig.ui
+ ${CMAKE_SOURCE_DIR}/src/ui/windows/w_MainWindow.ui
+ ${CMAKE_SOURCE_DIR}/src/ui/windows/w_PreferencesWindow.ui
+ ${CMAKE_SOURCE_DIR}/src/ui/windows/w_PluginManager.ui
+ ${CMAKE_SOURCE_DIR}/src/ui/windows/w_ScreenShot_Core.ui
+ )
+
+set(QV2RAY_UI_SOURCES
+ # Qv2ray Application
+ ${CMAKE_SOURCE_DIR}/src/StackTraceHelper.hpp
+ ${CMAKE_SOURCE_DIR}/src/StackTraceHelper.cpp
+ ${CMAKE_SOURCE_DIR}/src/Qv2rayApplication.cpp
+ ${CMAKE_SOURCE_DIR}/src/Qv2rayApplication.hpp
+ # Common Utils
+ ${CMAKE_SOURCE_DIR}/src/ui/common/QvDialog.hpp
+ ${CMAKE_SOURCE_DIR}/src/ui/common/QRCodeHelper.cpp
+ ${CMAKE_SOURCE_DIR}/src/ui/common/QRCodeHelper.hpp
+ ${CMAKE_SOURCE_DIR}/src/ui/common/UIBase.hpp
+ ${CMAKE_SOURCE_DIR}/src/ui/common/JsonHighlighter.hpp
+ ${CMAKE_SOURCE_DIR}/src/ui/common/JsonHighlighter.cpp
+ ${CMAKE_SOURCE_DIR}/src/ui/common/LogHighlighter.hpp
+ ${CMAKE_SOURCE_DIR}/src/ui/common/LogHighlighter.cpp
+ # Message bus
+ ${CMAKE_SOURCE_DIR}/src/ui/messaging/QvMessageBus.hpp
+ ${CMAKE_SOURCE_DIR}/src/ui/messaging/QvMessageBus.cpp
+ # NodeEditor Models
+ ${CMAKE_SOURCE_DIR}/src/ui/models/NodeModelsBase.hpp
+ ${CMAKE_SOURCE_DIR}/src/ui/models/InboundNodeModel.hpp
+ ${CMAKE_SOURCE_DIR}/src/ui/models/InboundNodeModel.cpp
+ ${CMAKE_SOURCE_DIR}/src/ui/models/OutboundNodeModel.hpp
+ ${CMAKE_SOURCE_DIR}/src/ui/models/OutboundNodeModel.cpp
+ ${CMAKE_SOURCE_DIR}/src/ui/models/RuleNodeModel.hpp
+ ${CMAKE_SOURCE_DIR}/src/ui/models/RuleNodeModel.cpp
+ # Style Manager
+ ${CMAKE_SOURCE_DIR}/src/ui/styles/StyleManager.cpp
+ ${CMAKE_SOURCE_DIR}/src/ui/styles/StyleManager.hpp
+ # UI Widgets
+ ${CMAKE_SOURCE_DIR}/src/ui/widgets/ConnectionInfoWidget.hpp
+ ${CMAKE_SOURCE_DIR}/src/ui/widgets/ConnectionInfoWidget.cpp
+ ${CMAKE_SOURCE_DIR}/src/ui/widgets/QvAutoCompleteTextEdit.hpp
+ ${CMAKE_SOURCE_DIR}/src/ui/widgets/QvAutoCompleteTextEdit.cpp
+ ${CMAKE_SOURCE_DIR}/src/ui/widgets/RouteSettingsMatrix.hpp
+ ${CMAKE_SOURCE_DIR}/src/ui/widgets/RouteSettingsMatrix.cpp
+ ${CMAKE_SOURCE_DIR}/src/ui/widgets/ConnectionSettingsWidget.hpp
+ ${CMAKE_SOURCE_DIR}/src/ui/widgets/ConnectionSettingsWidget.cpp
+ ${CMAKE_SOURCE_DIR}/src/ui/widgets/ConnectionItemWidget.hpp
+ ${CMAKE_SOURCE_DIR}/src/ui/widgets/ConnectionItemWidget.cpp
+ ${CMAKE_SOURCE_DIR}/src/ui/widgets/StreamSettingsWidget.hpp
+ ${CMAKE_SOURCE_DIR}/src/ui/widgets/StreamSettingsWidget.cpp
+ ${CMAKE_SOURCE_DIR}/src/ui/widgets/InboundSettingsWidget.cpp
+ ${CMAKE_SOURCE_DIR}/src/ui/widgets/InboundSettingsWidget.hpp
+ ${CMAKE_SOURCE_DIR}/src/ui/widgets/DnsSettingsWidget.cpp
+ ${CMAKE_SOURCE_DIR}/src/ui/widgets/DnsSettingsWidget.hpp
+ # Editors
+ ${CMAKE_SOURCE_DIR}/src/ui/editors/w_InboundEditor.cpp
+ ${CMAKE_SOURCE_DIR}/src/ui/editors/w_InboundEditor.hpp
+ ${CMAKE_SOURCE_DIR}/src/ui/editors/w_JsonEditor.cpp
+ ${CMAKE_SOURCE_DIR}/src/ui/editors/w_JsonEditor.hpp
+ ${CMAKE_SOURCE_DIR}/src/ui/editors/w_OutboundEditor.cpp
+ ${CMAKE_SOURCE_DIR}/src/ui/editors/w_OutboundEditor.hpp
+ ${CMAKE_SOURCE_DIR}/src/ui/editors/w_RoutesEditor.hpp
+ ${CMAKE_SOURCE_DIR}/src/ui/editors/w_RoutesEditor.cpp
+ ${CMAKE_SOURCE_DIR}/src/ui/editors/w_RoutesEditor_extra.cpp
+ # Windows
+ ${CMAKE_SOURCE_DIR}/src/ui/windows/w_ImportConfig.hpp
+ ${CMAKE_SOURCE_DIR}/src/ui/windows/w_ImportConfig.cpp
+ ${CMAKE_SOURCE_DIR}/src/ui/windows/w_MainWindow.hpp
+ ${CMAKE_SOURCE_DIR}/src/ui/windows/w_MainWindow.cpp
+ ${CMAKE_SOURCE_DIR}/src/ui/windows/w_MainWindow_extra.cpp
+ ${CMAKE_SOURCE_DIR}/src/ui/windows/w_PreferencesWindow.hpp
+ ${CMAKE_SOURCE_DIR}/src/ui/windows/w_PreferencesWindow.cpp
+ ${CMAKE_SOURCE_DIR}/src/ui/windows/w_PluginManager.hpp
+ ${CMAKE_SOURCE_DIR}/src/ui/windows/w_PluginManager.cpp
+ ${CMAKE_SOURCE_DIR}/src/ui/windows/w_ScreenShot_Core.hpp
+ ${CMAKE_SOURCE_DIR}/src/ui/windows/w_ScreenShot_Core.cpp
+ ${CMAKE_SOURCE_DIR}/src/ui/windows/w_GroupManager.hpp
+ ${CMAKE_SOURCE_DIR}/src/ui/windows/w_GroupManager.cpp
+ )
diff --git a/cmake/cpp-httplib.cmake b/cmake/cpp-httplib.cmake
deleted file mode 100644
index e09fe015..00000000
--- a/cmake/cpp-httplib.cmake
+++ /dev/null
@@ -1 +0,0 @@
-set(cpp-httplib_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/3rdparty/cpp-httplib)
diff --git a/cmake/deployment.cmake b/cmake/deployment.cmake
index 3d3ab105..f75afc80 100644
--- a/cmake/deployment.cmake
+++ b/cmake/deployment.cmake
@@ -1,33 +1,3 @@
-# Directories to look for dependencies
-set(DIRS "${CMAKE_BINARY_DIR}")
-
-# Path used for searching by FIND_XXX(), with appropriate suffixes added
-if(CMAKE_PREFIX_PATH)
- foreach(dir ${CMAKE_PREFIX_PATH})
- list(APPEND DIRS "${dir}/bin" "${dir}/lib")
- endforeach()
-endif()
-
-# Append Qt's lib folder which is two levels above Qt5Widgets_DIR
-list(APPEND DIRS "${Qt5Widgets_DIR}/../..")
-list(APPEND DIRS "/usr/local/lib")
-list(APPEND DIRS "/usr/lib")
-
-if(MSVC)
- if(NOT BUILD_NSIS)
- set(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION .)
- endif()
-endif()
-
-include(InstallRequiredSystemLibraries)
-
-message(STATUS "APPS: ${APPS}")
-message(STATUS "QT_PLUGINS: ${QT_PLUGINS}")
-message(STATUS "DIRS: ${DIRS}")
-
-install(CODE "include(BundleUtilities)
- fixup_bundle(\"${APPS}\" \"${QT_PLUGINS}\" \"${DIRS}\")")
-
# Packaging
set(CPACK_PACKAGE_VENDOR "Qv2ray Development Group")
set(CPACK_PACKAGE_VERSION ${QV2RAY_VERSION_STRING})
@@ -45,6 +15,11 @@ if(MSVC)
set(CPACK_NSIS_MUI_UNIICON "${CMAKE_SOURCE_DIR}/assets/icons/qv2ray.ico")
set(CPACK_NSIS_DISPLAY_NAME "Qv2ray")
set(CPACK_NSIS_PACKAGE_NAME "Qv2ray")
+ set(CPACK_NSIS_EXTRA_PREINSTALL_COMMANDS "
+ ExecWait \\\"taskkill /f /im qv2ray.exe\\\"
+ ExecWait \\\"taskkill /f /im v2ray.exe\\\"
+ ExecWait \\\"taskkill /f /im wv2ray.exe\\\"
+ ")
set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "
CreateShortCut \\\"$DESKTOP\\\\Qv2ray.lnk\\\" \\\"$INSTDIR\\\\qv2ray.exe\\\"
CreateDirectory \\\"$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Qv2ray\\\"
@@ -56,6 +31,7 @@ if(MSVC)
WriteRegStr HKLM \\\"Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Uninstall\\\\qv2ray\\\" \\\"URLInfoAbout\\\" \\\"https://github.com/Qv2ray/Qv2ray\\\"
")
set(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS "
+ ExecWait \\\"taskkill /f /im qv2ray.exe\\\"
Delete \\\"$DESKTOP\\\\Qv2ray.lnk\\\"
Delete \\\"$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Qv2ray\\\\Qv2ray.lnk\\\"
RMDir \\\"$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Qv2ray\\\"
@@ -78,3 +54,33 @@ if(APPLE)
endif()
include(CPack)
+
+# Directories to look for dependencies
+set(DIRS "${CMAKE_BINARY_DIR}")
+
+# Path used for searching by FIND_XXX(), with appropriate suffixes added
+if(CMAKE_PREFIX_PATH)
+ foreach(dir ${CMAKE_PREFIX_PATH})
+ list(APPEND DIRS "${dir}/bin" "${dir}/lib")
+ endforeach()
+endif()
+
+# Append Qt's lib folder which is two levels above Qt5Widgets_DIR
+list(APPEND DIRS "${Qt5Widgets_DIR}/../..")
+list(APPEND DIRS "/usr/local/lib")
+list(APPEND DIRS "/usr/lib")
+
+if(MSVC)
+ set(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION .)
+endif()
+
+include(InstallRequiredSystemLibraries)
+
+message(STATUS "APPS: ${APPS}")
+message(STATUS "QT_PLUGINS: ${QT_PLUGINS}")
+message(STATUS "DIRS: ${DIRS}")
+
+install(CODE "
+ include(BundleUtilities)
+ fixup_bundle(\"${APPS}\" \"\" \"${DIRS}\")
+ " COMPONENT Runtime)
diff --git a/cmake/libsemver.cmake b/cmake/libsemver.cmake
new file mode 100644
index 00000000..87141844
--- /dev/null
+++ b/cmake/libsemver.cmake
@@ -0,0 +1,4 @@
+set(LIBSEMVER_SOURCES
+ ${CMAKE_SOURCE_DIR}/3rdparty/libsemver/version.cpp
+ ${CMAKE_SOURCE_DIR}/3rdparty/libsemver/version.hpp
+ )
diff --git a/cmake/protobuf.cmake b/cmake/protobuf.cmake
index 39573bbd..fea2e85b 100644
--- a/cmake/protobuf.cmake
+++ b/cmake/protobuf.cmake
@@ -1,5 +1,5 @@
find_package(Protobuf REQUIRED)
-protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS ${CMAKE_SOURCE_DIR}/tools/v2ray_geosite.proto)
-set(QV2RAY_PROTOBUF_LIBRARY
+protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS ${CMAKE_SOURCE_DIR}/assets/v2ray_geosite.proto)
+set(QV2RAY_PROTOBUF_LIBRARY
protobuf::libprotobuf
-)
\ No newline at end of file
+ )
diff --git a/cmake/qnodeeditor.cmake b/cmake/qnodeeditor.cmake
index fe82837b..a4550f9d 100644
--- a/cmake/qnodeeditor.cmake
+++ b/cmake/qnodeeditor.cmake
@@ -23,14 +23,16 @@ if(QV2RAY_QNODEEDITOR_PROVIDER STREQUAL "module")
${QNODEEDITOR_DIR}/src/NodeStyle.cpp
${QNODEEDITOR_DIR}/src/Properties.cpp
${QNODEEDITOR_DIR}/src/StyleCollection.cpp
- )
- set(QNODEEDITOR_INCLUDE_PATH
- ${QNODEEDITOR_DIR}/include/nodes/internal
- )
+ )
- set(HEADERS_TO_MOC
+ set(QNODEEDITOR_INCLUDE_PATH
+ ${QNODEEDITOR_DIR}/include
+ ${QNODEEDITOR_DIR}/include/nodes/internal
+ )
+
+ set(QNODEEDITOR_HEADERS
${QNODEEDITOR_DIR}/include/nodes/internal/Compiler.hpp
- ${QNODEEDITOR_DIR}/include/nodes/internal/Connection.hpp
+ ${QNODEEDITOR_DIR}/include/nodes/internal/Connection.hpp
${QNODEEDITOR_DIR}/include/nodes/internal/ConnectionGeometry.hpp
${QNODEEDITOR_DIR}/include/nodes/internal/ConnectionGraphicsObject.hpp
${QNODEEDITOR_DIR}/include/nodes/internal/ConnectionState.hpp
@@ -56,17 +58,29 @@ if(QV2RAY_QNODEEDITOR_PROVIDER STREQUAL "module")
${QNODEEDITOR_DIR}/include/nodes/internal/Serializable.hpp
${QNODEEDITOR_DIR}/include/nodes/internal/Style.hpp
${QNODEEDITOR_DIR}/include/nodes/internal/TypeConverter.hpp
- )
-
- qt5_wrap_cpp(QNODEEDITOR_SOURCES
- ${HEADERS_TO_MOC}
- TARGET qv2ray
- OPTIONS --no-notes # Don't display a note for the headers which don't produce a moc_*.cpp
- )
+ )
+ # qt5_wrap_cpp(QNODEEDITOR_SOURCES
+ # ${HEADERS_TO_MOC}
+ # TARGET qv2ray
+ # OPTIONS --no-notes # Don't display a note for the headers which don't produce a moc_*.cpp
+ # )
+ set(QNODEEDITOR_LIBRARY qv2ray-nodeeditor)
+ add_library(${QNODEEDITOR_LIBRARY} STATIC
+ ${QNODEEDITOR_SOURCES}
+ ${QNODEEDITOR_HEADERS}
+ )
+ target_include_directories(${QNODEEDITOR_LIBRARY} PUBLIC
+ ${QNODEEDITOR_INCLUDE_PATH}
+ )
+ target_link_libraries(${QNODEEDITOR_LIBRARY}
+ Qt5::Core
+ Qt5::Widgets
+ Qt5::Gui
+ )
set(QNODEEDITOR_QRC_RESOURCES ${QNODEEDITOR_DIR}/resources/resources.qrc)
elseif(QV2RAY_QNODEEDITOR_PROVIDER STREQUAL "package")
find_package(NodeEditor REQUIRED CONFIG)
+ find_path(QNODEEDITOR_INCLUDE_PATH NAMES Node.hpp PATH_SUFFIXES nodes/internal)
set(QNODEEDITOR_LIBRARY NodeEditor::nodes)
- find_path(QNODEEDITOR_INCLUDE_PATH Node.hpp PATH_SUFFIXES nodes/internal)
-endif()
\ No newline at end of file
+endif()
diff --git a/cmake/singleapplication.cmake b/cmake/singleapplication.cmake
index 1f91b21b..d5925f63 100644
--- a/cmake/singleapplication.cmake
+++ b/cmake/singleapplication.cmake
@@ -1,6 +1,11 @@
add_definitions(-DQAPPLICATION_CLASS=QApplication)
-set(SINGLEAPPLICATION_DIR ${CMAKE_SOURCE_DIR}/3rdparty/SingleApplication)
-set(SINGLEAPPLICATION_SOURCES
- ${SINGLEAPPLICATION_DIR}/singleapplication.cpp
- ${SINGLEAPPLICATION_DIR}/singleapplication_p.cpp
-)
\ No newline at end of file
+if(QV2RAY_SINGLEAPPLICATION_PROVIDER STREQUAL "module")
+ set(SINGLEAPPLICATION_DIR ${CMAKE_SOURCE_DIR}/3rdparty/SingleApplication)
+ set(SINGLEAPPLICATION_SOURCES
+ ${SINGLEAPPLICATION_DIR}/singleapplication.cpp
+ ${SINGLEAPPLICATION_DIR}/singleapplication_p.cpp
+ )
+elseif(QV2RAY_SINGLEAPPLICATION_PROVIDER STREQUAL "package")
+ find_library(SINGLEAPPLICATION_LIBRARY NAMES SingleApplication)
+ find_path(SINGLEAPPLICATION_DIR NAMES singleapplication.h PATH_SUFFIXES singleapplication)
+endif()
diff --git a/cmake/zxing-cpp.cmake b/cmake/zxing-cpp.cmake
index 6ab908e9..5a85340f 100644
--- a/cmake/zxing-cpp.cmake
+++ b/cmake/zxing-cpp.cmake
@@ -125,7 +125,7 @@ if(QV2RAY_ZXING_PROVIDER STREQUAL "module")
${ZXING_DIR}/src/pdf417/PDFScanningDecoder.cpp
${ZXING_DIR}/src/pdf417/PDFHighLevelEncoder.cpp
${ZXING_DIR}/src/pdf417/PDFModulusPoly.cpp
- )
+ )
set(ZXING_INCLUDE_PATH
${ZXING_DIR}/src
${ZXING_DIR}/src/aztec
@@ -136,10 +136,17 @@ if(QV2RAY_ZXING_PROVIDER STREQUAL "module")
${ZXING_DIR}/src/pdf417
${ZXING_DIR}/src/qrcode
${ZXING_DIR}/src/textcodec
- )
+ )
+ set(ZXING_LIBRARY qv2ray-zxing)
+ add_library(${ZXING_LIBRARY} STATIC
+ ${ZXING_SOURCES}
+ )
+ target_include_directories(${ZXING_LIBRARY} PUBLIC
+ ${ZXING_INCLUDE_PATH}
+ )
elseif(QV2RAY_ZXING_PROVIDER STREQUAL "package")
find_package(PkgConfig REQUIRED)
pkg_check_modules(ZXING REQUIRED zxing)
set(ZXING_LIBRARY ${ZXING_LIBRARIES})
set(ZXING_INCLUDE_PATH ${ZXING_INCLUDE_DIRS})
-endif()
\ No newline at end of file
+endif()
diff --git a/debian/changelog b/debian/changelog
index 1d4ffcfe..b860e6af 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,47 @@
+qv2ray (2.6.0~rc4-1) unstable; urgency=medium
+
+ * fix: fixed ssd:// order issue, fixed #635
+ * add: remove connections via delete key
+ * add: added qv2ray://
+ * add: use env QV2RAY_CONFIG_PATH to specify config path, make darkmode theme cross-platform, refactor
+ * add: introducing Qv2rayApplication
+
+ -- Guobang Bi Thu, 18 Jun 2020 16:10:05 +0800
+
+qv2ray (2.6.0~rc3-1) unstable; urgency=medium
+
+ * add: added DNS Settings and GroupRouteManager
+ * fix: bgcolor can't switch to default when swiching to flatwhite/psblack and then back to breeze or other
+ * refactor: change include structure
+ * fix: sort groups by displayName
+ * fix: improve GroupManager performance by 5000%
+ * fix: fixed Recent Connections jumplist
+ * fix: fixed EMBED_TRANSLATIONS option
+ * fix: prevent reading geosite/geoip every time
+ * add: added ICMP as latency tester
+
+ -- Guobang Bi Wed, 27 May 2020 18:17:03 +0800
+
+qv2ray (2.6.0~rc2-1) unstable; urgency=medium
+
+ * fix: fixed tcping message
+ * refactor: refactored auto update algorithm
+ * fix: fixed Windows non-block TCPing
+ * fix: UI tweak, prevent connection list taking too much space
+
+ -- Guobang Bi Mon, 18 May 2020 12:01:50 +0800
+
+qv2ray (2.6.0~rc1-1) unstable; urgency=medium
+
+ * NTP Check
+ * Port Detection
+ * Introduce unit test
+ * Introduce group editor/manager
+ * added vmess v1 upgrader (close #609)
+ * Refactor system proxy
+
+ -- Guobang Bi Sun, 17 May 2020 20:23:35 +0800
+
qv2ray (2.5.0-1) unstable; urgency=medium
* updating translations
diff --git a/debian/control b/debian/control
index c3ce5108..7f4c0e05 100644
--- a/debian/control
+++ b/debian/control
@@ -11,6 +11,7 @@ Build-Depends: debhelper (>= 11),
protobuf-compiler (>= 3.6.1.3-1~),
protobuf-compiler-grpc (>= 1.16.1-1~),
qtbase5-dev (>= 5.11.3-1~),
+ qtdeclarative5-dev (>= 5.11.3-1~),
qttools5-dev (>= 5.11.3-1~)
Standards-Version: 4.5.0
Homepage: https://github.com/Qv2ray/Qv2ray
@@ -21,4 +22,7 @@ Package: qv2ray
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}
Recommends: v2ray
-Description: Qv2ray, A Qt frontend for v2ray. Written in c++.
+Description: Qt frontend for v2ray
+ Project Qv2ray is a flexable cross platform Qt GUI client for V2ray.
+ It makes everything easier by using GUI editors. It also supports
+ ShadowsicksR, Trojan and NaiveProxy by making them plugins.
diff --git a/debian/rules b/debian/rules
index b102e12d..4d9457f4 100755
--- a/debian/rules
+++ b/debian/rules
@@ -16,6 +16,4 @@ export QT_SELECT := 5
dh $@ --buildsystem=cmake+ninja
override_dh_auto_configure:
- dh_auto_configure -- -DEMBED_TRANSLATIONS=ON -DCMAKE_BUILD_TYPE=Release
-
-override_dh_auto_test:
+ dh_auto_configure -- -DEMBED_TRANSLATIONS=ON -DBUILD_TESTING=ON
diff --git a/hooks/pre-commit b/hooks/pre-commit
new file mode 100755
index 00000000..a7d0ad5c
--- /dev/null
+++ b/hooks/pre-commit
@@ -0,0 +1,12 @@
+#!/bin/bash
+STAGE_FILES=$(git diff --cached --name-only --diff-filter=ACM -- 'makespec/BUILDVERSION')
+#echo $STAGE_FILES
+if test ${#STAGE_FILES} -gt 0
+then
+ echo 'BUILDVERSION already changed, not touching'
+else
+ echo 'Increasing BUILDVERSION'
+ expr $(cat ./makespec/BUILDVERSION) + 1 > ./makespec/BUILDVERSION
+ cat ./makespec/BUILDVERSION
+ git add ./makespec/BUILDVERSION
+fi
diff --git a/libs/QJsonStruct b/libs/QJsonStruct
new file mode 160000
index 00000000..91c3ca1c
--- /dev/null
+++ b/libs/QJsonStruct
@@ -0,0 +1 @@
+Subproject commit 91c3ca1c3279448052b6be19dc8157517c35a7ca
diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION
index 040c32b6..02d2a545 100644
--- a/makespec/BUILDVERSION
+++ b/makespec/BUILDVERSION
@@ -1 +1 @@
-5335
\ No newline at end of file
+5697
diff --git a/makespec/VERSION b/makespec/VERSION
index 437459cd..e70b4523 100644
--- a/makespec/VERSION
+++ b/makespec/VERSION
@@ -1 +1 @@
-2.5.0
+2.6.0
diff --git a/makespec/VERSIONSUFFIX b/makespec/VERSIONSUFFIX
index 8b137891..2c1cea9a 100644
--- a/makespec/VERSIONSUFFIX
+++ b/makespec/VERSIONSUFFIX
@@ -1 +1 @@
-
+-rc4
diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml
index c1959052..d943b190 100644
--- a/snap/snapcraft.yaml
+++ b/snap/snapcraft.yaml
@@ -1,5 +1,5 @@
name: qv2ray
-base: core18
+base: core20
adopt-info: qv2ray
icon: assets/icons/qv2ray.png
@@ -44,6 +44,15 @@ apps:
desktop: "usr/share/applications/qv2ray.desktop"
parts:
+ ppa:
+ plugin: nil
+ build-packages:
+ - software-properties-common
+ override-pull: |
+ sudo add-apt-repository ppa:ymshenyu/libuv
+ sudo apt-get update
+ sudo apt-get -y dist-upgrade
+
qv2ray:
plugin: cmake
source-type: git
@@ -51,14 +60,13 @@ parts:
parse-info: [usr/share/metainfo/qv2ray.metainfo.xml]
build-packages:
- build-essential
- - qttools5-dev-tools
- qttools5-dev
- qt5-default
- libgrpc++-dev
- libprotobuf-dev
- protobuf-compiler-grpc
- - ninja-build
- pkg-config
+ - libzxingcore-dev
stage-packages:
- libgcc1
- libstdc++6
@@ -70,11 +78,13 @@ parts:
- libqt5network5
- libqt5widgets5
- libglib2.0-bin
- configflags:
+ - libzxingcore1
+ cmake-parameters:
- -DCMAKE_INSTALL_PREFIX=/usr
- -DCMAKE_BUILD_TYPE=Release
- - -GNinja
- -DEMBED_TRANSLATIONS=ON
+ - -DFALL_BACK_TO_XDG_OPEN=ON
+ - -DQV2RAY_ZXING_PROVIDER=package
override-pull: |
snapcraftctl pull
build_number=$(cat makespec/BUILDVERSION)
@@ -84,7 +94,6 @@ parts:
sed -i 's|^Icon=.*|Icon=/usr/share/icons/hicolor/256x256/apps/qv2ray.png|g' assets/qv2ray.desktop
after:
- desktop-qt5
- - ppa
desktop-qt5:
source: https://github.com/ubuntu/snapcraft-desktop-helpers.git
@@ -110,21 +119,70 @@ parts:
- locales-all
- xdg-user-dirs
- fcitx-frontend-qt5
- after:
- - ppa
qt5-gtk-platform:
plugin: nil
stage-packages:
- qt5-gtk-platformtheme
- after:
- - ppa
- ppa:
- plugin: nil
+ qv2ray-ssr-plugin:
+ plugin: cmake
+ source-type: git
+ source: https://github.com/Qv2ray/QvPlugin-SSR.git
+ source-branch: dev
build-packages:
- - software-properties-common
- - dirmngr
- override-build: |
- sudo add-apt-repository -y ppa:ymshenyu/qv2ray-deps
- sudo apt-get dist-upgrade -y
\ No newline at end of file
+ - build-essential
+ - libsodium-dev
+ - libuv1-dev
+ - libssl-dev
+ - qttools5-dev
+ - qt5-default
+ stage-packages:
+ - libgcc1
+ - libstdc++6
+ - libssl1.1
+ - libqt5core5a
+ - libqt5gui5
+ - libqt5network5
+ - libqt5widgets5
+ - libuv1
+ - libsodium23
+ cmake-parameters:
+ - -DCMAKE_INSTALL_PREFIX=/usr
+ - -DCMAKE_BUILD_TYPE=Release
+ - -DSSR_UVW_WITH_QT=ON
+ - -DUSE_SYSTEM_SODIUM=ON
+ - -DUSE_SYSTEM_LIBUV=ON
+ - -DSTATIC_LINK_LIBUV=OFF
+ - -DSTATIC_LINK_SODIUM=OFF
+ after:
+ - desktop-qt5
+
+ qv2ray-trojan-plugin:
+ plugin: cmake
+ source-type: git
+ source: https://github.com/Qv2ray/QvPlugin-Trojan.git
+ source-branch: dev
+ build-packages:
+ - build-essential
+ - libboost-system-dev
+ - libboost-program-options-dev
+ - libssl-dev
+ - qttools5-dev
+ - qt5-default
+ stage-packages:
+ - libgcc1
+ - libstdc++6
+ - libssl1.1
+ - libqt5core5a
+ - libqt5gui5
+ - libqt5network5
+ - libqt5widgets5
+ - libboost-program-options1.71.0
+ - libboost-system1.71.0
+ cmake-parameters:
+ - -DCMAKE_INSTALL_PREFIX=/usr
+ - -DCMAKE_BUILD_TYPE=Release
+ - -DFORCE_TCP_FASTOPEN=ON
+ after:
+ - desktop-qt5
\ No newline at end of file
diff --git a/src/Qv2rayApplication.cpp b/src/Qv2rayApplication.cpp
new file mode 100644
index 00000000..fb178819
--- /dev/null
+++ b/src/Qv2rayApplication.cpp
@@ -0,0 +1,541 @@
+#include "Qv2rayApplication.hpp"
+
+#include "3rdparty/libsemver/version.hpp"
+#include "base/Qv2rayBase.hpp"
+#include "common/QvHelpers.hpp"
+#include "common/QvTranslator.hpp"
+#include "core/handler/ConfigHandler.hpp"
+#include "core/handler/RouteHandler.hpp"
+#include "core/settings/SettingsBackend.hpp"
+#include "ui/styles/StyleManager.hpp"
+#include "ui/windows/w_MainWindow.hpp"
+
+#include
+#include
+
+#ifdef Q_OS_WIN
+ #include
+#endif
+
+namespace Qv2ray
+{
+ constexpr auto QV2RAY_CONFIG_PATH_ENV_NAME = "QV2RAY_CONFIG_PATH";
+
+ Qv2rayApplication::Qv2rayApplication(int &argc, char *argv[])
+#ifdef Q_OS_ANDROID
+ : QApplication(argc, argv)
+#else
+ : SingleApplication(argc, argv, true, User | ExcludeAppPath | ExcludeAppVersion)
+#endif
+ {
+ LOG(MODULE_INIT, "Qv2ray " QV2RAY_VERSION_STRING " on " + QSysInfo::prettyProductName() + " " + QSysInfo::currentCpuArchitecture())
+ DEBUG(MODULE_INIT, "Qv2ray Start Time: " + QSTRN(QTime::currentTime().msecsSinceStartOfDay()))
+ DEBUG("QV2RAY_BUILD_INFO", QV2RAY_BUILD_INFO)
+ DEBUG("QV2RAY_BUILD_EXTRA_INFO", QV2RAY_BUILD_EXTRA_INFO)
+ DEBUG("QV2RAY_BUILD_NUMBER", QSTRN(QV2RAY_VERSION_BUILD))
+ hTray = new QSystemTrayIcon();
+ }
+
+ void Qv2rayApplication::QuitApplication(int retCode)
+ {
+ isExiting = true;
+ QCoreApplication::exit(retCode);
+ }
+
+ Qv2rayApplication::Qv2raySetupStatus Qv2rayApplication::SetupQv2ray()
+ {
+#ifdef Q_OS_WIN
+ SetCurrentDirectory(applicationDirPath().toStdWString().c_str());
+#endif
+ // Install a default translater. From the OS/DE
+ Qv2rayTranslator = std::make_unique();
+ Qv2rayTranslator->InstallTranslation(QLocale::system().name());
+ //
+ setQuitOnLastWindowClosed(false);
+
+#ifndef Q_OS_ANDROID
+ connect(this, &SingleApplication::receivedMessage, this, &Qv2rayApplication::onMessageReceived);
+ connect(this, &SingleApplication::aboutToQuit, this, &Qv2rayApplication::aboutToQuitSlot);
+ if (isSecondary())
+ {
+ Qv2rayProcessArgument.arguments << Qv2rayProcessArguments::NORMAL;
+ sendMessage(JsonToString(Qv2rayProcessArgument.toJson(), QJsonDocument::Compact).toUtf8());
+ return SINGLE_APPLICATION;
+ }
+#endif
+
+#ifdef Q_OS_WIN
+ // Set special font in Windows
+ QFont font;
+ font.setPointSize(9);
+ font.setFamily("Microsoft YaHei");
+ setFont(font);
+#endif
+
+#ifdef Q_OS_LINUX
+ setFallbackSessionManagementEnabled(false);
+ connect(this, &QGuiApplication::commitDataRequest, [] {
+ ConnectionManager->SaveConnectionConfig();
+ LOG(MODULE_INIT, "Quit triggered by session manager.")
+ });
+#endif
+ return NORMAL;
+ }
+
+ void Qv2rayApplication::aboutToQuitSlot()
+ {
+ delete mainWindow;
+ delete hTray;
+ delete ConnectionManager;
+ delete RouteManager;
+ delete PluginHost;
+ delete StyleManager;
+ }
+
+ void Qv2rayApplication::onMessageReceived(quint32 clientId, QByteArray _msg)
+ {
+ // Sometimes SingleApplication will send message with clientId == 0, ignore them.
+ if (clientId == instanceId())
+ return;
+ const auto msg = Qv2rayProcessArguments::fromJson(JsonFromString(_msg));
+ LOG(MODULE_INIT, "Client ID: " + QSTRN(clientId) + ", message received, version: " + msg.version)
+ DEBUG(MODULE_INIT, _msg)
+ //
+ const auto currentVersion = semver::version::from_string(QV2RAY_VERSION_STRING);
+ const auto newVersionString = msg.version.isEmpty() ? "0.0.0" : msg.version.toStdString();
+ const auto newVersion = semver::version::from_string(newVersionString);
+ //
+ if (newVersion > currentVersion)
+ {
+ QTimer::singleShot(0, [=]() {
+ const auto newPath = msg.fullArgs.first();
+ QString message;
+ message += tr("A new version of Qv2ray is attemping to start:") + NEWLINE;
+ message += NEWLINE;
+ message += tr("New version information: ") + NEWLINE;
+ message += tr("Qv2ray version: %1").arg(msg.version) + NEWLINE;
+ message += tr("Qv2ray path: %1").arg(newPath) + NEWLINE;
+ message += NEWLINE;
+ message += tr("Do you want to exit and launch that new version?");
+
+ const auto result = QvMessageBoxAsk(nullptr, tr("New version detected"), message);
+ if (result == QMessageBox::Yes)
+ {
+ Qv2rayProcessArgument._qvNewVersionPath = newPath;
+ QuitApplication(QV2RAY_NEW_VERSION);
+ }
+ });
+ }
+
+ for (const auto &argument : msg.arguments)
+ {
+ switch (argument)
+ {
+ case Qv2rayProcessArguments::EXIT:
+ {
+ QuitApplication();
+ break;
+ }
+ case Qv2rayProcessArguments::NORMAL:
+ {
+ mainWindow->show();
+ mainWindow->raise();
+ mainWindow->activateWindow();
+ break;
+ }
+ case Qv2rayProcessArguments::RECONNECT:
+ {
+ ConnectionManager->RestartConnection();
+ break;
+ }
+ case Qv2rayProcessArguments::DISCONNECT:
+ {
+ ConnectionManager->StopConnection();
+ break;
+ }
+ case Qv2rayProcessArguments::QV2RAY_LINK:
+ {
+ for (const auto &link : msg.links)
+ {
+ const auto url = QUrl::fromUserInput(link);
+ const auto command = url.host();
+ auto subcommands = url.path().split("/");
+ subcommands.removeAll("");
+ QMap args;
+ for (const auto &kvp : QUrlQuery(url).queryItems())
+ {
+ args.insert(kvp.first, kvp.second);
+ }
+ if (command == "open")
+ {
+ emit mainWindow->ProcessCommand(command, subcommands, args);
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ Qv2rayExitCode Qv2rayApplication::RunQv2ray()
+ {
+ // Show MainWindow
+ mainWindow = new MainWindow();
+ return Qv2rayExitCode(exec());
+ }
+
+ bool Qv2rayApplication::FindAndCreateInitialConfiguration()
+ {
+ if (initialized)
+ {
+ LOG(MODULE_INIT, "Qv2ray has already been initialized!")
+ return false;
+ }
+ LOG(MODULE_INIT, "Application exec path: " + applicationDirPath())
+ // Non-standard paths needs special handing for "_debug"
+ const auto currentPathConfig = applicationDirPath() + "/config" QV2RAY_CONFIG_DIR_SUFFIX;
+ const auto homeQv2ray = QDir::homePath() + "/.qv2ray" QV2RAY_CONFIG_DIR_SUFFIX;
+ //
+ // Standard paths already handles the "_debug" suffix for us.
+ const auto configQv2ray = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
+ //
+ //
+ // Some built-in search paths for Qv2ray to find configs. (load the first one if possible).
+ QStringList configFilePaths;
+ const auto useManualConfigPath = qEnvironmentVariableIsSet(QV2RAY_CONFIG_PATH_ENV_NAME);
+ const auto manualConfigPath = qEnvironmentVariable(QV2RAY_CONFIG_PATH_ENV_NAME);
+ if (useManualConfigPath)
+ {
+ LOG(MODULE_INIT, "Using config path from env: " + manualConfigPath)
+ configFilePaths << manualConfigPath;
+ }
+ else
+ {
+ configFilePaths << currentPathConfig;
+ configFilePaths << configQv2ray;
+ configFilePaths << homeQv2ray;
+ }
+ QString configPath = "";
+ bool hasExistingConfig = false;
+
+ for (const auto &path : configFilePaths)
+ {
+ // Verify the config path, check if the config file exists and in the
+ // correct JSON format. True means we check for config existence as
+ // well. ----------------------------------------------|HERE|
+ bool isValidConfigPath = CheckSettingsPathAvailability(path, true);
+
+ // If we already found a valid config file. just simply load it...
+ if (hasExistingConfig)
+ break;
+
+ if (isValidConfigPath)
+ {
+ DEBUG(MODULE_INIT, "Path: " + path + " is valid.")
+ configPath = path;
+ hasExistingConfig = true;
+ }
+ else
+ {
+ LOG(MODULE_INIT, "Path: " + path + " does not contain a valid config file.")
+ }
+ }
+
+ if (hasExistingConfig)
+ {
+ // Use the config path found by the checks above
+ SetConfigDirPath(configPath);
+ LOG(MODULE_INIT, "Using " + QV2RAY_CONFIG_DIR + " as the config path.")
+ }
+ else
+ {
+ // If there's no existing config.
+ //
+ // Create new config at these dirs, these are default values for each platform.
+ if (useManualConfigPath)
+ {
+ configPath = manualConfigPath;
+ }
+ else
+ {
+#if defined(Q_OS_WIN) && !defined(QV2RAY_NO_ASIDECONFIG)
+ configPath = currentPathConfig;
+#else
+ configPath = configQv2ray;
+#endif
+ }
+
+ bool hasPossibleNewLocation = QDir().mkpath(configPath) && CheckSettingsPathAvailability(configPath, false);
+ // Check if the dirs are write-able
+ if (!hasPossibleNewLocation)
+ {
+ // None of the path above can be used as a dir for storing config.
+ // Even the last folder failed to pass the check.
+ LOG(MODULE_INIT, "FATAL")
+ LOG(MODULE_INIT, " ---> CANNOT find a proper place to store Qv2ray config files.")
+ QvMessageBoxWarn(nullptr, tr("Cannot Start Qv2ray"),
+ tr("Cannot find a place to store config files.") + NEWLINE + //
+ tr("Qv2ray has searched these paths below:") + NEWLINE + NEWLINE + //
+ configFilePaths.join(NEWLINE) + NEWLINE + //
+ tr("It usually means you don't have the write permission to all of those locations.") + NEWLINE + //
+ tr("Qv2ray will now exit.")); //
+ return false;
+ }
+
+ // Found a valid config dir, with write permission, but assume no config is located in it.
+ LOG(MODULE_INIT, "Set " + configPath + " as the config path.")
+ SetConfigDirPath(configPath);
+
+ if (QFile::exists(QV2RAY_CONFIG_FILE))
+ {
+ // As we already tried to load config from every possible dir.
+ //
+ // This condition branch (!hasExistingConfig check) holds the fact that current config dir,
+ // should NOT contain any valid file (at least in the same name)
+ //
+ // It usually means that QV2RAY_CONFIG_FILE here has a corrupted JSON format.
+ //
+ // Otherwise Qv2ray would have loaded this config already instead of notifying to create a new config in this folder.
+ //
+ LOG(MODULE_INIT, "This should not occur: Qv2ray config exists but failed to load.")
+ QvMessageBoxWarn(nullptr, tr("Failed to initialise Qv2ray"),
+ tr("Failed to determine the location of config file:") + NEWLINE + //
+ tr("Qv2ray has found a config file, but it failed to be loaded due to some errors.") + NEWLINE + //
+ tr("A workaround is to remove the this file and restart Qv2ray:") + NEWLINE + //
+ QV2RAY_CONFIG_FILE + NEWLINE + //
+ tr("Qv2ray will now exit.") + NEWLINE + //
+ tr("Please report if you think it's a bug.")); //
+ return false;
+ }
+
+ Qv2rayConfigObject conf;
+ conf.kernelConfig.KernelPath(QString(QV2RAY_DEFAULT_VCORE_PATH));
+ conf.kernelConfig.AssetsPath(QString(QV2RAY_DEFAULT_VASSETS_PATH));
+ conf.logLevel = 3;
+ conf.uiConfig.language = QLocale::system().name();
+ conf.defaultRouteConfig.dnsConfig.servers << QvConfig_DNS::DNSServerObject{ "1.1.1.1" } //
+ << QvConfig_DNS::DNSServerObject{ "8.8.8.8" } //
+ << QvConfig_DNS::DNSServerObject{ "8.8.4.4" };
+
+ // Save initial config.
+ SaveGlobalSettings(conf);
+ LOG(MODULE_INIT, "Created initial config file.")
+ }
+
+ if (!QDir(QV2RAY_GENERATED_DIR).exists())
+ {
+ // The dir used to generate final config file, for V2ray interaction.
+ QDir().mkdir(QV2RAY_GENERATED_DIR);
+ LOG(MODULE_INIT, "Created config generation dir at: " + QV2RAY_GENERATED_DIR)
+ }
+ return true;
+ }
+
+ bool Qv2rayApplication::LoadConfiguration()
+ {
+ // Load the config for upgrade, but do not parse it to the struct.
+ auto conf = JsonFromString(StringFromFile(QV2RAY_CONFIG_FILE));
+ const auto configVersion = conf["config_version"].toInt();
+
+ if (configVersion > QV2RAY_CONFIG_VERSION)
+ {
+ // Config version is larger than the current version...
+ // This is rare but it may happen....
+ QvMessageBoxWarn(nullptr, tr("Qv2ray Cannot Continue"), //
+ tr("You are running a lower version of Qv2ray compared to the current config file.") + NEWLINE + //
+ tr("Please check if there's an issue explaining about it.") + NEWLINE + //
+ tr("Or submit a new issue if you think this is an error.") + NEWLINE + NEWLINE + //
+ tr("Qv2ray will now exit."));
+ return false;
+ }
+ else if (configVersion < QV2RAY_CONFIG_VERSION)
+ {
+ // That is the config file needs to be upgraded.
+ conf = Qv2ray::UpgradeSettingsVersion(configVersion, QV2RAY_CONFIG_VERSION, conf);
+ }
+
+ // Load config object from upgraded config QJsonObject
+ auto confObject = Qv2rayConfigObject::fromJson(conf);
+
+ if (!Qv2rayTranslator->GetAvailableLanguages().contains(confObject.uiConfig.language))
+ {
+ // Prevent empty.
+ LOG(MODULE_UI, "Setting default UI language to system locale.")
+ confObject.uiConfig.language = QLocale::system().name();
+ }
+
+ if (!Qv2rayTranslator->InstallTranslation(confObject.uiConfig.language))
+ {
+ QvMessageBoxWarn(nullptr, "Translation Failed",
+ "Cannot load translation for " + confObject.uiConfig.language + NEWLINE + //
+ "English is now used." + NEWLINE + NEWLINE + //
+ "Please go to Preferences Window to change language or open an Issue");
+ }
+
+ // Let's save the config.
+ SaveGlobalSettings(confObject);
+ return true;
+ }
+
+ void Qv2rayApplication::InitializeGlobalVariables()
+ {
+ StyleManager = new QvStyleManager();
+ PluginHost = new QvPluginHost();
+ RouteManager = new RouteHandler();
+ ConnectionManager = new QvConfigHandler();
+ StyleManager->ApplyStyle(GlobalConfig.uiConfig.theme);
+ }
+
+ bool Qv2rayApplication::PreInitialize(int argc, char **argv)
+ {
+ QString errorMessage;
+
+ {
+ QCoreApplication coreApp(argc, argv);
+ const auto &args = coreApp.arguments();
+ Qv2rayProcessArgument.version = QV2RAY_VERSION_STRING;
+ Qv2rayProcessArgument.fullArgs = args;
+ switch (ParseCommandLine(&errorMessage, args))
+ {
+ case QV2RAY_QUIT: return false;
+ case QV2RAY_ERROR: LOG(MODULE_INIT, errorMessage) return false;
+ default: break;
+ }
+#ifdef Q_OS_WIN
+ const auto urlScheme = coreApp.applicationName();
+ const auto appPath = QDir::toNativeSeparators(coreApp.applicationFilePath());
+ const auto regPath = "HKEY_CURRENT_USER\\Software\\Classes\\" + urlScheme;
+
+ QSettings reg(regPath, QSettings::NativeFormat);
+
+ reg.setValue("Default", "Qv2ray");
+ reg.setValue("URL Protocol", "");
+
+ reg.beginGroup("DefaultIcon");
+ reg.setValue("Default", QString("%1,1").arg(appPath));
+ reg.endGroup();
+
+ reg.beginGroup("shell");
+ reg.beginGroup("open");
+ reg.beginGroup("command");
+ reg.setValue("Default", appPath + " %1");
+#endif
+ }
+
+ // noScaleFactors = disable HiDPI
+ if (StartupOption.noScaleFactor)
+ {
+ LOG(MODULE_INIT, "Force set QT_SCALE_FACTOR to 1.")
+ DEBUG(MODULE_UI, "Original QT_SCALE_FACTOR was: " + qEnvironmentVariable("QT_SCALE_FACTOR"))
+ qputenv("QT_SCALE_FACTOR", "1");
+ }
+ else
+ {
+ DEBUG(MODULE_INIT, "High DPI scaling is enabled.")
+ QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
+#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
+ QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
+#endif
+ }
+ return true;
+ }
+
+ Qv2rayApplication::commandline_status Qv2rayApplication::ParseCommandLine(QString *errorMessage, const QStringList &args)
+ {
+ QCommandLineParser parser;
+ //
+ QCommandLineOption noAPIOption("noAPI", tr("Disable gRPC API subsystem"));
+ QCommandLineOption noPluginsOption("noPlugin", tr("Disable plugins feature"));
+ QCommandLineOption noScaleFactorOption("noScaleFactor", tr("Disable Qt UI scale factor"));
+ QCommandLineOption debugOption("debug", tr("Enable debug output"));
+ QCommandLineOption disconnectOption("disconnect", tr("Stop current connection"));
+ QCommandLineOption reconnectOption("reconnect", tr("Reconnect last connection"));
+ QCommandLineOption exitOption("exit", tr("Exit Qv2ray"));
+ //
+ parser.setApplicationDescription(tr("Qv2ray - A cross-platform Qt frontend for V2ray."));
+ parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
+ //
+ parser.addOption(noAPIOption);
+ parser.addOption(noPluginsOption);
+ parser.addOption(noScaleFactorOption);
+ parser.addOption(debugOption);
+ parser.addOption(disconnectOption);
+ parser.addOption(reconnectOption);
+ parser.addOption(exitOption);
+ //
+ auto helpOption = parser.addHelpOption();
+ auto versionOption = parser.addVersionOption();
+
+ if (!parser.parse(args))
+ {
+ *errorMessage = parser.errorText();
+ return QV2RAY_ERROR;
+ }
+
+ if (parser.isSet(versionOption))
+ {
+ parser.showVersion();
+ return QV2RAY_QUIT;
+ }
+
+ if (parser.isSet(helpOption))
+ {
+ parser.showHelp();
+ return QV2RAY_QUIT;
+ }
+
+ for (const auto &arg : parser.positionalArguments())
+ {
+ if (arg.startsWith("qv2ray://"))
+ {
+ Qv2rayProcessArgument.arguments << Qv2rayProcessArguments::QV2RAY_LINK;
+ Qv2rayProcessArgument.links << arg;
+ }
+ }
+
+ if (parser.isSet(exitOption))
+ {
+ DEBUG(MODULE_INIT, "disconnectOption is set.")
+ Qv2rayProcessArgument.arguments << Qv2rayProcessArguments::EXIT;
+ }
+
+ if (parser.isSet(disconnectOption))
+ {
+ DEBUG(MODULE_INIT, "disconnectOption is set.")
+ Qv2rayProcessArgument.arguments << Qv2rayProcessArguments::DISCONNECT;
+ }
+
+ if (parser.isSet(reconnectOption))
+ {
+ DEBUG(MODULE_INIT, "reconnectOption is set.")
+ Qv2rayProcessArgument.arguments << Qv2rayProcessArguments::RECONNECT;
+ }
+
+ if (parser.isSet(noAPIOption))
+ {
+ DEBUG(MODULE_INIT, "noAPIOption is set.")
+ StartupOption.noAPI = true;
+ }
+
+ if (parser.isSet(debugOption))
+ {
+ DEBUG(MODULE_INIT, "debugOption is set.")
+ StartupOption.debugLog = true;
+ }
+
+ if (parser.isSet(noScaleFactorOption))
+ {
+ DEBUG(MODULE_INIT, "noScaleFactorOption is set.")
+ StartupOption.noScaleFactor = true;
+ }
+
+ if (parser.isSet(noPluginsOption))
+ {
+ DEBUG(MODULE_INIT, "noPluginOption is set.")
+ StartupOption.noPlugins = true;
+ }
+
+ return QV2RAY_CONTINUE;
+ }
+
+} // namespace Qv2ray
diff --git a/src/Qv2rayApplication.hpp b/src/Qv2rayApplication.hpp
new file mode 100644
index 00000000..0efd2c4a
--- /dev/null
+++ b/src/Qv2rayApplication.hpp
@@ -0,0 +1,108 @@
+#pragma once
+
+#include "libs/QJsonStruct/QJsonStruct.hpp"
+
+#include
+#ifdef Q_OS_ANDROID
+ #include
+#else
+ #include
+#endif
+class MainWindow;
+
+namespace Qv2ray
+{
+ enum Qv2rayExitCode
+ {
+ QV2RAY_NORMAL = 0,
+ QV2RAY_SECONDARY_INSTANCE = 0,
+ QV2RAY_PRE_INITIALIZE_FAIL = -1,
+ QV2RAY_EARLY_SETUP_FAIL = -2,
+ QV2RAY_CONFIG_PATH_FAIL = -3,
+ QV2RAY_CONFIG_FILE_FAIL = -4,
+ QV2RAY_SSL_FAIL = -5,
+ QV2RAY_NEW_VERSION = -6
+ };
+ struct Qv2rayProcessArguments
+ {
+ enum Argument
+ {
+ NORMAL = 0,
+ QV2RAY_LINK = 1,
+ EXIT = 2,
+ RECONNECT = 3,
+ DISCONNECT = 4
+ };
+ QList arguments;
+ QString version;
+ QString data;
+ QList links;
+ QList fullArgs;
+ //
+ QString _qvNewVersionPath;
+ JSONSTRUCT_REGISTER(Qv2rayProcessArguments, F(arguments, version, data, links, fullArgs))
+ };
+
+ inline Qv2rayProcessArguments Qv2rayProcessArgument;
+#ifdef Q_OS_ANDROID
+ class Qv2rayApplication : public QApplication
+#else
+ class Qv2rayApplication : public SingleApplication
+#endif
+ {
+ Q_OBJECT
+
+ enum commandline_status
+ {
+ QV2RAY_ERROR,
+ QV2RAY_QUIT,
+ QV2RAY_CONTINUE
+ };
+
+ public:
+ enum Qv2raySetupStatus
+ {
+ NORMAL,
+ SINGLE_APPLICATION,
+ FAILED
+ };
+ //
+ void QuitApplication(int retCode = 0);
+ static bool PreInitialize(int argc, char **argv);
+ explicit Qv2rayApplication(int &argc, char *argv[]);
+ Qv2raySetupStatus SetupQv2ray();
+ bool FindAndCreateInitialConfiguration();
+ bool LoadConfiguration();
+ void InitializeGlobalVariables();
+ Qv2rayExitCode RunQv2ray();
+
+ public:
+ QSystemTrayIcon **GetTrayIcon()
+ {
+ return &hTray;
+ }
+ void showMessage(const QString &m, const QIcon &icon, int msecs = 10000)
+ {
+ hTray->showMessage("Qv2ray", m, icon, msecs);
+ }
+ void showMessage(const QString &m, QSystemTrayIcon::MessageIcon icon = QSystemTrayIcon::Information, int msecs = 10000)
+ {
+ hTray->showMessage("Qv2ray", m, icon, msecs);
+ }
+
+ private slots:
+ void aboutToQuitSlot();
+ void onMessageReceived(quint32 clientID, QByteArray msg);
+
+ private:
+ QSystemTrayIcon *hTray;
+ MainWindow *mainWindow;
+ static commandline_status ParseCommandLine(QString *errorMessage, const QStringList &args);
+ bool initialized = false;
+ };
+} // namespace Qv2ray
+
+using namespace Qv2ray;
+
+#define qvApp (dynamic_cast(QCoreApplication::instance()))
+#define qvAppTrayIcon (*qvApp->GetTrayIcon())
diff --git a/src/StackTraceHelper.cpp b/src/StackTraceHelper.cpp
new file mode 100644
index 00000000..28af3f22
--- /dev/null
+++ b/src/StackTraceHelper.cpp
@@ -0,0 +1,91 @@
+#include "StackTraceHelper.hpp"
+namespace Qv2ray
+{
+#ifdef Q_OS_UNIX
+ QString StackTraceHelper::GetStackTraceImpl_Unix()
+ {
+ backward::StackTrace st;
+ backward::TraceResolver resolver;
+ st.load_here();
+ resolver.load_stacktrace(st);
+ //
+ #ifdef QV2RAY_PRINT_FULL_BACKTRACE
+ backward::Printer p;
+ std::stringstream o;
+ p.print(st, o);
+ QString msg;
+ msg += QString::fromStdString(o.str());
+ return msg;
+ #else
+ QString msg;
+ for (size_t i = 0; i < st.size(); i++)
+ {
+ /*
+ * It works because in real life, most signals are not around failures of the memory allocator (ie: malloc) itself.
+ * As long as you can allocate memory, you are pretty ok.
+ * Now, here is an example where backward will deadlock:
+ *
+ * you buffer overflow inside your allocator data structure
+ * you ask your allocator to free or allocate something
+ * allocator acquires some locks
+ * allocator shit itself because its datastructures are corrupted
+ * signal is raised, backward-cpp kicks in
+ * while walking the stack, backward-cpp tries to allocate memory, calling your allocator...
+ * allocator tries to acquires some locks...
+ *
+ * oops, deadlock.
+ */
+ auto trace = resolver.resolve(st[i]);
+ QString sourceFile;
+ if (!trace.source.filename.empty())
+ sourceFile = QString("%0:[%1:%2]").arg(trace.source.filename.c_str()).arg(trace.source.line).arg(trace.source.col);
+ else
+ sourceFile = "[N/A]";
+
+ // #Index: [ADDRESS] Function File Line:Col
+ msg += QString("#%1: [%2] %3 in %4 --> %5\r\n")
+ .arg(i)
+ .arg(reinterpret_cast(trace.addr))
+ .arg(trace.object_function.c_str())
+ .arg(trace.object_filename.c_str())
+ .arg(sourceFile);
+ }
+ return msg;
+ #endif
+ }
+#endif
+
+#ifdef Q_OS_WIN
+ QString StackTraceHelper::GetStackTraceImpl_Windows()
+ {
+ void *stack[1024];
+ HANDLE process = GetCurrentProcess();
+ SymInitialize(process, NULL, TRUE);
+ SymSetOptions(SYMOPT_LOAD_ANYTHING);
+ WORD numberOfFrames = CaptureStackBackTrace(0, 1024, stack, NULL);
+ SYMBOL_INFO *symbol = (SYMBOL_INFO *) malloc(sizeof(SYMBOL_INFO) + (512 - 1) * sizeof(TCHAR));
+ symbol->MaxNameLen = 512;
+ symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
+ DWORD displacement;
+ IMAGEHLP_LINE64 *line = (IMAGEHLP_LINE64 *) malloc(sizeof(IMAGEHLP_LINE64));
+ line->SizeOfStruct = sizeof(IMAGEHLP_LINE64);
+ //
+ QString msg;
+ //
+ for (int i = 0; i < numberOfFrames; i++)
+ {
+ const auto address = (DWORD64) stack[i];
+ SymFromAddr(process, address, NULL, symbol);
+ if (SymGetLineFromAddr64(process, address, &displacement, line))
+ {
+ msg += QString("[%1]: %2 (%3:%4)\r\n").arg(symbol->Address).arg(symbol->Name).arg(line->FileName).arg(line->LineNumber);
+ }
+ else
+ {
+ msg += QString("[%1]: %2 SymGetLineFromAddr64[%3]\r\n").arg(symbol->Address).arg(symbol->Name).arg(GetLastError());
+ }
+ }
+ return msg;
+ }
+#endif
+} // namespace Qv2ray
diff --git a/src/StackTraceHelper.hpp b/src/StackTraceHelper.hpp
new file mode 100644
index 00000000..b807a693
--- /dev/null
+++ b/src/StackTraceHelper.hpp
@@ -0,0 +1,36 @@
+#pragma once
+
+#include
+#ifdef Q_OS_UNIX
+ #include "backward.hpp"
+
+ #include
+#endif
+#ifdef Q_OS_WIN
+ #include
+ //
+ #include
+#endif
+
+namespace Qv2ray
+{
+ class StackTraceHelper
+ {
+ public:
+ static QString GetStackTrace()
+ {
+#ifdef Q_OS_UNIX
+ return GetStackTraceImpl_Unix();
+#elif defined(Q_OS_WIN)
+ return GetStackTraceImpl_Windows();
+#endif
+ }
+
+ private:
+#ifdef Q_OS_UNIX
+ static QString GetStackTraceImpl_Unix();
+#elif defined(Q_OS_WIN)
+ static QString GetStackTraceImpl_Windows();
+#endif
+ };
+} // namespace Qv2ray
diff --git a/src/base/JsonHelpers.hpp b/src/base/JsonHelpers.hpp
index 38114f90..3934b7a0 100644
--- a/src/base/JsonHelpers.hpp
+++ b/src/base/JsonHelpers.hpp
@@ -7,42 +7,13 @@
#define CONCATENATE1(arg1, arg2) CONCATENATE2(arg1, arg2)
#define CONCATENATE2(arg1, arg2) arg1##arg2
-#define EXPAND(x) x
-#define FOR_EACH_1(what, x, ...) what(x)
-
-#define FOR_EACH_2(what, x, ...) \
- what(x); \
- EXPAND(FOR_EACH_1(what, __VA_ARGS__))
-#define FOR_EACH_3(what, x, ...) \
- what(x); \
- EXPAND(FOR_EACH_2(what, __VA_ARGS__))
-#define FOR_EACH_4(what, x, ...) \
- what(x); \
- EXPAND(FOR_EACH_3(what, __VA_ARGS__))
-#define FOR_EACH_5(what, x, ...) \
- what(x); \
- EXPAND(FOR_EACH_4(what, __VA_ARGS__))
-#define FOR_EACH_6(what, x, ...) \
- what(x); \
- EXPAND(FOR_EACH_5(what, __VA_ARGS__))
-#define FOR_EACH_7(what, x, ...) \
- what(x); \
- EXPAND(FOR_EACH_6(what, __VA_ARGS__))
-#define FOR_EACH_8(what, x, ...) \
- what(x); \
- EXPAND(FOR_EACH_7(what, __VA_ARGS__))
-
-#define FOR_EACH_NARG(...) FOR_EACH_NARG_(__VA_ARGS__, FOR_EACH_RSEQ_N())
-#define FOR_EACH_NARG_(...) EXPAND(FOR_EACH_ARG_N(__VA_ARGS__))
-#define FOR_EACH_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N
-#define FOR_EACH_RSEQ_N() 8, 7, 6, 5, 4, 3, 2, 1, 0
-#define CONCATENATE(x, y) x##y
-#define FOR_EACH_(N, what, ...) EXPAND(CONCATENATE(FOR_EACH_, N)(what, __VA_ARGS__))
-#define FOR_EACH(what, ...) FOR_EACH_(FOR_EACH_NARG(__VA_ARGS__), what, __VA_ARGS__)
-#define JADDEx_(jsonObj, field) jsonObj.insert(#field, field);
-#define JADDEx(field) JADDEx_(root, field)
-
// Add key value pair into JSON named 'root'
+#define JADDEx(field) root.insert(#field, field);
#define JADD(...) FOR_EACH(JADDEx, __VA_ARGS__)
-#define RROOT return root;
+#define JAUTOREMOVE(jObj, key) \
+ { \
+ if ((jObj[key].isArray() && jObj[key].toArray().isEmpty()) || (jObj[key].isObject() && jObj[key].toObject().isEmpty()) || \
+ (jObj[key].isString() && jObj[key].toString().isEmpty())) \
+ jObj.remove(key); \
+ }
diff --git a/src/base/Qv2rayBase.hpp b/src/base/Qv2rayBase.hpp
index 55d89ea8..597a9955 100644
--- a/src/base/Qv2rayBase.hpp
+++ b/src/base/Qv2rayBase.hpp
@@ -1,9 +1,7 @@
#pragma once
//
-#include
#include
#include
-#include
#include
#include
#include
@@ -21,8 +19,6 @@
#include "base/models/QvSettingsObject.hpp"
#include "base/models/QvStartupConfig.hpp"
-using namespace std;
-using namespace std::chrono;
using namespace Qv2ray;
using namespace Qv2ray::base;
using namespace Qv2ray::base::safetype;
@@ -31,6 +27,18 @@ using namespace Qv2ray::base::objects;
using namespace Qv2ray::base::objects::protocol;
using namespace Qv2ray::base::objects::transfer;
+class _qv2ray_global_config_impl_details
+{
+ public:
+ static Qv2rayConfigObject _GlobalConfig;
+ static bool _isExiting;
+ static QString _Qv2rayConfigPath;
+};
+
+#define GlobalConfig (_qv2ray_global_config_impl_details::_GlobalConfig)
+#define isExiting (_qv2ray_global_config_impl_details::_isExiting)
+#define Qv2rayConfigPath (_qv2ray_global_config_impl_details::_Qv2rayConfigPath)
+
#define QV2RAY_BUILD_INFO QString(_QV2RAY_BUILD_INFO_STR_)
#define QV2RAY_BUILD_EXTRA_INFO QString(_QV2RAY_BUILD_EXTRA_INFO_STR_)
@@ -42,17 +50,14 @@ using namespace Qv2ray::base::objects::transfer;
#endif
// Get Configured Config Dir Path
-#define QV2RAY_CONFIG_DIR (Qv2ray::Qv2rayConfigPath)
+#define QV2RAY_CONFIG_DIR (Qv2rayConfigPath)
#define QV2RAY_CONFIG_FILE (QV2RAY_CONFIG_DIR + "Qv2ray.conf")
+//
+#define QV2RAY_ROUTING_DIR (QV2RAY_CONFIG_DIR + "rounting/")
#define QV2RAY_CONNECTIONS_DIR (QV2RAY_CONFIG_DIR + "connections/")
-#define QV2RAY_SUBSCRIPTION_DIR (QV2RAY_CONFIG_DIR + "subscriptions/")
+//
#define QV2RAY_PLUGIN_SETTINGS_DIR (QV2RAY_CONFIG_DIR + "plugin_settings/")
-
-// 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")
@@ -85,9 +90,11 @@ using namespace Qv2ray::base::objects::transfer;
// GUI TOOLS
#define RED(obj) \
- auto _temp = obj->palette(); \
- _temp.setColor(QPalette::Text, Qt::red); \
- obj->setPalette(_temp);
+ { \
+ auto _temp = obj->palette(); \
+ _temp.setColor(QPalette::Text, Qt::red); \
+ obj->setPalette(_temp); \
+ }
#define BLACK(obj) obj->setPalette(QWidget::palette());
@@ -97,11 +104,11 @@ using namespace Qv2ray::base::objects::transfer;
#define ACCESS_OPTIONAL_VALUE(obj) (obj.value())
#endif
-#define Q_TRAYICON(name) (QIcon(GlobalConfig.uiConfig.useDarkTrayIcon ? ":/assets/icons/ui_dark/" name : ":/assets/icons/ui_light/" name))
+#define QV2RAY_COLORSCHEME_ROOT_X(flag) ((flag) ? QStringLiteral(":/assets/icons/ui_dark/") : QStringLiteral(":/assets/icons/ui_light/"))
+#define QV2RAY_COLORSCHEME_ROOT QV2RAY_COLORSCHEME_ROOT_X(GlobalConfig.uiConfig.useDarkTheme)
-#define QV2RAY_COLORSCHEME_ROOT \
- ((GlobalConfig.uiConfig.useDarkTheme) ? QStringLiteral(":/assets/icons/ui_dark/") : QStringLiteral(":/assets/icons/ui_light/"))
#define QICON_R(file) QIcon(QV2RAY_COLORSCHEME_ROOT + file)
+#define Q_TRAYICON(name) (QIcon(QV2RAY_COLORSCHEME_ROOT_X(GlobalConfig.uiConfig.useDarkTrayIcon) + name))
#define QSTRN(num) QString::number(num)
@@ -115,48 +122,40 @@ using namespace Qv2ray::base::objects::transfer;
#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 UpgradeSettingsVersion(int fromVersion, int toVersion, QJsonObject root);
-
- // Qv2ray runtime config
- inline bool isExiting = false;
- inline QString Qv2rayConfigPath = "";
- inline base::config::Qv2rayConfig GlobalConfig = base::config::Qv2rayConfig();
- //
- inline void ExitQv2ray()
- {
- isExiting = true;
- QApplication::quit();
- }
-
inline QStringList Qv2rayAssetsPaths(const QString &dirName)
{
// Configuration Path
QStringList list;
list << QV2RAY_CONFIG_DIR + dirName;
+ list << ":/" + dirName;
//
#ifdef Q_OS_LINUX
// Linux platform directories.
+ list << QString("/lib/qv2ray/" + dirName);
+ list << QString("/usr/lib/qv2ray/" + dirName);
+ list << QString("/usr/local/lib/qv2ray/" + dirName);
+ //
list << QString("/usr/share/qv2ray/" + dirName);
list << QString("/usr/local/share/qv2ray/" + dirName);
- list << QStandardPaths::locateAll(QStandardPaths::AppDataLocation, dirName, QStandardPaths::LocateDirectory);
- list << QStandardPaths::locateAll(QStandardPaths::AppConfigLocation, dirName, QStandardPaths::LocateDirectory);
+ // For AppImage?
+ list << QString(QDir(QCoreApplication::applicationDirPath() + "/../share/qv2ray/" + dirName).absolutePath());
+ // For Snap
+ if (qEnvironmentVariableIsSet("SNAP"))
+ {
+ list << QString(qEnvironmentVariable("SNAP") + "/usr/share/qv2ray/" + dirName);
+ }
#elif defined(Q_OS_MAC)
// macOS platform directories.
- list << QDir(QApplication::applicationDirPath() + "/../Resources/" + dirName).absolutePath();
+ list << QDir(QCoreApplication::applicationDirPath() + "/../Resources/" + dirName).absolutePath();
#endif
+ list << QStandardPaths::locateAll(QStandardPaths::AppDataLocation, dirName, QStandardPaths::LocateDirectory);
+ list << QStandardPaths::locateAll(QStandardPaths::AppConfigLocation, dirName, QStandardPaths::LocateDirectory);
// This is the default behavior on Windows
- list << QApplication::applicationDirPath() + "/" + dirName;
+ list << QCoreApplication::applicationDirPath() + "/" + dirName;
list.removeDuplicates();
return list;
- };
+ }
} // namespace Qv2ray
diff --git a/src/base/Qv2rayFeatures.hpp b/src/base/Qv2rayFeatures.hpp
index 2425dc43..2bdf1862 100644
--- a/src/base/Qv2rayFeatures.hpp
+++ b/src/base/Qv2rayFeatures.hpp
@@ -1,9 +1,22 @@
#pragma once
+#include
// Qv2ray build features.
-//
-// Always use libgRPC++ on windows platform.
+
+#ifdef Q_OS_LINUX
+ #define CanHasLibQvb 1
+ #define NativeDarkmode 0
+#elif defined(Q_OS_MAC)
+ #define CanHasLibQvb 1
+ #define NativeDarkmode 0
+#elif defined(Q_OS_WIN)
+ #define CanHasLibQvb 0
+ #define NativeDarkmode 1
+#endif
+
#ifdef BACKEND_LIBQVB
- #ifdef _WIN32
- #error "libQvb is not supported on Windows Platform"
+ #if !QvHasFeature(CanHasLibQvb)
+ #error Qv2ray API backend libQvb is not supported on this platform
#endif
#endif
+
+#define QvHasFeature(feat) ((feat / 1) == 1)
diff --git a/src/base/Qv2rayLog.cpp b/src/base/Qv2rayLog.cpp
index c2bb4f21..4871db38 100644
--- a/src/base/Qv2rayLog.cpp
+++ b/src/base/Qv2rayLog.cpp
@@ -4,6 +4,14 @@
#include
+#ifdef Q_OS_ANDROID
+ #include
+#endif
+
+Qv2rayConfigObject _qv2ray_global_config_impl_details::_GlobalConfig;
+bool _qv2ray_global_config_impl_details::_isExiting;
+QString _qv2ray_global_config_impl_details::_Qv2rayConfigPath;
+
namespace Qv2ray::base
{
// Forwarded from QvTinyLog
@@ -15,7 +23,7 @@ namespace Qv2ray::base
void __QV2RAY_LOG_FUNC__(int type, const std::string &func, int line, const QString &module, const QString &log)
{
auto logString = QString("[" % module % "]: " % log);
- auto funcPrepend = QString::fromStdString(func + ":" + to_string(line) + " ");
+ auto funcPrepend = QString::fromStdString(func + ":" + std::to_string(line) + " ");
#ifdef QT_DEBUG
// Debug build version, we only print info for DEBUG logs and print
@@ -40,7 +48,11 @@ namespace Qv2ray::base
}
}
#endif
- cout << logString.toStdString() << endl;
+#ifdef Q_OS_ANDROID
+ __android_log_write(ANDROID_LOG_INFO, "Qv2ray", logString.toStdString().c_str());
+#else
+ std::cout << logString.toStdString() << std::endl;
+#endif
{
QMutexLocker _(&__loggerMutex);
__loggerBuffer->append(logString + NEWLINE);
diff --git a/src/base/Qv2rayLog.hpp b/src/base/Qv2rayLog.hpp
index c01499c0..61fbc88f 100644
--- a/src/base/Qv2rayLog.hpp
+++ b/src/base/Qv2rayLog.hpp
@@ -1,7 +1,6 @@
#pragma once
#include
-using namespace std;
/*
* Tiny log module.
@@ -18,7 +17,7 @@ namespace Qv2ray::base
#define QV2RAY_LOG_NORMAL 0
#define QV2RAY_LOG_DEBUG 1
-#define __LOG_IMPL(LEVEL, MODULE, MSG) __QV2RAY_LOG_FUNC__(LEVEL, Q_FUNC_INFO, __LINE__, MODULE, MSG);
+#define __LOG_IMPL(LEVEL, MODULE, MSG) ::Qv2ray::base::__QV2RAY_LOG_FUNC__(LEVEL, Q_FUNC_INFO, __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));
diff --git a/src/base/models/CoreObjectModels.hpp b/src/base/models/CoreObjectModels.hpp
index 3c4e5a68..fa87d9b7 100644
--- a/src/base/models/CoreObjectModels.hpp
+++ b/src/base/models/CoreObjectModels.hpp
@@ -1,5 +1,6 @@
#pragma once
-#include "3rdparty/x2struct/x2struct.hpp"
+#include "libs/QJsonStruct/QJsonIO.hpp"
+#include "libs/QJsonStruct/QJsonStruct.hpp"
#include
#include
@@ -7,13 +8,49 @@
namespace Qv2ray::base::objects
{
+ struct DNSObject
+ {
+ struct DNSServerObject
+ {
+ bool QV2RAY_DNS_IS_COMPLEX_DNS;
+ QString address;
+ int port;
+ QList domains;
+ QList expectIPs;
+ DNSServerObject() : QV2RAY_DNS_IS_COMPLEX_DNS(false), port(53){};
+ DNSServerObject(const QString &_address) : DNSServerObject()
+ {
+ address = _address;
+ };
+
+ friend bool operator==(const DNSServerObject &left, const DNSServerObject &right)
+ {
+ return left.QV2RAY_DNS_IS_COMPLEX_DNS == right.QV2RAY_DNS_IS_COMPLEX_DNS && //
+ left.address == right.address && //
+ left.port == right.port && //
+ left.domains == right.domains && //
+ left.expectIPs == right.expectIPs;
+ }
+ JSONSTRUCT_REGISTER(DNSServerObject, F(QV2RAY_DNS_IS_COMPLEX_DNS, address, port, domains, expectIPs))
+ };
+ QMap hosts;
+ QList servers;
+ QString clientIp;
+ QString tag;
+ friend bool operator==(const DNSObject &left, const DNSObject &right)
+ {
+ return left.hosts == right.hosts && left.servers == right.servers && left.clientIp == right.clientIp && left.tag == right.tag;
+ }
+ JSONSTRUCT_REGISTER(DNSObject, F(hosts, servers, clientIp, tag))
+ };
//
// Used in config generation
struct AccountObject
{
QString user;
QString pass;
- XTOSTRUCT(O(user, pass))
+ AccountObject() : user(), pass(){};
+ JSONSTRUCT_REGISTER(AccountObject, F(user, pass))
};
//
//
@@ -21,10 +58,8 @@ namespace Qv2ray::base::objects
{
QString tag;
QList services;
- ApiObject() : tag("api"), services()
- {
- }
- XTOSTRUCT(O(tag, services))
+ ApiObject() : tag("api"), services(){};
+ JSONSTRUCT_REGISTER(ApiObject, F(tag, services))
};
//
//
@@ -32,10 +67,8 @@ namespace Qv2ray::base::objects
{
bool statsInboundUplink;
bool statsInboundDownlink;
- SystemPolicyObject() : statsInboundUplink(), statsInboundDownlink()
- {
- }
- XTOSTRUCT(O(statsInboundUplink, statsInboundDownlink))
+ SystemPolicyObject() : statsInboundUplink(), statsInboundDownlink(){};
+ JSONSTRUCT_REGISTER(SystemPolicyObject, F(statsInboundUplink, statsInboundDownlink))
};
//
//
@@ -48,10 +81,8 @@ namespace Qv2ray::base::objects
bool statsUserUplink;
bool statsUserDownlink;
int bufferSize;
- LevelPolicyObject() : handshake(), connIdle(), uplinkOnly(), downlinkOnly(), statsUserUplink(), statsUserDownlink(), bufferSize()
- {
- }
- XTOSTRUCT(O(handshake, connIdle, uplinkOnly, downlinkOnly, statsUserUplink, statsUserDownlink, bufferSize))
+ LevelPolicyObject() : handshake(), connIdle(), uplinkOnly(), downlinkOnly(), statsUserUplink(), statsUserDownlink(), bufferSize(){};
+ JSONSTRUCT_REGISTER(LevelPolicyObject, F(handshake, connIdle, uplinkOnly, downlinkOnly, statsUserUplink, statsUserDownlink, bufferSize))
};
//
//
@@ -59,10 +90,8 @@ namespace Qv2ray::base::objects
{
QMap level;
QList system;
- PolicyObject() : level(), system()
- {
- }
- XTOSTRUCT(O(level, system))
+ PolicyObject() : level(), system(){};
+ JSONSTRUCT_REGISTER(PolicyObject, F(level, system))
};
//
//
@@ -87,11 +116,9 @@ namespace Qv2ray::base::objects
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))
+ port("1-65535"), network(""), source(), user(), inboundTag(), protocol(), attrs(), outboundTag(""), balancerTag(""){};
+ JSONSTRUCT_REGISTER(RuleObject, F(QV2RAY_RULE_ENABLED, QV2RAY_RULE_USE_BALANCER, QV2RAY_RULE_TAG, type, domain, ip, port, network,
+ source, user, inboundTag, protocol, attrs, outboundTag, balancerTag))
};
//
//
@@ -99,10 +126,8 @@ namespace Qv2ray::base::objects
{
QString tag;
QList selector;
- BalancerObject() : tag(), selector()
- {
- }
- XTOSTRUCT(O(tag, selector))
+ BalancerObject() : tag(), selector(){};
+ JSONSTRUCT_REGISTER(BalancerObject, F(tag, selector))
};
//
//
@@ -114,10 +139,8 @@ namespace Qv2ray::base::objects
QString method;
QList path;
QMap> headers;
- HTTPRequestObject() : version("1.1"), method("GET"), path(), headers()
- {
- }
- XTOSTRUCT(O(version, method, path, headers))
+ HTTPRequestObject() : version("1.1"), method("GET"), path(), headers(){};
+ JSONSTRUCT_REGISTER(HTTPRequestObject, F(version, method, path, headers))
};
//
//
@@ -127,10 +150,8 @@ namespace Qv2ray::base::objects
QString status;
QString reason;
QMap> headers;
- HTTPResponseObject() : version("1.1"), status("200"), reason("OK"), headers()
- {
- }
- XTOSTRUCT(O(version, status, reason, headers))
+ HTTPResponseObject() : version("1.1"), status("200"), reason("OK"), headers(){};
+ JSONSTRUCT_REGISTER(HTTPResponseObject, F(version, status, reason, headers))
};
//
//
@@ -139,30 +160,24 @@ namespace Qv2ray::base::objects
QString type;
HTTPRequestObject request;
HTTPResponseObject response;
- TCPHeader_M_Object() : type("none"), request(), response()
- {
- }
- XTOSTRUCT(O(type, request, response))
+ TCPHeader_M_Object() : type("none"), request(), response(){};
+ JSONSTRUCT_REGISTER(TCPHeader_M_Object, F(type, request, response))
};
//
//
struct HeaderObject
{
QString type;
- HeaderObject() : type("none")
- {
- }
- XTOSTRUCT(O(type))
+ HeaderObject() : type("none"){};
+ JSONSTRUCT_REGISTER(HeaderObject, F(type))
};
//
//
struct TCPObject
{
TCPHeader_M_Object header;
- TCPObject() : header()
- {
- }
- XTOSTRUCT(O(header))
+ TCPObject() : header(){};
+ JSONSTRUCT_REGISTER(TCPObject, F(header))
};
//
//
@@ -175,11 +190,11 @@ namespace Qv2ray::base::objects
bool congestion = false;
int readBufferSize = 1;
int writeBufferSize = 1;
+ QString seed;
HeaderObject header;
- KCPObject() : header()
- {
- }
- XTOSTRUCT(O(mtu, tti, uplinkCapacity, downlinkCapacity, congestion, readBufferSize, writeBufferSize, header))
+ KCPObject() : header(){};
+ JSONSTRUCT_REGISTER(KCPObject,
+ F(mtu, tti, uplinkCapacity, downlinkCapacity, congestion, readBufferSize, writeBufferSize, header, seed))
};
//
//
@@ -187,10 +202,8 @@ namespace Qv2ray::base::objects
{
QString path;
QMap headers;
- WebSocketObject() : path("/"), headers()
- {
- }
- XTOSTRUCT(O(path, headers))
+ WebSocketObject() : path("/"), headers(){};
+ JSONSTRUCT_REGISTER(WebSocketObject, F(path, headers))
};
//
//
@@ -198,20 +211,16 @@ namespace Qv2ray::base::objects
{
QList host;
QString path;
- HttpObject() : host(), path("/")
- {
- }
- XTOSTRUCT(O(host, path))
+ HttpObject() : host(), path("/"){};
+ JSONSTRUCT_REGISTER(HttpObject, F(host, path))
};
//
//
struct DomainSocketObject
{
QString path;
- DomainSocketObject() : path("/")
- {
- }
- XTOSTRUCT(O(path))
+ DomainSocketObject() : path("/"){};
+ JSONSTRUCT_REGISTER(DomainSocketObject, F(path))
};
//
//
@@ -220,10 +229,8 @@ namespace Qv2ray::base::objects
QString security;
QString key;
HeaderObject header;
- QuicObject() : security(""), key(""), header()
- {
- }
- XTOSTRUCT(O(security, key, header))
+ QuicObject() : security(""), key(""), header(){};
+ JSONSTRUCT_REGISTER(QuicObject, F(security, key, header))
};
//
//
@@ -232,10 +239,8 @@ namespace Qv2ray::base::objects
int mark;
bool tcpFastOpen;
QString tproxy;
- SockoptObject() : mark(0), tcpFastOpen(false), tproxy("off")
- {
- }
- XTOSTRUCT(O(mark, tcpFastOpen, tproxy))
+ SockoptObject() : mark(0), tcpFastOpen(false), tproxy("off"){};
+ JSONSTRUCT_REGISTER(SockoptObject, F(mark, tcpFastOpen, tproxy))
};
//
//
@@ -246,10 +251,8 @@ namespace Qv2ray::base::objects
QString keyFile;
QList certificate;
QList key;
- CertificateObject() : usage(), certificateFile(), keyFile(), certificate(), key()
- {
- }
- XTOSTRUCT(O(usage, certificateFile, keyFile, certificate, key))
+ CertificateObject() : usage(), certificateFile(), keyFile(), certificate(), key(){};
+ JSONSTRUCT_REGISTER(CertificateObject, F(usage, certificateFile, keyFile, certificate, key))
};
//
//
@@ -258,13 +261,14 @@ namespace Qv2ray::base::objects
QString serverName;
bool allowInsecure;
bool allowInsecureCiphers;
+ bool disableSessionResumption;
QList alpn;
QList certificates;
bool disableSystemRoot;
- TLSObject() : serverName(), allowInsecure(), allowInsecureCiphers(), certificates(), disableSystemRoot()
- {
- }
- XTOSTRUCT(O(serverName, allowInsecure, allowInsecureCiphers, alpn, certificates, disableSystemRoot))
+ TLSObject()
+ : serverName(), allowInsecure(), allowInsecureCiphers(), disableSessionResumption(true), certificates(), disableSystemRoot(){};
+ JSONSTRUCT_REGISTER(TLSObject, F(serverName, allowInsecure, allowInsecureCiphers, disableSessionResumption, alpn, certificates,
+ disableSystemRoot))
};
} // namespace transfer
//
@@ -273,10 +277,8 @@ namespace Qv2ray::base::objects
{
bool enabled = false;
QList destOverride;
- SniffingObject() : enabled(), destOverride()
- {
- }
- XTOSTRUCT(O(enabled, destOverride))
+ SniffingObject() : enabled(), destOverride(){};
+ JSONSTRUCT_REGISTER(SniffingObject, F(enabled, destOverride))
};
//
//
@@ -294,10 +296,9 @@ namespace Qv2ray::base::objects
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))
+ dsSettings(), quicSettings(){};
+ JSONSTRUCT_REGISTER(StreamSettingsObject, F(network, security, sockopt, tcpSettings, tlsSettings, kcpSettings, wsSettings, httpSettings,
+ dsSettings, quicSettings))
};
//
//
@@ -305,10 +306,8 @@ namespace Qv2ray::base::objects
{
bool enabled;
int concurrency;
- MuxObject() : enabled(), concurrency()
- {
- }
- XTOSTRUCT(O(enabled, concurrency))
+ MuxObject() : enabled(), concurrency(){};
+ JSONSTRUCT_REGISTER(MuxObject, F(enabled, concurrency))
};
//
// Some protocols from: https://v2ray.com/chapter_02/02_protocols.html
@@ -320,10 +319,8 @@ namespace Qv2ray::base::objects
QString network;
QString address;
int port;
- DNSOut() : network(""), address("0.0.0.0"), port(0)
- {
- }
- XTOSTRUCT(O(network, address, port))
+ DNSOut() : network(""), address("0.0.0.0"), port(0){};
+ JSONSTRUCT_REGISTER(DNSOut, F(network, address, port))
};
//
// MTProto, InBound || OutBound
@@ -334,13 +331,11 @@ namespace Qv2ray::base::objects
QString email;
int level;
QString secret;
- UserObject() : email("user@domain.com"), level(0), secret("")
- {
- }
- XTOSTRUCT(O(email, level, secret))
+ UserObject() : email("user@domain.com"), level(0), secret(""){};
+ JSONSTRUCT_REGISTER(UserObject, F(email, level, secret))
};
QList users;
- XTOSTRUCT(O(users))
+ JSONSTRUCT_REGISTER(MTProtoIn, F(users))
};
//
// Socks, OutBound
@@ -351,19 +346,14 @@ namespace Qv2ray::base::objects
QString user;
QString pass;
int level;
- UserObject() : user(), pass(), level(0)
- {
- }
- XTOSTRUCT(O(user, pass, level))
+ UserObject() : user(), pass(), level(0){};
+ JSONSTRUCT_REGISTER(UserObject, F(user, pass, level))
};
-
QString address;
int port;
QList users;
- SocksServerObject() : address("0.0.0.0"), port(0), users()
- {
- }
- XTOSTRUCT(O(address, port, users))
+ SocksServerObject() : address("0.0.0.0"), port(0), users(){};
+ JSONSTRUCT_REGISTER(SocksServerObject, F(address, port, users))
};
//
// VMess Server
@@ -375,19 +365,16 @@ namespace Qv2ray::base::objects
int alterId;
QString security;
int level;
- UserObject() : id(""), alterId(64), security("auto"), level(0)
- {
- }
- XTOSTRUCT(O(id, alterId, security, level))
+ QString testsEnabled;
+ UserObject() : id(), alterId(64), security("auto"), level(0), testsEnabled("none"){};
+ JSONSTRUCT_REGISTER(UserObject, F(id, alterId, security, level, testsEnabled))
};
QString address;
int port;
QList users;
- VMessServerObject() : address(""), port(0), users()
- {
- }
- XTOSTRUCT(O(address, port, users))
+ VMessServerObject() : address(""), port(0), users(){};
+ JSONSTRUCT_REGISTER(VMessServerObject, F(address, port, users))
};
//
// ShadowSocks Server
@@ -401,10 +388,8 @@ namespace Qv2ray::base::objects
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))
+ : email("user@domain.com"), address("0.0.0.0"), method("aes-256-cfb"), password(""), ota(false), level(0), port(0){};
+ JSONSTRUCT_REGISTER(ShadowSocksServerObject, F(email, address, port, method, password, ota, level))
};
} // namespace protocol
} // namespace Qv2ray::base::objects
diff --git a/src/base/models/QvConfigIdentifier.hpp b/src/base/models/QvConfigIdentifier.hpp
index 4137c9d9..dde9622c 100644
--- a/src/base/models/QvConfigIdentifier.hpp
+++ b/src/base/models/QvConfigIdentifier.hpp
@@ -1,52 +1,180 @@
#pragma once
-#include "3rdparty/x2struct/x2struct.hpp"
+#include "QvCoreSettings.hpp"
+#include "libs/QJsonStruct/QJsonStruct.hpp"
+#include
+#include
#include
#include
+
namespace Qv2ray::base
{
- constexpr unsigned int QVTCPING_VALUE_ERROR = 99999;
- constexpr unsigned int QVTCPING_VALUE_NODATA = QVTCPING_VALUE_ERROR - 1;
+ template
+ class IDType
+ {
+ public:
+ explicit IDType() : m_id("null"){};
+ explicit IDType(const QString &id) : m_id(id){};
+ friend bool operator==(const IDType &lhs, const IDType &rhs)
+ {
+ return lhs.m_id == rhs.m_id;
+ }
+ friend bool operator!=(const IDType &lhs, const IDType &rhs)
+ {
+ return lhs.m_id != rhs.m_id;
+ }
+ const QString toString() const
+ {
+ return m_id;
+ }
+ void loadJson(const QJsonValue &d)
+ {
+ m_id = d.toString("null");
+ }
+ QJsonValue toJson() const
+ {
+ return m_id;
+ }
+
+ private:
+ QString m_id;
+ };
+
+ // Define several safetypes to prevent misuse of QString.
+ class __QvGroup;
+ class __QvConnection;
+ class __QvRoute;
+ typedef IDType<__QvGroup> GroupId;
+ typedef IDType<__QvConnection> ConnectionId;
+ typedef IDType<__QvRoute> GroupRoutingId;
+ //
+ inline const static auto NullConnectionId = ConnectionId("null");
+ inline const static auto NullGroupId = GroupId("null");
+ inline const static auto NullRoutingId = GroupRoutingId("null");
+ //
+ class ConnectionGroupPair
+ {
+ public:
+ ConnectionId connectionId = NullConnectionId;
+ GroupId groupId = NullGroupId;
+ ConnectionGroupPair() : connectionId(NullConnectionId), groupId(NullGroupId){};
+ ConnectionGroupPair(const ConnectionId &conn, const GroupId &group) : connectionId(conn), groupId(group){};
+ void clear()
+ {
+ connectionId = NullConnectionId;
+ groupId = NullGroupId;
+ }
+ bool isEmpty() const
+ {
+ return groupId == NullGroupId || connectionId == NullConnectionId;
+ }
+ friend bool operator==(const ConnectionGroupPair &lhs, const ConnectionGroupPair &rhs)
+ {
+ return lhs.groupId == rhs.groupId && lhs.connectionId == rhs.connectionId;
+ }
+ JSONSTRUCT_REGISTER(ConnectionGroupPair, F(connectionId, groupId))
+ };
+ //
+ constexpr unsigned int LATENCY_TEST_VALUE_ERROR = 99999;
+ constexpr unsigned int LATENCY_TEST_VALUE_NODATA = LATENCY_TEST_VALUE_ERROR - 1;
using namespace std::chrono;
- // Common struct for Groups and Subscriptions
- struct GroupObject_Config
+
+ struct __Qv2rayConfigObjectBase
{
QString displayName;
- QList connections;
- int64_t importDate;
- GroupObject_Config() : displayName(), connections(), importDate()
- {
- }
- XTOSTRUCT(O(displayName, connections, importDate))
+ qint64 creationDate;
+ qint64 lastUpdatedDate;
+ __Qv2rayConfigObjectBase()
+ : displayName(), creationDate(system_clock::to_time_t(system_clock::now())), //
+ lastUpdatedDate(system_clock::to_time_t(system_clock::now())){}; //
+ JSONSTRUCT_REGISTER(__Qv2rayConfigObjectBase, F(displayName, creationDate, lastUpdatedDate))
};
- struct SubscriptionObject_Config : GroupObject_Config
+ struct GroupRoutingConfig : __Qv2rayConfigObjectBase
{
+ bool overrideDNS;
+ config::QvConfig_DNS dnsConfig;
//
- QString address;
- int64_t lastUpdated;
- float updateInterval;
- SubscriptionObject_Config() : address(""), lastUpdated(system_clock::to_time_t(system_clock::now())), updateInterval(10)
- {
- }
- XTOSTRUCT(O(lastUpdated, updateInterval, address, connections, displayName, importDate))
+ bool overrideRoute;
+ config::QvConfig_Route routeConfig;
+ //
+ bool overrideConnectionConfig;
+ config::QvConfig_Connection connectionConfig;
+ //
+ bool overrideForwardProxyConfig;
+ config::QvConfig_ForwardProxy forwardProxyConfig;
+ //
+ GroupRoutingConfig()
+ : overrideDNS(false), //
+ overrideRoute(false), //
+ overrideConnectionConfig(false), //
+ overrideForwardProxyConfig(false) //
+ {};
+ JSONSTRUCT_REGISTER(GroupRoutingConfig, //
+ F(overrideRoute, routeConfig), //
+ F(overrideDNS, dnsConfig), //
+ F(overrideConnectionConfig, connectionConfig), //
+ F(overrideForwardProxyConfig, forwardProxyConfig))
};
- struct ConnectionObject_Config
+ enum SubscriptionFilterRelation
{
- QString displayName;
- int64_t importDate;
- int64_t lastConnected;
- int64_t latency;
- int64_t upLinkData;
- int64_t downLinkData;
- ConnectionObject_Config()
- : displayName(), importDate(system_clock::to_time_t(system_clock::now())), lastConnected(), latency(QVTCPING_VALUE_NODATA),
- upLinkData(0), downLinkData(0)
- {
- }
- XTOSTRUCT(O(displayName, importDate, lastConnected, latency, upLinkData, downLinkData))
+ RELATION_AND = 0,
+ RELATION_OR = 1
};
+
+ struct SubscriptionConfigObject
+ {
+ QString address;
+ float updateInterval;
+ SubscriptionFilterRelation IncludeRelation;
+ SubscriptionFilterRelation ExcludeRelation;
+ QList IncludeKeywords;
+ QList ExcludeKeywords;
+ SubscriptionConfigObject()
+ : address(""), updateInterval(10), //
+ IncludeRelation(RELATION_OR), ExcludeRelation(RELATION_AND), //
+ IncludeKeywords(), ExcludeKeywords(){};
+ JSONSTRUCT_REGISTER(SubscriptionConfigObject,
+ F(updateInterval, address, IncludeRelation, ExcludeRelation, IncludeKeywords, ExcludeKeywords))
+ };
+
+ struct GroupObject : __Qv2rayConfigObjectBase
+ {
+ QList connections;
+ bool isSubscription;
+ GroupRoutingId routeConfigId;
+ SubscriptionConfigObject subscriptionOption;
+ GroupObject() : __Qv2rayConfigObjectBase(), connections(), isSubscription(false), subscriptionOption(){};
+ JSONSTRUCT_REGISTER(GroupObject, F(connections, isSubscription, routeConfigId, subscriptionOption), B(__Qv2rayConfigObjectBase))
+ };
+
+ struct ConnectionObject : __Qv2rayConfigObjectBase
+ {
+ qint64 lastConnected;
+ qint64 latency;
+ qint64 upLinkData;
+ qint64 downLinkData;
+ //
+ int __qvConnectionRefCount;
+ //
+ ConnectionObject() : lastConnected(), latency(LATENCY_TEST_VALUE_NODATA), upLinkData(0), downLinkData(0), __qvConnectionRefCount(0){};
+ JSONSTRUCT_REGISTER(ConnectionObject, F(lastConnected, latency, upLinkData, downLinkData), B(__Qv2rayConfigObjectBase))
+ };
+
+ template
+ inline uint qHash(IDType key)
+ {
+ return ::qHash(key.toString());
+ }
+ inline uint qHash(const Qv2ray::base::ConnectionGroupPair &pair)
+ {
+ return ::qHash(pair.connectionId.toString() + pair.groupId.toString());
+ }
} // namespace Qv2ray::base
using namespace Qv2ray::base;
+Q_DECLARE_METATYPE(ConnectionGroupPair)
+Q_DECLARE_METATYPE(ConnectionId)
+Q_DECLARE_METATYPE(GroupId)
+Q_DECLARE_METATYPE(GroupRoutingId)
diff --git a/src/base/models/QvCoreSettings.hpp b/src/base/models/QvCoreSettings.hpp
new file mode 100644
index 00000000..86e3e64c
--- /dev/null
+++ b/src/base/models/QvCoreSettings.hpp
@@ -0,0 +1,142 @@
+#pragma once
+#include "base/models/CoreObjectModels.hpp"
+#include "libs/QJsonStruct/QJsonStruct.hpp"
+namespace Qv2ray::base::config
+{
+ struct QvConfig_Route
+ {
+ struct QvRouteConfig_Impl
+ {
+ QList direct;
+ QList block;
+ QList proxy;
+ QvRouteConfig_Impl(){};
+ friend bool operator==(const QvRouteConfig_Impl &left, const QvRouteConfig_Impl &right)
+ {
+ return left.direct == right.direct && left.block == right.block && left.proxy == right.proxy;
+ }
+ QvRouteConfig_Impl(const QList &_direct, const QList &_block, const QList &_proxy)
+ : direct(_direct), //
+ block(_block), //
+ proxy(_proxy){};
+ JSONSTRUCT_REGISTER(QvRouteConfig_Impl, F(proxy, block, direct))
+ };
+ QString domainStrategy;
+ QvRouteConfig_Impl domains;
+ QvRouteConfig_Impl ips;
+ friend bool operator==(const QvConfig_Route &left, const QvConfig_Route &right)
+ {
+ return left.domainStrategy == right.domainStrategy && left.domains == right.domains && left.ips == right.ips;
+ }
+ QvConfig_Route(){};
+ QvConfig_Route(const QvRouteConfig_Impl &_domains, const QvRouteConfig_Impl &_ips, const QString &ds)
+ : domainStrategy(ds), //
+ domains(_domains), //
+ ips(_ips){};
+ JSONSTRUCT_REGISTER(QvConfig_Route, F(domainStrategy, domains, ips))
+ };
+
+ using QvConfig_DNS = objects::DNSObject;
+
+ struct QvConfig_Outbounds
+ {
+ int mark;
+ QvConfig_Outbounds() : mark(255){};
+ JSONSTRUCT_REGISTER(QvConfig_Outbounds, F(mark))
+ };
+
+ struct QvConfig_ForwardProxy
+ {
+ bool enableForwardProxy;
+ QString type;
+ QString serverAddress;
+ int port;
+ bool useAuth;
+ QString username;
+ QString password;
+ QvConfig_ForwardProxy()
+ : enableForwardProxy(false), type("http"), serverAddress("127.0.0.1"), port(8008), useAuth(false), username(), password(){};
+ JSONSTRUCT_REGISTER(QvConfig_ForwardProxy, F(enableForwardProxy, type, serverAddress, port, useAuth, username, password))
+ };
+
+ struct QvConfig_Connection
+ {
+ bool enableProxy;
+ bool bypassCN;
+ bool bypassBT;
+ bool v2rayFreedomDNS;
+ bool withLocalDNS;
+ QvConfig_Connection() : enableProxy(true), bypassCN(true), bypassBT(false), v2rayFreedomDNS(false), withLocalDNS(false){};
+ JSONSTRUCT_REGISTER(QvConfig_Connection, F(bypassCN, bypassBT, enableProxy, v2rayFreedomDNS, withLocalDNS))
+ };
+
+ struct QvConfig_SystemProxy
+ {
+ bool setSystemProxy;
+ QvConfig_SystemProxy() : setSystemProxy(true){};
+ JSONSTRUCT_REGISTER(QvConfig_SystemProxy, F(setSystemProxy))
+ };
+
+ struct __Qv2rayConfig_ProtocolInboundBase
+ {
+ int port;
+ bool useAuth;
+ bool sniffing;
+ objects::AccountObject account;
+ __Qv2rayConfig_ProtocolInboundBase(int _port = 0) : port(_port), useAuth(false), sniffing(false), account(){};
+ JSONSTRUCT_REGISTER(__Qv2rayConfig_ProtocolInboundBase, F(port, useAuth, sniffing, account))
+ };
+
+ struct QvConfig_SocksInbound : __Qv2rayConfig_ProtocolInboundBase
+ {
+ bool enableUDP;
+ QString localIP;
+ QvConfig_SocksInbound() : __Qv2rayConfig_ProtocolInboundBase(1089), enableUDP(true), localIP("127.0.0.1"){};
+ JSONSTRUCT_REGISTER(QvConfig_SocksInbound, B(__Qv2rayConfig_ProtocolInboundBase), F(enableUDP, localIP))
+ };
+
+ struct QvConfig_HttpInbound : __Qv2rayConfig_ProtocolInboundBase
+ {
+ QvConfig_HttpInbound() : __Qv2rayConfig_ProtocolInboundBase(8889){};
+ JSONSTRUCT_REGISTER(QvConfig_HttpInbound, B(__Qv2rayConfig_ProtocolInboundBase))
+ };
+
+ struct QvConfig_TProxy
+ {
+ QString tProxyIP;
+ QString tProxyV6IP;
+ int port;
+ bool hasTCP;
+ bool hasUDP;
+ QString mode;
+ bool dnsIntercept;
+ QvConfig_TProxy()
+ : tProxyIP("127.0.0.1"), //
+ tProxyV6IP(""), //
+ port(12345), //
+ hasTCP(true), //
+ hasUDP(false), //
+ mode("tproxy"), //
+ dnsIntercept(true) //
+ {};
+ JSONSTRUCT_REGISTER(QvConfig_TProxy, F(tProxyIP, tProxyV6IP, port, hasTCP, hasUDP, mode, dnsIntercept))
+ };
+
+ struct QvConfig_Inbounds
+ {
+ QString listenip;
+ bool useSocks;
+ bool useHTTP;
+ bool useTPROXY;
+ //
+ QvConfig_TProxy tProxySettings;
+ QvConfig_HttpInbound httpSettings;
+ QvConfig_SocksInbound socksSettings;
+ QvConfig_SystemProxy systemProxySettings;
+ QvConfig_Inbounds() : listenip("127.0.0.1"), useSocks(true), useHTTP(true), useTPROXY(false){};
+
+ JSONSTRUCT_REGISTER(QvConfig_Inbounds, //
+ F(listenip, useSocks, useHTTP, useTPROXY), //
+ F(tProxySettings, httpSettings, socksSettings, systemProxySettings))
+ };
+} // namespace Qv2ray::base::config
diff --git a/src/base/models/QvRuntimeConfig.hpp b/src/base/models/QvRuntimeConfig.hpp
index d1dd033c..f572f88b 100644
--- a/src/base/models/QvRuntimeConfig.hpp
+++ b/src/base/models/QvRuntimeConfig.hpp
@@ -8,6 +8,7 @@ namespace Qv2ray::base
struct Qv2rayRuntimeConfig
{
bool screenShotHideQv2ray = false;
+ bool deepinHorribleProxyHint = false;
};
inline base::Qv2rayRuntimeConfig RuntimeConfig = base::Qv2rayRuntimeConfig();
} // namespace Qv2ray::base
diff --git a/src/base/models/QvSafeType.hpp b/src/base/models/QvSafeType.hpp
index adf17f6d..4298e503 100644
--- a/src/base/models/QvSafeType.hpp
+++ b/src/base/models/QvSafeType.hpp
@@ -21,7 +21,6 @@
#define nothing
#define SAFE_TYPEDEF(Base, name) SAFE_TYPEDEF_EXTRA(Base, name, nothing)
-using namespace std;
namespace Qv2ray::base::safetype
{
// To prevent anonying QJsonObject misuse
diff --git a/src/base/models/QvSettingsObject.hpp b/src/base/models/QvSettingsObject.hpp
index 03dc86a5..4ebbe421 100644
--- a/src/base/models/QvSettingsObject.hpp
+++ b/src/base/models/QvSettingsObject.hpp
@@ -1,210 +1,54 @@
#pragma once
-#include "3rdparty/x2struct/x2struct.hpp"
-#include "base/models/CoreObjectModels.hpp"
#include "base/models/QvConfigIdentifier.hpp"
+#include "base/models/QvCoreSettings.hpp"
#include
-const int QV2RAY_CONFIG_VERSION = 11;
+constexpr int QV2RAY_CONFIG_VERSION = 14;
namespace Qv2ray::base::config
{
- struct QvBarLine
- {
- QString Family;
- bool Bold, Italic;
- int ColorA, ColorR, ColorG, ColorB;
- int ContentType;
- double Size;
- QString Message;
- QvBarLine()
- : Family("Consolas"), Bold(true), Italic(false), ColorA(255), ColorR(255), ColorG(255), ColorB(255), ContentType(0), Size(9),
- Message("")
- {
- }
- XTOSTRUCT(O(Bold, Italic, ColorA, ColorR, ColorG, ColorB, Size, Family, Message, ContentType))
- };
-
- struct QvBarPage
- {
- int OffsetYpx;
- QList Lines;
- QvBarPage() : OffsetYpx(5)
- {
- }
- XTOSTRUCT(O(OffsetYpx, Lines))
- };
-
- struct Qv2rayToolBarConfig
- {
- QList Pages;
- XTOSTRUCT(O(Pages))
- };
-
- struct Qv2rayForwardProxyConfig
- {
- bool enableForwardProxy;
- QString type;
- QString serverAddress;
- int port;
- bool useAuth;
- QString username;
- QString password;
- Qv2rayForwardProxyConfig()
- : enableForwardProxy(false), type("http"), serverAddress("127.0.0.1"), port(8008), useAuth(false), username(), password()
- {
- }
- XTOSTRUCT(O(enableForwardProxy, type, serverAddress, port, useAuth, username, password))
- };
-
- struct Qv2rayInboundsConfig
- {
- QString listenip;
- bool setSystemProxy;
-
- // SOCKS
- bool useSocks;
- int socks_port;
- bool socks_useAuth;
- bool socksUDP;
- QString socksLocalIP;
- objects::AccountObject socksAccount;
- // HTTP
- bool useHTTP;
- int http_port;
- bool http_useAuth;
- objects::AccountObject httpAccount;
-
- // dokodemo-door transparent proxy
- bool useTPROXY;
- QString tproxy_ip;
- int tproxy_port;
- bool tproxy_use_tcp;
- bool tproxy_use_udp;
- bool tproxy_followRedirect;
- /*redirect or tproxy way, and tproxy need cap_net_admin*/
- QString tproxy_mode;
- bool dnsIntercept;
-
- Qv2rayInboundsConfig()
- : listenip("127.0.0.1"), setSystemProxy(true), useSocks(true), socks_port(1088), socks_useAuth(false), socksUDP(true),
- socksLocalIP("127.0.0.1"), socksAccount(), useHTTP(true), http_port(8888), http_useAuth(false), httpAccount(), useTPROXY(false),
- tproxy_ip("127.0.0.1"), tproxy_port(12345), tproxy_use_tcp(true), tproxy_use_udp(false), tproxy_followRedirect(true),
- tproxy_mode("tproxy"), dnsIntercept(true)
- {
- }
-
- XTOSTRUCT(O(setSystemProxy, listenip, useSocks, useHTTP, socks_port, socks_useAuth, socksAccount, socksUDP, socksLocalIP, http_port,
- http_useAuth, httpAccount, useTPROXY, tproxy_ip, tproxy_port, tproxy_use_tcp, tproxy_use_udp, tproxy_followRedirect,
- tproxy_mode, dnsIntercept))
- };
-
- struct Qv2rayOutboundsConfig
- {
- int mark;
- Qv2rayOutboundsConfig() : mark(255)
- {
- }
- XTOSTRUCT(O(mark))
- };
-
- struct Qv2rayUIConfig
+ struct Qv2rayConfig_UI
{
QString theme;
QString language;
- QList recentConnections;
+ QList recentConnections;
bool quietMode;
bool useDarkTheme;
bool useDarkTrayIcon;
int maximumLogLines;
int maxJumpListCount;
- Qv2rayUIConfig()
- : theme("Fusion"), language("en_US"), useDarkTheme(false), useDarkTrayIcon(true), maximumLogLines(500), maxJumpListCount(20)
- {
- }
- XTOSTRUCT(O(theme, language, quietMode, useDarkTheme, useDarkTrayIcon, maximumLogLines, maxJumpListCount, recentConnections))
+ Qv2rayConfig_UI()
+ : theme("Fusion"), language("en_US"), useDarkTheme(false), useDarkTrayIcon(true), maximumLogLines(500), maxJumpListCount(20){};
+ JSONSTRUCT_REGISTER(Qv2rayConfig_UI,
+ F(theme, language, quietMode, useDarkTheme, useDarkTrayIcon, maximumLogLines, maxJumpListCount, recentConnections))
};
-
- struct Qv2rayRouteConfig_Impl
- {
- QList direct;
- QList block;
- QList proxy;
- Qv2rayRouteConfig_Impl(){};
- friend bool operator==(const Qv2rayRouteConfig_Impl &left, const Qv2rayRouteConfig_Impl &right)
- {
- return left.direct == right.direct && left.block == right.block && left.proxy == left.proxy;
- }
- Qv2rayRouteConfig_Impl(const QList &_direct, const QList &_block, const QList &_proxy)
- : direct(_direct), block(_block), proxy(_proxy){};
- XTOSTRUCT(O(proxy, block, direct))
- };
-
- struct Qv2rayRouteConfig
- {
- QString domainStrategy;
- Qv2rayRouteConfig_Impl domains;
- Qv2rayRouteConfig_Impl ips;
- friend bool operator==(const Qv2rayRouteConfig &left, const Qv2rayRouteConfig &right)
- {
- return left.domainStrategy == right.domainStrategy && left.domains == right.domains && left.ips == right.ips;
- }
- Qv2rayRouteConfig(){};
- Qv2rayRouteConfig(const Qv2rayRouteConfig_Impl &_domains, const Qv2rayRouteConfig_Impl &_ips, const QString &ds)
- : domainStrategy(ds), domains(_domains), ips(_ips){};
- XTOSTRUCT(O(domainStrategy, domains, ips))
- };
-
- struct Qv2rayPluginConfig
+ struct Qv2rayConfig_Plugin
{
QMap pluginStates;
bool v2rayIntegration;
int portAllocationStart;
- Qv2rayPluginConfig() : pluginStates(), v2rayIntegration(true), portAllocationStart(15000){};
- XTOSTRUCT(O(pluginStates, v2rayIntegration))
+ Qv2rayConfig_Plugin() : pluginStates(), v2rayIntegration(true), portAllocationStart(15000){};
+ JSONSTRUCT_REGISTER(Qv2rayConfig_Plugin, F(pluginStates, v2rayIntegration, portAllocationStart))
};
- struct Qv2rayConnectionConfig
- {
- bool bypassCN;
- bool enableProxy;
- bool v2rayFreedomDNS;
- bool withLocalDNS;
- Qv2rayRouteConfig routeConfig;
- QList dnsList;
- Qv2rayForwardProxyConfig forwardProxyConfig;
- Qv2rayConnectionConfig()
- : bypassCN(true), enableProxy(true), v2rayFreedomDNS(false), withLocalDNS(false), routeConfig(),
- dnsList(QStringList{ "8.8.4.4", "1.1.1.1" })
- {
- }
- XTOSTRUCT(O(bypassCN, enableProxy, v2rayFreedomDNS, withLocalDNS, dnsList, forwardProxyConfig, routeConfig))
- };
-
- struct Qv2rayAPIConfig
+ struct Qv2rayConfig_Kernel
{
bool enableAPI;
int statsPort;
- Qv2rayAPIConfig() : enableAPI(true), statsPort(15490)
- {
- }
- XTOSTRUCT(O(enableAPI, statsPort))
- };
-
- struct Qv2rayKernelConfig
- {
+ //
QString v2CorePath_linux;
QString v2AssetsPath_linux;
QString v2CorePath_macx;
QString v2AssetsPath_macx;
QString v2CorePath_win;
- QString v2AssetsPath_win; //
- Qv2rayKernelConfig()
- : v2CorePath_linux(), v2AssetsPath_linux(), //
+ QString v2AssetsPath_win;
+ Qv2rayConfig_Kernel()
+ : enableAPI(true), statsPort(15490), //
+ v2CorePath_linux(), v2AssetsPath_linux(), //
v2CorePath_macx(), v2AssetsPath_macx(), //
v2CorePath_win(), v2AssetsPath_win() //
- {
- }
+ {};
//
#ifdef Q_OS_LINUX
#define _VARNAME_VCOREPATH_ v2CorePath_linux
@@ -229,10 +73,14 @@ namespace Qv2ray::base::config
#undef _VARNAME_VCOREPATH_
#undef _VARNAME_VASSETSPATH_
- XTOSTRUCT(O(v2CorePath_linux, v2AssetsPath_linux, v2CorePath_macx, v2AssetsPath_macx, v2CorePath_win, v2AssetsPath_win))
+ JSONSTRUCT_REGISTER(Qv2rayConfig_Kernel, //
+ F(enableAPI, statsPort), //
+ F(v2CorePath_linux, v2AssetsPath_linux), //
+ F(v2CorePath_macx, v2AssetsPath_macx), //
+ F(v2CorePath_win, v2AssetsPath_win))
};
- struct Qv2rayUpdateConfig
+ struct Qv2rayConfig_Update
{
QString ignoredVersion;
///
@@ -240,105 +88,96 @@ namespace Qv2ray::base::config
/// 0: Stable
/// 1: Testing
int updateChannel;
- XTOSTRUCT(O(ignoredVersion, updateChannel))
+ JSONSTRUCT_REGISTER(Qv2rayConfig_Update, F(ignoredVersion, updateChannel))
};
- struct Qv2rayAdvancedConfig
+ struct Qv2rayConfig_Advanced
{
bool setAllowInsecure;
- bool setAllowInsecureCiphers;
+ bool setSessionResumption;
bool testLatencyPeriodcally;
- XTOSTRUCT(O(setAllowInsecure, setAllowInsecureCiphers, testLatencyPeriodcally))
+ JSONSTRUCT_REGISTER(Qv2rayConfig_Advanced, F(setAllowInsecure, setSessionResumption, testLatencyPeriodcally))
};
- struct Qv2rayNetworkConfig
+ enum Qv2rayLatencyTestingMethod
{
- enum Qv2rayProxyType
+ TCPING,
+ ICMPING
+ };
+
+ struct Qv2rayConfig_Network
+ {
+ Qv2rayLatencyTestingMethod latencyTestingMethod;
+ enum Qv2rayProxyType : int
{
- QVPROXY_NONE,
- QVPROXY_SYSTEM,
- QVPROXY_CUSTOM
+ QVPROXY_NONE = 0,
+ QVPROXY_SYSTEM = 1,
+ QVPROXY_CUSTOM = 2
} proxyType;
QString address;
QString type;
int port;
QString userAgent;
- Qv2rayNetworkConfig()
+ Qv2rayConfig_Network()
: proxyType(QVPROXY_NONE), //
address("127.0.0.1"), //
type("http"), //
port(8000), //
userAgent("Qv2ray/$VERSION WebRequestHelper"){};
- XTOSTRUCT(O(proxyType, type, address, port, userAgent))
+ JSONSTRUCT_REGISTER(Qv2rayConfig_Network, F(latencyTestingMethod, proxyType, type, address, port, userAgent))
};
- struct Qv2rayConfig
+ enum Qv2rayAutoConnectionBehavior
+ {
+ AUTO_CONNECTION_NONE = 0,
+ AUTO_CONNECTION_FIXED = 1,
+ AUTO_CONNECTION_LAST_CONNECTED = 2
+ };
+
+ struct Qv2rayConfigObject
{
int config_version;
bool tProxySupport;
int logLevel;
//
- QString autoStartId;
+ ConnectionGroupPair autoStartId;
+ ConnectionGroupPair lastConnectedId;
+ Qv2rayAutoConnectionBehavior autoStartBehavior;
//
// Key = groupId, connectionId
- QMap groups;
- QMap subscriptions;
- /// Connections are used privately.
- QMap connections;
+ // QList groups;
+ // QList connections;
//
- Qv2rayUIConfig uiConfig;
- Qv2rayAPIConfig apiConfig;
- Qv2rayPluginConfig pluginConfig;
- Qv2rayKernelConfig kernelConfig;
- Qv2rayUpdateConfig updateConfig;
- Qv2rayNetworkConfig networkConfig;
- Qv2rayToolBarConfig toolBarConfig;
- Qv2rayInboundsConfig inboundConfig;
- Qv2rayOutboundsConfig outboundConfig;
- Qv2rayAdvancedConfig advancedConfig;
- Qv2rayConnectionConfig connectionConfig;
+ Qv2rayConfig_UI uiConfig;
+ Qv2rayConfig_Plugin pluginConfig;
+ Qv2rayConfig_Kernel kernelConfig;
+ Qv2rayConfig_Update updateConfig;
+ Qv2rayConfig_Network networkConfig;
+ QvConfig_Inbounds inboundConfig;
+ QvConfig_Outbounds outboundConfig;
+ Qv2rayConfig_Advanced advancedConfig;
+ GroupRoutingConfig defaultRouteConfig;
- Qv2rayConfig()
+ Qv2rayConfigObject()
: config_version(QV2RAY_CONFIG_VERSION), //
tProxySupport(false), //
logLevel(), //
- autoStartId("null"), //
- groups(), //
- subscriptions(), //
- connections(), //
+ autoStartId(), //
+ autoStartBehavior(), //
uiConfig(), //
- apiConfig(), //
pluginConfig(), //
kernelConfig(), //
updateConfig(), //
networkConfig(), //
- toolBarConfig(), //
inboundConfig(), //
outboundConfig(), //
advancedConfig(), //
- connectionConfig()
- {
- }
+ defaultRouteConfig(){};
- XTOSTRUCT(O(config_version, //
- tProxySupport, //
- logLevel, //
- uiConfig, //
- pluginConfig, //
- updateConfig, //
- kernelConfig, //
- networkConfig, //
- groups, //
- connections, //
- subscriptions, //
- autoStartId, //
- inboundConfig, //
- outboundConfig, //
- connectionConfig, //
- toolBarConfig, //
- advancedConfig, //
- apiConfig //
- ))
+ JSONSTRUCT_REGISTER(Qv2rayConfigObject, //
+ F(config_version, tProxySupport, autoStartId, lastConnectedId, autoStartBehavior, logLevel), //
+ F(uiConfig, advancedConfig, pluginConfig, updateConfig, kernelConfig, networkConfig), //
+ F(inboundConfig, outboundConfig, defaultRouteConfig))
};
} // namespace Qv2ray::base::config
diff --git a/src/base/models/QvStartupConfig.hpp b/src/base/models/QvStartupConfig.hpp
index 8f88f79f..6c0e4839 100644
--- a/src/base/models/QvStartupConfig.hpp
+++ b/src/base/models/QvStartupConfig.hpp
@@ -1,24 +1,26 @@
#pragma once
-namespace Qv2ray
+namespace Qv2ray::base
{
- namespace base
+ struct QvStartupOptions
{
- 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;
- /// Disable Qt scale factors support.
- bool noScaleFactors;
- /// Disable all plugin features.
- bool noPlugins;
- };
- } // namespace base
- inline base::QvStartupOptions StartupOption = base::QvStartupOptions();
-} // namespace Qv2ray
+ /// No API subsystem
+ bool noAPI;
+
+ /// Enable Debug Log.
+ bool debugLog;
+
+ /// Disable Qt scale factors support.
+ bool noScaleFactor;
+
+ /// Disable all plugin features.
+ bool noPlugins;
+
+ /// Exit existing Qv2ray instance
+ bool exitQv2ray;
+
+ ///
+ };
+} // namespace Qv2ray::base
+
+inline Qv2ray::base::QvStartupOptions StartupOption = Qv2ray::base::QvStartupOptions();
diff --git a/src/common/CommandArgs.cpp b/src/common/CommandArgs.cpp
deleted file mode 100644
index 51f24736..00000000
--- a/src/common/CommandArgs.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-#include "CommandArgs.hpp"
-
-#include "base/Qv2rayBase.hpp"
-
-namespace Qv2ray::common
-{
- QvCommandArgParser::QvCommandArgParser()
- : QObject(), noAPIOption("noAPI", tr("Disable gRPC API subsystems.")), //
- runAsRootOption("I-just-wanna-run-with-root", tr("Explicitly run Qv2ray as root.")), //
- debugOption("debug", tr("Enable Debug Output")), //
- noScaleFactorOption("noScaleFactor", tr("Disable manually set QT_SCALE_FACTOR")), //
- noPluginsOption("noPlugin", tr("Disable plugin feature")), //
- withToolbarOption("withToolbarPlugin", 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(noScaleFactorOption);
- parser.addOption(noPluginsOption);
- 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(noScaleFactorOption))
- {
- DEBUG(MODULE_INIT, "noScaleFactorOption is set.")
- StartupOption.noScaleFactors = true;
- }
-
- if (parser.isSet(noPluginsOption))
- {
- DEBUG(MODULE_INIT, "noPluginOption is set.")
- StartupOption.noPlugins = true;
- }
-
- if (parser.isSet(withToolbarOption))
- {
- DEBUG(MODULE_INIT, "withToolbarOption is set.")
- StartupOption.enableToolbarPlguin = true;
- }
-
- return CommandLineOk;
- }
-
-} // namespace Qv2ray::common
diff --git a/src/common/CommandArgs.hpp b/src/common/CommandArgs.hpp
deleted file mode 100644
index dbab70cc..00000000
--- a/src/common/CommandArgs.hpp
+++ /dev/null
@@ -1,38 +0,0 @@
-#pragma once
-
-#include
-
-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 noScaleFactorOption;
- QCommandLineOption noPluginsOption;
- QCommandLineOption withToolbarOption;
- QCommandLineOption helpOption;
- QCommandLineOption versionOption;
- };
-} // namespace Qv2ray::common
-
-using namespace Qv2ray::common;
diff --git a/src/common/HTTPRequestHelper.cpp b/src/common/HTTPRequestHelper.cpp
index bb37f48a..42f103c2 100644
--- a/src/common/HTTPRequestHelper.cpp
+++ b/src/common/HTTPRequestHelper.cpp
@@ -5,39 +5,30 @@
#include
#include
-namespace Qv2ray::common
+namespace Qv2ray::common::network
{
- QvHttpRequestHelper::QvHttpRequestHelper(QObject *parent) : QObject(parent), reply()
- {
- }
-
- QvHttpRequestHelper::~QvHttpRequestHelper()
- {
- accessManager.disconnect();
- }
-
- void QvHttpRequestHelper::setHeader(const QByteArray &key, const QByteArray &value)
+ void NetworkRequestHelper::setHeader(QNetworkRequest &request, const QByteArray &key, const QByteArray &value)
{
DEBUG(MODULE_NETWORK, "Adding HTTP request header: " + key + ":" + value)
request.setRawHeader(key, value);
}
- void QvHttpRequestHelper::setAccessManagerAttributes(QNetworkAccessManager &accessManager)
+ void NetworkRequestHelper::setAccessManagerAttributes(QNetworkRequest &request, QNetworkAccessManager &accessManager)
{
switch (GlobalConfig.networkConfig.proxyType)
{
- case Qv2rayNetworkConfig::QVPROXY_NONE:
+ case Qv2rayConfig_Network::QVPROXY_NONE:
{
DEBUG(MODULE_NETWORK, "Get without proxy.")
accessManager.setProxy(QNetworkProxy(QNetworkProxy::ProxyType::NoProxy));
break;
}
- case Qv2rayNetworkConfig::QVPROXY_SYSTEM:
+ case Qv2rayConfig_Network::QVPROXY_SYSTEM:
{
accessManager.setProxy(QNetworkProxyFactory::systemProxyForQuery().first());
break;
}
- case Qv2rayNetworkConfig::QVPROXY_CUSTOM:
+ case Qv2rayConfig_Network::QVPROXY_CUSTOM:
{
QNetworkProxy p{
GlobalConfig.networkConfig.type == "http" ? QNetworkProxy::HttpProxy : QNetworkProxy::Socks5Proxy, //
@@ -47,6 +38,7 @@ namespace Qv2ray::common
accessManager.setProxy(p);
break;
}
+ default: Q_UNREACHABLE();
}
if (accessManager.proxy().type() == QNetworkProxy::Socks5Proxy)
@@ -56,59 +48,60 @@ namespace Qv2ray::common
}
request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
- request.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, true);
+
+#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
+ // request.setAttribute(QNetworkRequest::Http2AllowedAttribute, true);
+#else
+ // request.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, true);
+#endif
+
auto ua = GlobalConfig.networkConfig.userAgent;
ua.replace("$VERSION", QV2RAY_VERSION_STRING);
request.setHeader(QNetworkRequest::KnownHeaders::UserAgentHeader, ua);
}
- QByteArray QvHttpRequestHelper::Get(const QString &url)
+ QByteArray NetworkRequestHelper::HttpGet(const QUrl &url)
{
- request.setUrl({ url });
- setAccessManagerAttributes(accessManager);
+ QNetworkRequest request;
+ QNetworkAccessManager accessManager;
+ request.setUrl(url);
+ setAccessManagerAttributes(request, accessManager);
auto _reply = accessManager.get(request);
//
- QEventLoop loop;
- connect(_reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
- loop.exec();
+ {
+ QEventLoop loop;
+ QObject::connect(&accessManager, &QNetworkAccessManager::finished, &loop, &QEventLoop::quit);
+ loop.exec();
+ }
//
// Data or timeout?
+ LOG(MODULE_NETWORK, _reply->errorString());
auto data = _reply->readAll();
return data;
}
- void QvHttpRequestHelper::AsyncGet(const QString &url)
+ void NetworkRequestHelper::AsyncHttpGet(const QString &url, std::function funcPtr)
{
- request.setUrl({ url });
- setAccessManagerAttributes(accessManager);
- reply = accessManager.get(request);
- connect(reply, &QNetworkReply::finished, this, &QvHttpRequestHelper::onRequestFinished_p);
- connect(reply, &QNetworkReply::readyRead, this, &QvHttpRequestHelper::onReadyRead_p);
+ QNetworkRequest request;
+ request.setUrl(url);
+ setAccessManagerAttributes(request, accessManager);
+ auto reply = accessManager.get(request);
+ QObject::connect(reply, &QNetworkReply::finished, [=]() {
+ {
+#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
+ bool h2Used = reply->attribute(QNetworkRequest::Http2WasUsedAttribute).toBool();
+#else
+ bool h2Used = reply->attribute(QNetworkRequest::HTTP2WasUsedAttribute).toBool();
+#endif
+ if (h2Used)
+ DEBUG(MODULE_NETWORK, "HTTP/2 was used.")
+
+ if (reply->error() != QNetworkReply::NoError)
+ LOG(MODULE_NETWORK, "Network error: " + QString(QMetaEnum::fromType().key(reply->error())))
+
+ funcPtr(reply->readAll());
+ }
+ });
}
- void QvHttpRequestHelper::onRequestFinished_p()
- {
- if (reply->attribute(QNetworkRequest::HTTP2WasUsedAttribute).toBool())
- {
- DEBUG(MODULE_NETWORK, "HTTP/2 was used.")
- }
-
- if (reply->error() != QNetworkReply::NoError)
- {
- QString error = QMetaEnum::fromType().key(reply->error());
- LOG(MODULE_NETWORK, "Network request error string: " + error)
- QByteArray empty;
- emit OnRequestFinished(empty);
- }
- else
- {
- emit OnRequestFinished(this->data);
- }
- }
-
- void QvHttpRequestHelper::onReadyRead_p()
- {
- DEBUG(MODULE_NETWORK, "A request is now ready read")
- this->data += reply->readAll();
- }
-} // namespace Qv2ray::common
+} // namespace Qv2ray::common::network
diff --git a/src/common/HTTPRequestHelper.hpp b/src/common/HTTPRequestHelper.hpp
index ff2d675f..f9d2f2a4 100644
--- a/src/common/HTTPRequestHelper.hpp
+++ b/src/common/HTTPRequestHelper.hpp
@@ -1,55 +1,27 @@
-/*
- Copyright (C) 2019 SoneWinstone (jianwenzhen@qq.com)
- Copyright (C) 2019 Leroy.H.Y
-
- 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 .
-*/
-
#pragma once
#include
#include
#include
#include
+#include
-namespace Qv2ray::common
+namespace Qv2ray::common::network
{
- class QvHttpRequestHelper : public QObject
+ class NetworkRequestHelper : QObject
{
Q_OBJECT
public:
- explicit QvHttpRequestHelper(QObject *parent = nullptr);
- ~QvHttpRequestHelper();
- // get
- void AsyncGet(const QString &url);
- QByteArray Get(const QString &url);
- signals:
- void OnRequestFinished(QByteArray &data);
-
- private slots:
- void onRequestFinished_p();
- void onReadyRead_p();
+ explicit NetworkRequestHelper(QObject *parent) : QObject(parent){};
+ ~NetworkRequestHelper(){};
+ void AsyncHttpGet(const QString &url, std::function funcPtr);
+ static QByteArray HttpGet(const QUrl &url);
private:
- void setAccessManagerAttributes(QNetworkAccessManager &accessManager);
- void setHeader(const QByteArray &key, const QByteArray &value);
- QByteArray data;
- QUrl url;
- QNetworkReply *reply;
- QNetworkRequest request;
+ static void setAccessManagerAttributes(QNetworkRequest &request, QNetworkAccessManager &accessManager);
+ static void setHeader(QNetworkRequest &request, const QByteArray &key, const QByteArray &value);
QNetworkAccessManager accessManager;
};
-} // namespace Qv2ray::common
+} // namespace Qv2ray::common::network
-using namespace Qv2ray::common;
+using namespace Qv2ray::common::network;
diff --git a/src/common/QJsonModel.cpp b/src/common/QJsonModel.cpp
index 33bbcd75..c633b8cd 100644
--- a/src/common/QJsonModel.cpp
+++ b/src/common/QJsonModel.cpp
@@ -26,7 +26,6 @@
#include
#include
-#include
QJsonTreeItem::QJsonTreeItem(QJsonTreeItem *parent)
{
diff --git a/src/common/QJsonModel.hpp b/src/common/QJsonModel.hpp
index 69bddec8..3b07e7cf 100644
--- a/src/common/QJsonModel.hpp
+++ b/src/common/QJsonModel.hpp
@@ -25,12 +25,12 @@
#pragma once
#include
-#include
+#include
+#include
#include
#include
#include
#include
-
class QJsonModel;
class QJsonItem;
diff --git a/src/common/QvHelpers.cpp b/src/common/QvHelpers.cpp
index b4a7dffb..58a3426f 100644
--- a/src/common/QvHelpers.cpp
+++ b/src/common/QvHelpers.cpp
@@ -1,18 +1,13 @@
#include "common/QvHelpers.hpp"
+#include "base/Qv2rayBase.hpp"
#include "libs/puresource/src/PureJson.hpp"
-#include
-#include
-#include
-#include
-#include
-
namespace Qv2ray::common
{
const QString GenerateRandomString(int len)
{
- const QString possibleCharacters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
+ const QString possibleCharacters("abcdefghijklmnopqrstuvwxyz");
QString randomString;
for (int i = 0; i < len; ++i)
@@ -41,39 +36,31 @@ namespace Qv2ray::common
if (!wasOpened)
source.close();
//
- QTextCodec::ConverterState state;
QTextCodec *codec = QTextCodec::codecForName("UTF-8");
+ QTextCodec::ConverterState state;
const QString text = codec->toUnicode(byteArray.constData(), byteArray.size(), &state);
if (state.invalidChars > 0)
{
LOG(MODULE_FILEIO, "Not a valid UTF-8 sequence: " + source.fileName())
- return byteArray;
- }
- else
- {
- return text;
}
+ return state.invalidChars > 0 ? byteArray : text;
}
bool StringToFile(const QString &text, const QString &targetpath)
{
- auto file = QFile(targetpath);
- return StringToFile(text, file);
- }
- bool StringToFile(const QString &text, QFile &targetFile)
- {
- QFileInfo info(targetFile);
- if (!info.dir().exists())
+ bool override = false;
{
- info.dir().mkpath(info.dir().path());
+ QFileInfo info(targetpath);
+ override = info.exists();
+ if (!override && !info.dir().exists())
+ info.dir().mkpath(info.dir().path());
}
- bool override = targetFile.exists();
- targetFile.open(QFile::WriteOnly);
- targetFile.write(text.toUtf8());
- targetFile.close();
+ QSaveFile f{ targetpath };
+ f.open(QIODevice::WriteOnly);
+ f.write(text.toUtf8());
+ f.commit();
return override;
}
-
QString JsonToString(const QJsonObject &json, QJsonDocument::JsonFormat format)
{
QJsonDocument doc;
@@ -116,6 +103,31 @@ namespace Qv2ray::common
return doc.object();
}
+ // backported from QvPlugin-SSR.
+ QString SafeBase64Decode(QString string)
+ {
+ QByteArray ba = string.replace(QChar('-'), QChar('+')).replace(QChar('_'), QChar('/')).toUtf8();
+ return QByteArray::fromBase64(ba, QByteArray::Base64Option::OmitTrailingEquals);
+ }
+
+ // backported from QvPlugin-SSR.
+ QString SafeBase64Encode(const QString &string, bool trim)
+ {
+ QString base64 = string.toUtf8().toBase64();
+ if (trim)
+ {
+ auto tmp = base64.replace(QChar('+'), QChar('-')).replace(QChar('/'), QChar('_'));
+ auto crbedin = tmp.crbegin();
+ auto idx = tmp.length();
+ while (crbedin != tmp.crend() && (*crbedin) == '=') idx -= 1, crbedin++;
+ return idx != tmp.length() ? tmp.remove(idx, tmp.length() - idx) : tmp;
+ }
+ else
+ {
+ return base64.replace(QChar('+'), QChar('-')).replace(QChar('/'), QChar('_'));
+ }
+ }
+
QString Base64Encode(const QString &string)
{
QByteArray ba = string.toUtf8();
@@ -130,19 +142,11 @@ namespace Qv2ray::common
QStringList SplitLines(const QString &_string)
{
+#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
+ return _string.split(QRegExp("[\r\n]"), Qt::SkipEmptyParts);
+#else
return _string.split(QRegExp("[\r\n]"), QString::SkipEmptyParts);
- }
-
- list SplitLines_std(const QString &_string)
- {
- list list;
-
- for (auto line : _string.split(QRegExp("[\r\n]"), QString::SkipEmptyParts))
- {
- list.push_back(line.toStdString());
- }
-
- return list;
+#endif
}
QStringList GetFileList(const QDir &dir)
@@ -155,22 +159,6 @@ namespace Qv2ray::common
return GetFileList(dir).contains(fileName);
}
- void QvMessageBoxWarn(QWidget *parent, const QString &title, const QString &text)
- {
- QMessageBox::warning(parent, title, text, QMessageBox::Ok | QMessageBox::Default, 0);
- }
-
- void QvMessageBoxInfo(QWidget *parent, const QString &title, const QString &text)
- {
- QMessageBox::information(parent, title, text, QMessageBox::Ok | QMessageBox::Default, 0);
- }
-
- QMessageBox::StandardButton QvMessageBoxAsk(QWidget *parent, const QString &title, const QString &text,
- QMessageBox::StandardButton extraButtons)
- {
- return QMessageBox::question(parent, title, text, QMessageBox::Yes | QMessageBox::No | extraButtons);
- }
-
QString FormatBytes(const int64_t b)
{
auto _bytes = b;
@@ -195,7 +183,7 @@ namespace Qv2ray::common
{
std::string _name = fileName.toStdString();
std::replace_if(
- _name.begin(), _name.end(), [](char c) { return std::string::npos != string(R"("/\?%&^*;:|><)").find(c); }, '_');
+ _name.begin(), _name.end(), [](char c) { return std::string::npos != std::string(R"("/\?%&^*;:|><)").find(c); }, '_');
return QString::fromStdString(_name);
}
@@ -225,38 +213,20 @@ namespace Qv2ray::common
i++;
}
}
- QPixmap ApplyEffectToImage(QPixmap src, QGraphicsEffect *effect, int extent)
+
+ void QvMessageBoxWarn(QWidget *parent, const QString &title, const QString &text)
{
- constexpr int extent2 = 0;
- if (src.isNull())
- return QPixmap(); // No need to do anything else!
- if (!effect)
- return src; // No need to do anything else!
- QGraphicsScene scene;
- auto p = scene.addPixmap(src);
- p->setGraphicsEffect(effect);
- //
- QImage res(src.size() + QSize(extent2, extent2), QImage::Format_ARGB32);
- res.fill(Qt::transparent);
- QPainter ptr(&res);
- //
- scene.render(&ptr, QRectF(), QRectF(-extent, -extent, src.width() + extent2, src.height() + extent * 2));
- //
- scene.removeItem(p);
- return QPixmap::fromImage(res);
- }
- QPixmap BlurImage(const QPixmap &pixmap, const double rad)
- {
- QGraphicsBlurEffect pBlur;
- pBlur.setBlurRadius(rad);
- return ApplyEffectToImage(pixmap, &pBlur, 0);
+ QMessageBox::warning(parent, title, text, QMessageBox::Ok | QMessageBox::Default, 0);
}
- QPixmap ColorizeImage(const QPixmap &pixmap, const QColor &color, const qreal factor)
+ void QvMessageBoxInfo(QWidget *parent, const QString &title, const QString &text)
{
- QGraphicsColorizeEffect pColor;
- pColor.setColor(color);
- pColor.setStrength(factor);
- return ApplyEffectToImage(pixmap, &pColor, 0);
+ QMessageBox::information(parent, title, text, QMessageBox::Ok | QMessageBox::Default, 0);
}
+ QMessageBox::StandardButton QvMessageBoxAsk(QWidget *parent, const QString &title, const QString &text,
+ QMessageBox::StandardButton extraButtons)
+ {
+ return QMessageBox::question(parent, title, text, QMessageBox::Yes | QMessageBox::No | extraButtons);
+ }
+
} // namespace Qv2ray::common
diff --git a/src/common/QvHelpers.hpp b/src/common/QvHelpers.hpp
index 14c61e30..616a4f57 100644
--- a/src/common/QvHelpers.hpp
+++ b/src/common/QvHelpers.hpp
@@ -1,8 +1,12 @@
#pragma once
-#include "base/Qv2rayBase.hpp"
-
+#include
+#include
+#include
+#include
+#include
#include
+#include
#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*\])"
@@ -13,21 +17,20 @@
namespace Qv2ray::common
{
QStringList GetFileList(const QDir &dir);
+
+ QString SafeBase64Decode(QString string);
+ QString SafeBase64Encode(const QString &string, bool trim);
+
QString Base64Encode(const QString &string);
QString Base64Decode(const QString &string);
QStringList SplitLines(const QString &str);
- list SplitLines_std(const QString &_string);
+ // list SplitLines_std(const QString &_string);
bool FileExistsIn(const QDir &dir, const QString &fileName);
const QString GenerateRandomString(int len = 12);
//
- void QvMessageBoxWarn(QWidget *parent, const QString &title, const QString &text);
- void QvMessageBoxInfo(QWidget *parent, const QString &title, const QString &text);
- QMessageBox::StandardButton QvMessageBoxAsk(QWidget *parent, const QString &title, const QString &text,
- QMessageBox::StandardButton extraButtons = QMessageBox::NoButton);
- //
QString StringFromFile(const QString &filePath);
QString StringFromFile(QFile &source);
- bool StringToFile(const QString &text, QFile &target);
+ // bool StringToFile(const QString &text, QFile &target);
bool StringToFile(const QString &text, const QString &targetpath);
//
QJsonObject JsonFromString(const QString &string);
@@ -38,9 +41,6 @@ namespace Qv2ray::common
QString FormatBytes(const int64_t bytes);
void DeducePossibleFileName(const QString &baseDir, QString *fileName, const QString &extension);
//
- QPixmap ApplyEffectToImage(QPixmap src, QGraphicsEffect *effect, int extent);
- QPixmap BlurImage(const QPixmap &pixmap, const double rad = 50);
- QPixmap ColorizeImage(const QPixmap &pixmap, const QColor &color, const qreal factor);
// This function cannot be marked as inline.
QString RemoveInvalidFileName(const QString &fileName);
bool IsValidFileName(const QString &fileName);
@@ -49,27 +49,6 @@ namespace Qv2ray::common
return GenerateRandomString().toLower();
// return QUuid::createUuid().toString(QUuid::WithoutBraces);
}
- //
- template
- QString StructToJsonString(const TYPE &t)
- {
- return QString::fromStdString(x2struct::X::tojson(t, "", 4, ' '));
- }
- //
- template
- TYPE StructFromJsonString(const QString &str)
- {
- TYPE v;
- x2struct::X::loadjson(str.toStdString(), v, false);
- return v;
- }
- // Misc
- template
- QJsonObject GetRootObject(const T &t)
- {
- auto json = StructToJsonString(t);
- return JsonFromString(json);
- }
inline QString TruncateString(const QString &str, int limit = -1, const QString &suffix = "...")
{
@@ -81,7 +60,6 @@ namespace Qv2ray::common
namespace validation
{
const inline QRegularExpression __regex_ipv4_full(REGEX_IPV4_ADDR "$");
- const inline QRegularExpression __regex_ipv6_full(REGEX_IPV6_ADDR "$");
inline bool IsIPv4Address(const QString &addr)
{
@@ -90,12 +68,22 @@ namespace Qv2ray::common
inline bool IsIPv6Address(const QString &addr)
{
- return __regex_ipv6_full.match(addr).hasMatch();
+ QHostAddress address(addr);
+ return QAbstractSocket::IPv6Protocol == address.protocol();
}
inline bool IsValidIPAddress(const QString &addr)
{
- return IsIPv4Address(addr) || IsIPv6Address(addr);
+ return !addr.isEmpty() && (IsIPv4Address(addr) || IsIPv6Address(addr));
+ }
+
+ inline bool IsValidDNSServer(const QString &addr)
+ {
+ return IsIPv4Address(addr) //
+ || IsIPv6Address(addr) //
+ || addr.startsWith("https://") //
+ || addr.startsWith("https+local://") //
+ || addr == "localhost";
}
} // namespace validation
@@ -103,18 +91,12 @@ namespace Qv2ray::common
{
QDateTime timestamp;
timestamp.setSecsSinceEpoch(t);
- return timestamp.toString(Qt::SystemLocaleShortDate);
- }
-
- inline void FastAppendTextDocument(const QString &message, QTextDocument *doc)
- {
- QTextCursor cursor(doc);
- cursor.movePosition(QTextCursor::End);
- cursor.beginEditBlock();
- cursor.insertBlock();
- cursor.insertText(message);
- cursor.endEditBlock();
+ return timestamp.toString();
}
+ void QvMessageBoxWarn(QWidget *parent, const QString &title, const QString &text);
+ void QvMessageBoxInfo(QWidget *parent, const QString &title, const QString &text);
+ QMessageBox::StandardButton QvMessageBoxAsk(QWidget *parent, const QString &title, const QString &text,
+ QMessageBox::StandardButton extraButtons = QMessageBox::StandardButton::NoButton);
} // namespace Qv2ray::common
using namespace Qv2ray::common;
diff --git a/src/common/QvTranslator.cpp b/src/common/QvTranslator.cpp
index 2c5f5c63..0542fb21 100644
--- a/src/common/QvTranslator.cpp
+++ b/src/common/QvTranslator.cpp
@@ -1,16 +1,8 @@
#include "QvTranslator.hpp"
-#include "base/Qv2rayLog.hpp"
+#include "base/Qv2rayBase.hpp"
#include "common/QvHelpers.hpp"
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
using namespace Qv2ray::base;
// path searching list.
@@ -27,20 +19,19 @@ QStringList getLanguageSearchPaths()
list << QString(QV2RAY_TRANSLATION_PATH);
#endif
return list;
-};
+}
namespace Qv2ray::common
{
QvTranslator::QvTranslator()
{
- DEBUG(MODULE_UI, "QvTranslator constructor.")
GetAvailableLanguages();
}
QStringList QvTranslator::GetAvailableLanguages()
{
languages.clear();
- for (auto path : getLanguageSearchPaths())
+ for (const auto &path : getLanguageSearchPaths())
{
languages << QDir(path).entryList(QStringList{ "*.qm" }, QDir::Hidden | QDir::Files);
}
@@ -52,20 +43,21 @@ namespace Qv2ray::common
bool QvTranslator::InstallTranslation(const QString &code)
{
- for (auto path : getLanguageSearchPaths())
+ for (const auto &path : getLanguageSearchPaths())
{
if (FileExistsIn(QDir(path), code + ".qm"))
{
- LOG(MODULE_UI, "Found " + code + " in folder: " + path)
+ DEBUG(MODULE_UI, "Found " + code + " in folder: " + path)
QTranslator *translatorNew = new QTranslator();
translatorNew->load(code + ".qm", path);
if (pTranslator)
{
- LOG(MODULE_INIT, "Removed translations")
+ LOG(MODULE_UI, "Removed translations")
qApp->removeTranslator(pTranslator.get());
}
this->pTranslator.reset(translatorNew);
qApp->installTranslator(pTranslator.get());
+ LOG(MODULE_UI, "Successfully installed a translator for " + code)
return true;
}
}
diff --git a/src/components/autolaunch/QvAutoLaunch.cpp b/src/components/autolaunch/QvAutoLaunch.cpp
index cdf2f006..de3457fa 100644
--- a/src/components/autolaunch/QvAutoLaunch.cpp
+++ b/src/components/autolaunch/QvAutoLaunch.cpp
@@ -1,6 +1,8 @@
#include "QvAutoLaunch.hpp"
-#include
+#include "base/Qv2rayBase.hpp"
+
+#include
#include
#include
#include
@@ -29,7 +31,7 @@ namespace Qv2ray::components::autolaunch
bool GetLaunchAtLoginStatus()
{
#ifdef Q_OS_WIN
- QString appName = QApplication::applicationName();
+ QString appName = QCoreApplication::applicationName();
QSettings reg("HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", QSettings::NativeFormat);
return reg.contains(appName);
}
@@ -80,7 +82,7 @@ namespace Qv2ray::components::autolaunch
}
#elif defined Q_OS_LINUX
- QString appName = QApplication::applicationName();
+ QString appName = QCoreApplication::applicationName();
QString desktopFileLocation = getUserAutostartDir_private() + appName + QLatin1String(".desktop");
return QFile::exists(desktopFileLocation);
}
@@ -89,7 +91,7 @@ namespace Qv2ray::components::autolaunch
void SetLaunchAtLoginStatus(bool enable)
{
#ifdef Q_OS_WIN
- QString appName = QApplication::applicationName();
+ QString appName = QCoreApplication::applicationName();
QSettings reg("HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", QSettings::NativeFormat);
if (enable)
@@ -160,7 +162,7 @@ namespace Qv2ray::components::autolaunch
auto binPath = qEnvironmentVariableIsSet("APPIMAGE") ? qEnvironmentVariable("APPIMAGE") : QCoreApplication::applicationFilePath();
//
// From https://github.com/nextcloud/desktop/blob/master/src/common/utility_unix.cpp
- QString appName = QApplication::applicationName();
+ QString appName = QCoreApplication::applicationName();
QString userAutoStartPath = getUserAutostartDir_private();
QString desktopFileLocation = userAutoStartPath + appName + QLatin1String(".desktop");
@@ -184,24 +186,23 @@ namespace Qv2ray::components::autolaunch
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=") << binPath << 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;
+ ts << QLatin1String("[Desktop Entry]") << NEWLINE //
+ << QLatin1String("Name=") << QCoreApplication::applicationName() + QCoreApplication::arguments().join(" ") << NEWLINE //
+ << QLatin1String("GenericName=") << QLatin1String("V2ray Frontend") << NEWLINE //
+ << QLatin1String("Exec=") << binPath << NEWLINE //
+ << QLatin1String("Terminal=") << "false" << NEWLINE //
+ << QLatin1String("Icon=") << "qv2ray" << NEWLINE //
+ << QLatin1String("Categories=") << "Network" << NEWLINE //
+ << QLatin1String("Type=") << "Application" << NEWLINE //
+ << QLatin1String("StartupNotify=") << "false" << NEWLINE //
+ << QLatin1String("X-GNOME-Autostart-enabled=") << "true" << NEWLINE;
+ ts.flush();
+ iniFile.close();
}
else
{
- if (!QFile::remove(desktopFileLocation))
- {
- // qCWarning(lcUtility) << "Could not remove autostart desktop
- // file";
- }
+ QFile::remove(desktopFileLocation);
+ QFile::remove(desktopFileLocation.replace("qv2ray", "Qv2ray"));
}
}
#endif
diff --git a/src/components/darkmode/DarkmodeDetector.cpp b/src/components/darkmode/DarkmodeDetector.cpp
index 33e7c089..f13a497d 100644
--- a/src/components/darkmode/DarkmodeDetector.cpp
+++ b/src/components/darkmode/DarkmodeDetector.cpp
@@ -1,9 +1,10 @@
#include "DarkmodeDetector.hpp"
-#include
+#include "base/Qv2rayBase.hpp"
+
+#include
+#include
#ifdef Q_OS_LINUX
- #include
- #include
#elif defined(Q_OS_WIN32)
#include
#else
@@ -16,7 +17,7 @@ namespace Qv2ray::components::darkmode
// Copyright (C) 2020 KeePassXC Team
bool isDarkMode()
{
-#if defined(Q_OS_LINUX)
+#if !QvHasFeature(NativeDarkmode)
if (!qApp || !qApp->style())
{
return false;
@@ -25,9 +26,6 @@ namespace Qv2ray::components::darkmode
#elif defined(Q_OS_WIN32)
QSettings settings(R"(HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize)", QSettings::NativeFormat);
return settings.value("AppsUseLightTheme", 1).toInt() == 0;
-#elif defined(Q_OS_DARWIN)
- // TODO: expand this stub
- return false;
#endif
}
diff --git a/src/components/geosite/QvGeositeReader.cpp b/src/components/geosite/QvGeositeReader.cpp
index ec4487aa..0c490786 100644
--- a/src/components/geosite/QvGeositeReader.cpp
+++ b/src/components/geosite/QvGeositeReader.cpp
@@ -1,41 +1,54 @@
#include "QvGeositeReader.hpp"
-#include "v2ray_geosite.pb.h"
+#ifndef ANDROID
+ #include "v2ray_geosite.pb.h"
+#endif
namespace Qv2ray::components::geosite
{
+ QMap GeositeEntries;
QStringList ReadGeoSiteFromFile(const QString &filepath)
{
- QStringList list;
- LOG(MODULE_FILEIO, "Reading geosites from: " + filepath)
- //
- GOOGLE_PROTOBUF_VERIFY_VERSION;
- //
- QFile f(filepath);
- bool opened = f.open(QFile::OpenModeFlag::ReadOnly);
-
- if (!opened)
+ if (GeositeEntries.contains(filepath))
{
- LOG(MODULE_FILEIO, "File cannot be opened: " + filepath)
+ return GeositeEntries.value(filepath);
+ }
+ else
+ {
+ QStringList list;
+#ifndef ANDROID
+ LOG(MODULE_FILEIO, "Reading geosites from: " + filepath)
+ //
+ GOOGLE_PROTOBUF_VERIFY_VERSION;
+ //
+ QFile f(filepath);
+ bool opened = f.open(QFile::OpenModeFlag::ReadOnly);
+
+ if (!opened)
+ {
+ LOG(MODULE_FILEIO, "File cannot be opened: " + filepath)
+ return list;
+ }
+
+ auto content = f.readAll();
+ f.close();
+ //
+ GeoSiteList sites;
+ sites.ParseFromArray(content.data(), content.size());
+
+ for (const auto &e : sites.entry())
+ {
+ // We want to use lower string.
+ list << QString::fromStdString(e.country_code()).toLower();
+ }
+
+ LOG(MODULE_FILEIO, "Loaded " + QSTRN(list.count()) + " geosite entries from data file.")
+ // Optional: Delete all global objects allocated by libprotobuf.
+ google::protobuf::ShutdownProtobufLibrary();
+#endif
+ list.sort();
+ GeositeEntries[filepath] = list;
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(MODULE_FILEIO, "Loaded " + QSTRN(list.count()) + " geosite entries from data file.")
- // Optional: Delete all global objects allocated by libprotobuf.
- google::protobuf::ShutdownProtobufLibrary();
- list.sort();
- return list;
}
} // namespace Qv2ray::components::geosite
diff --git a/src/components/geosite/QvGeositeReader.hpp b/src/components/geosite/QvGeositeReader.hpp
index 2eaf93ef..f7a91945 100644
--- a/src/components/geosite/QvGeositeReader.hpp
+++ b/src/components/geosite/QvGeositeReader.hpp
@@ -4,7 +4,7 @@
namespace Qv2ray::components::geosite
{
QStringList ReadGeoSiteFromFile(const QString &filepath);
-}
+} // namespace Qv2ray::components::geosite
using namespace Qv2ray::components;
using namespace Qv2ray::components::geosite;
diff --git a/src/components/latency/LatencyTest.cpp b/src/components/latency/LatencyTest.cpp
new file mode 100644
index 00000000..fdeebac0
--- /dev/null
+++ b/src/components/latency/LatencyTest.cpp
@@ -0,0 +1,50 @@
+#include "LatencyTest.hpp"
+
+#include "LatencyTestThread.hpp"
+#include "core/handler/ConfigHandler.hpp"
+
+constexpr auto LATENCY_PROPERTY_KEY = "__QvLatencyTest__";
+
+namespace Qv2ray::components::latency
+{
+ LatencyTestHost::LatencyTestHost(const int defaultCount, QObject *parent) : QObject(parent)
+ {
+ totalTestCount = defaultCount;
+ }
+
+ void LatencyTestHost::StopAllLatencyTest()
+ {
+ for (const auto &thread : latencyThreads)
+ {
+ thread->terminate();
+ thread->deleteLater();
+ }
+ }
+ void LatencyTestHost::TestLatency(const ConnectionId &id, Qv2rayLatencyTestingMethod method)
+ {
+ const auto &[protocol, host, port] = GetConnectionInfo(id);
+ auto thread = new LatencyTestThread(host, port, method, totalTestCount, this);
+ connect(thread, &QThread::finished, this, &LatencyTestHost::OnLatencyThreadProcessCompleted);
+ thread->setProperty(LATENCY_PROPERTY_KEY, QVariant::fromValue(id));
+ latencyThreads.push_back(thread);
+ thread->start();
+ }
+
+ void LatencyTestHost::OnLatencyThreadProcessCompleted()
+ {
+ const auto senderThread = qobject_cast(sender());
+ latencyThreads.removeOne(senderThread);
+ auto result = senderThread->GetResult();
+
+ if (!result.errorMessage.isEmpty())
+ {
+ LOG(MODULE_NETWORK, "Ping --> " + result.errorMessage)
+ result.avg = LATENCY_TEST_VALUE_ERROR;
+ result.best = LATENCY_TEST_VALUE_ERROR;
+ result.worst = LATENCY_TEST_VALUE_ERROR;
+ }
+
+ emit OnLatencyTestCompleted(senderThread->property(LATENCY_PROPERTY_KEY).value(), result);
+ }
+
+} // namespace Qv2ray::components::latency
diff --git a/src/components/latency/LatencyTest.hpp b/src/components/latency/LatencyTest.hpp
new file mode 100644
index 00000000..e0e66318
--- /dev/null
+++ b/src/components/latency/LatencyTest.hpp
@@ -0,0 +1,37 @@
+#pragma once
+#include "base/Qv2rayBase.hpp"
+
+namespace Qv2ray::components::latency
+{
+ class LatencyTestThread;
+ struct LatencyTestResult
+ {
+ QString errorMessage;
+ int totalCount;
+ int failedCount;
+ long worst = LATENCY_TEST_VALUE_ERROR;
+ long best = LATENCY_TEST_VALUE_ERROR;
+ long avg = LATENCY_TEST_VALUE_ERROR;
+ Qv2rayLatencyTestingMethod method;
+ };
+
+ class LatencyTestHost : public QObject
+ {
+ Q_OBJECT
+ public:
+ explicit LatencyTestHost(const int defaultCount = 3, QObject *parent = nullptr);
+ void TestLatency(const ConnectionId &connectionId, Qv2rayLatencyTestingMethod);
+ void StopAllLatencyTest();
+ signals:
+ void OnLatencyTestCompleted(const ConnectionId &id, const LatencyTestResult &data);
+
+ private slots:
+ void OnLatencyThreadProcessCompleted();
+
+ private:
+ int totalTestCount;
+ QList latencyThreads;
+ };
+} // namespace Qv2ray::components::latency
+
+using namespace Qv2ray::components::latency;
diff --git a/src/components/latency/LatencyTestThread.cpp b/src/components/latency/LatencyTestThread.cpp
new file mode 100644
index 00000000..6aeb2cf8
--- /dev/null
+++ b/src/components/latency/LatencyTestThread.cpp
@@ -0,0 +1,79 @@
+#include "LatencyTestThread.hpp"
+
+#include "TCPing.hpp"
+
+#ifdef Q_OS_UNIX
+ #include "unix/ICMPPing.hpp"
+#else
+ #include "win/ICMPPing.hpp"
+#endif
+
+namespace Qv2ray::components::latency
+{
+
+ LatencyTestThread::LatencyTestThread(const QString &host, int port, Qv2rayLatencyTestingMethod method, int count, QObject *parent)
+ : QThread(parent)
+ {
+ this->count = count;
+ this->host = host;
+ this->port = port;
+ this->method = method;
+ this->resultData = {};
+ this->resultData.method = method;
+ }
+ void LatencyTestThread::run()
+ {
+ resultData.avg = 0;
+ resultData.best = 0;
+ resultData.worst = 0;
+ switch (method)
+ {
+ case ICMPING:
+ {
+ icmping::ICMPPing ping(30);
+ for (auto i = 0; i < count; i++)
+ {
+ resultData.totalCount++;
+ const auto pair = ping.ping(host);
+ const auto &errMessage = pair.second;
+ const long _latency = pair.first;
+ if (!errMessage.isEmpty())
+ {
+ resultData.errorMessage.append(NEWLINE + errMessage);
+ resultData.failedCount++;
+ }
+ else
+ {
+#ifdef Q_OS_WIN
+ // Is it Windows?
+ #undef min
+ #undef max
+#endif
+ resultData.avg += _latency;
+ resultData.best = std::min(resultData.best, _latency);
+ resultData.worst = std::max(resultData.worst, _latency);
+ }
+ }
+ if (resultData.totalCount != 0 && resultData.failedCount != 0)
+ {
+ resultData.errorMessage.clear();
+ resultData.avg = resultData.avg / (resultData.totalCount - resultData.failedCount) / 1000;
+ }
+ else
+ {
+ resultData.avg = LATENCY_TEST_VALUE_ERROR;
+ LOG(MODULE_NETWORK, resultData.errorMessage)
+ }
+ //
+ //
+ break;
+ }
+ case TCPING:
+ default:
+ {
+ this->resultData = tcping::TestTCPLatency(host, port, count);
+ break;
+ }
+ }
+ }
+} // namespace Qv2ray::components::latency
diff --git a/src/components/latency/LatencyTestThread.hpp b/src/components/latency/LatencyTestThread.hpp
new file mode 100644
index 00000000..45593d53
--- /dev/null
+++ b/src/components/latency/LatencyTestThread.hpp
@@ -0,0 +1,30 @@
+#pragma once
+#include "LatencyTest.hpp"
+
+#include
+namespace Qv2ray::components::latency
+{
+ class LatencyTestThread : public QThread
+ {
+ Q_OBJECT
+ public:
+ explicit LatencyTestThread(const QString &host, int port, Qv2rayLatencyTestingMethod, int count, QObject *parent = nullptr);
+ LatencyTestResult GetResult() const
+ {
+ return resultData;
+ }
+
+ protected:
+ void run() override;
+
+ private:
+ LatencyTestResult resultData;
+ QString host;
+ int port;
+ int count;
+ Qv2rayLatencyTestingMethod method;
+
+ // static LatencyTestResult TestLatency_p(const ConnectionId &id, const int count);
+ };
+
+} // namespace Qv2ray::components::latency
diff --git a/src/components/latency/QvTCPing.cpp b/src/components/latency/QvTCPing.cpp
deleted file mode 100644
index 32970468..00000000
--- a/src/components/latency/QvTCPing.cpp
+++ /dev/null
@@ -1,225 +0,0 @@
-#ifdef _WIN32
- #include
- #include
-#else
- #include
- #include
- #include
- #include
-#endif
-#include "QtConcurrent/QtConcurrent"
-#include "QvTCPing.hpp"
-#include "core/handler/ConfigHandler.hpp"
-
-namespace Qv2ray::components::tcping
-{
- static int resolveHost(const std::string &host, int portnr, struct addrinfo **res);
- static int testLatency(struct addrinfo *addr, std::chrono::system_clock::time_point *start, std::chrono::system_clock::time_point *end);
- //
- QvTCPingHelper::QvTCPingHelper(const int defaultCount, QObject *parent) : QObject(parent)
- {
- count = defaultCount;
- }
-
- void QvTCPingHelper::StopAllLatenceTest()
- {
- while (!pingWorkingThreads.isEmpty())
- {
- auto worker = pingWorkingThreads.dequeue();
- worker->future().cancel();
- worker->cancel();
- }
- }
- void QvTCPingHelper::TestLatency(const ConnectionId &id)
- {
- auto watcher = new QFutureWatcher(this);
- watcher->setFuture(QtConcurrent::run(&QvTCPingHelper::TestLatency_p, id, count));
- pingWorkingThreads.enqueue(watcher);
- //
- connect(watcher, &QFutureWatcher::finished, this, [=]() {
- auto result = watcher->result();
- this->pingWorkingThreads.removeOne(watcher);
-
- if (!result.errorMessage.isEmpty())
- {
- LOG(MODULE_NETWORK, "Ping --> " + result.errorMessage)
- result.avg = QVTCPING_VALUE_ERROR;
- result.best = QVTCPING_VALUE_ERROR;
- result.worst = QVTCPING_VALUE_ERROR;
- }
-
- emit this->OnLatencyTestCompleted(result);
- });
- }
-
- QvTCPingResultObject QvTCPingHelper::TestLatency_p(const ConnectionId &id, const int count)
- {
- QvTCPingResultObject data;
- data.connectionId = id;
-
- if (isExiting)
- return data;
-
- auto [protocol, host, port] = GetConnectionInfo(id);
- Q_UNUSED(protocol)
- double successCount = 0, errorCount = 0;
- addrinfo *resolved;
- int errcode;
-
- if ((errcode = resolveHost(host.toStdString(), port, &resolved)) != 0)
- {
-#ifdef _WIN32
- data.errorMessage = QString::fromStdWString(gai_strerror(errcode));
-#else
- data.errorMessage = gai_strerror(errcode);
-#endif
- return data;
- }
-
- bool noAddress = false;
- int currentCount = 0;
-
- data.avg = 0;
- data.worst = 0;
- data.best = 0;
-
- while (currentCount < count)
- {
- system_clock::time_point start;
- system_clock::time_point end;
-
- if ((errcode = testLatency(resolved, &start, &end)) != 0)
- {
- if (errcode != -EADDRNOTAVAIL)
- {
- LOG(MODULE_NETWORK, "error connecting to host: " + host + ":" + QSTRN(port) + " " + strerror(-errcode))
- errorCount++;
- }
- else
- {
- if (noAddress)
- {
- LOG(MODULE_NETWORK, "no address error")
- }
- else
- {
- LOG(MODULE_NETWORK, "error connecting to host: " + QSTRN(-errcode) + " " + strerror(-errcode))
- }
-
- noAddress = true;
- }
- }
- else
- {
- noAddress = false;
- successCount++;
- auto milliseconds = std::chrono::duration_cast(end - start);
- uint ms = milliseconds.count();
- data.avg += ms;
- data.worst = min(data.worst, ms);
- data.best = max(data.best, ms);
-
- if (ms > 1000)
- {
- LOG(MODULE_NETWORK, "Stop the test on the first long connect()")
- break; /* Stop the test on the first long connect() */
- }
- }
-
- currentCount++;
- QThread::msleep(500);
- }
-
- data.avg = data.avg / successCount;
- freeaddrinfo(resolved);
- data.avg = min(data.avg, QVTCPING_VALUE_ERROR);
- data.worst = min(data.worst, QVTCPING_VALUE_ERROR);
- data.best = min(data.best, QVTCPING_VALUE_ERROR);
- return data;
- }
-
- int 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 testLatency(struct addrinfo *addr, std::chrono::system_clock::time_point *start, std::chrono::system_clock::time_point *end)
- {
-#ifdef _WIN32
- SOCKET fd;
-#else
- int fd;
-#endif
- 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 */
- fd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
-
- if (!fd)
- {
- 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;
- }
-
- *start = system_clock::now();
-
- /* connect to peer */
- // Qt has its own connect() function in QObject....
- // So we add "::" here
- if (::connect(fd, addr->ai_addr, addr->ai_addrlen) == 0)
- {
- *end = system_clock::now();
-#ifdef Q_OS_WIN
- closesocket(fd);
-#else
- close(fd);
-#endif
- return 0;
- }
-
- next_addr1:
-#ifdef _WIN32
- closesocket(fd);
-#else
- close(fd);
-#endif
- next_addr0:
- addr = addr->ai_next;
- }
-
- rv = rv ? rv : -errno;
- return rv;
- }
-} // namespace Qv2ray::core::tcping
diff --git a/src/components/latency/QvTCPing.hpp b/src/components/latency/QvTCPing.hpp
deleted file mode 100644
index 98bb6411..00000000
--- a/src/components/latency/QvTCPing.hpp
+++ /dev/null
@@ -1,33 +0,0 @@
-#pragma once
-#include "base/Qv2rayBase.hpp"
-#include "core/CoreSafeTypes.hpp"
-
-namespace Qv2ray::components::tcping
-{
- struct QvTCPingResultObject
- {
- ConnectionId connectionId = NullConnectionId;
- QString errorMessage;
- int total, succeed, failed;
- uint worst = QVTCPING_VALUE_ERROR, best = QVTCPING_VALUE_ERROR, avg = QVTCPING_VALUE_ERROR;
- };
-
- class QvTCPingHelper : public QObject
- {
- Q_OBJECT
-
- public:
- explicit QvTCPingHelper(const int defaultCount = 5, QObject *parent = nullptr);
- void TestLatency(const ConnectionId &connectionId);
- void StopAllLatenceTest();
- signals:
- void OnLatencyTestCompleted(const QvTCPingResultObject &data);
-
- private:
- static QvTCPingResultObject TestLatency_p(const ConnectionId &id, const int count);
- int count;
- QQueue *> pingWorkingThreads;
- };
-} // namespace Qv2ray::components::tcping
-
-using namespace Qv2ray::components::tcping;
diff --git a/src/components/latency/TCPing.cpp b/src/components/latency/TCPing.cpp
new file mode 100644
index 00000000..789a192d
--- /dev/null
+++ b/src/components/latency/TCPing.cpp
@@ -0,0 +1,364 @@
+#include "TCPing.hpp"
+
+#ifdef _WIN32
+ #include
+ #include
+#else
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+#endif
+
+namespace Qv2ray::components::latency::tcping
+{
+
+#ifdef Q_OS_WIN
+ using qv_socket_t = SOCKET;
+ struct TranslateWSAError{
+ ~TranslateWSAError(){
+ errno = translate_sys_error(WSAGetLastError());
+ }
+ int translate_sys_error(int sys_errno) {
+ LOG(MODULE_NETWORK, "translate_sys_error:WSAGetLastError()==" + QSTRN(sys_errno))
+ switch (sys_errno) {
+ case ERROR_NOACCESS: return EACCES;
+ case WSAEACCES: return EACCES;
+ case ERROR_ELEVATION_REQUIRED: return EACCES;
+ case ERROR_CANT_ACCESS_FILE: return EACCES;
+ case ERROR_ADDRESS_ALREADY_ASSOCIATED: return EADDRINUSE;
+ case WSAEADDRINUSE: return EADDRINUSE;
+ case WSAEADDRNOTAVAIL: return EADDRNOTAVAIL;
+ case WSAEAFNOSUPPORT: return EAFNOSUPPORT;
+ case WSAEWOULDBLOCK: return EAGAIN;
+ case WSAEALREADY: return EALREADY;
+ case ERROR_INVALID_FLAGS: return EBADF;
+ case ERROR_INVALID_HANDLE: return EBADF;
+ case ERROR_LOCK_VIOLATION: return EBUSY;
+ case ERROR_PIPE_BUSY: return EBUSY;
+ case ERROR_SHARING_VIOLATION: return EBUSY;
+ case ERROR_OPERATION_ABORTED: return ECANCELED;
+ case WSAEINTR: return ECANCELED;
+ /*case ERROR_NO_UNICODE_TRANSLATION: return ECHARSET;*/
+ case ERROR_CONNECTION_ABORTED: return ECONNABORTED;
+ case WSAECONNABORTED: return ECONNABORTED;
+ case ERROR_CONNECTION_REFUSED: return ECONNREFUSED;
+ case WSAECONNREFUSED: return ECONNREFUSED;
+ case ERROR_NETNAME_DELETED: return ECONNRESET;
+ case WSAECONNRESET: return ECONNRESET;
+ case ERROR_ALREADY_EXISTS: return EEXIST;
+ case ERROR_FILE_EXISTS: return EEXIST;
+ case ERROR_BUFFER_OVERFLOW: return EFAULT;
+ case WSAEFAULT: return EFAULT;
+ case ERROR_HOST_UNREACHABLE: return EHOSTUNREACH;
+ case WSAEHOSTUNREACH: return EHOSTUNREACH;
+ case ERROR_INSUFFICIENT_BUFFER: return EINVAL;
+ case ERROR_INVALID_DATA: return EINVAL;
+ case ERROR_INVALID_PARAMETER: return EINVAL;
+ case ERROR_SYMLINK_NOT_SUPPORTED: return EINVAL;
+ case WSAEINVAL: return EINVAL;
+ case WSAEPFNOSUPPORT: return EINVAL;
+ case WSAESOCKTNOSUPPORT: return EINVAL;
+ case ERROR_BEGINNING_OF_MEDIA: return EIO;
+ case ERROR_BUS_RESET: return EIO;
+ case ERROR_CRC: return EIO;
+ case ERROR_DEVICE_DOOR_OPEN: return EIO;
+ case ERROR_DEVICE_REQUIRES_CLEANING: return EIO;
+ case ERROR_DISK_CORRUPT: return EIO;
+ case ERROR_EOM_OVERFLOW: return EIO;
+ case ERROR_FILEMARK_DETECTED: return EIO;
+ case ERROR_GEN_FAILURE: return EIO;
+ case ERROR_INVALID_BLOCK_LENGTH: return EIO;
+ case ERROR_IO_DEVICE: return EIO;
+ case ERROR_NO_DATA_DETECTED: return EIO;
+ case ERROR_NO_SIGNAL_SENT: return EIO;
+ case ERROR_OPEN_FAILED: return EIO;
+ case ERROR_SETMARK_DETECTED: return EIO;
+ case ERROR_SIGNAL_REFUSED: return EIO;
+ case WSAEISCONN: return EISCONN;
+ case ERROR_CANT_RESOLVE_FILENAME: return ELOOP;
+ case ERROR_TOO_MANY_OPEN_FILES: return EMFILE;
+ case WSAEMFILE: return EMFILE;
+ case WSAEMSGSIZE: return EMSGSIZE;
+ case ERROR_FILENAME_EXCED_RANGE: return ENAMETOOLONG;
+ case ERROR_NETWORK_UNREACHABLE: return ENETUNREACH;
+ case WSAENETUNREACH: return ENETUNREACH;
+ case WSAENOBUFS: return ENOBUFS;
+ case ERROR_BAD_PATHNAME: return ENOENT;
+ case ERROR_DIRECTORY: return ENOENT;
+ case ERROR_ENVVAR_NOT_FOUND: return ENOENT;
+ case ERROR_FILE_NOT_FOUND: return ENOENT;
+ case ERROR_INVALID_NAME: return ENOENT;
+ case ERROR_INVALID_DRIVE: return ENOENT;
+ case ERROR_INVALID_REPARSE_DATA: return ENOENT;
+ case ERROR_MOD_NOT_FOUND: return ENOENT;
+ case ERROR_PATH_NOT_FOUND: return ENOENT;
+ case WSAHOST_NOT_FOUND: return ENOENT;
+ case WSANO_DATA: return ENOENT;
+ case ERROR_NOT_ENOUGH_MEMORY: return ENOMEM;
+ case ERROR_OUTOFMEMORY: return ENOMEM;
+ case ERROR_CANNOT_MAKE: return ENOSPC;
+ case ERROR_DISK_FULL: return ENOSPC;
+ case ERROR_EA_TABLE_FULL: return ENOSPC;
+ case ERROR_END_OF_MEDIA: return ENOSPC;
+ case ERROR_HANDLE_DISK_FULL: return ENOSPC;
+ case ERROR_NOT_CONNECTED: return ENOTCONN;
+ case WSAENOTCONN: return ENOTCONN;
+ case ERROR_DIR_NOT_EMPTY: return ENOTEMPTY;
+ case WSAENOTSOCK: return ENOTSOCK;
+ case ERROR_NOT_SUPPORTED: return ENOTSUP;
+ case ERROR_BROKEN_PIPE: return EOF;
+ case ERROR_ACCESS_DENIED: return EPERM;
+ case ERROR_PRIVILEGE_NOT_HELD: return EPERM;
+ case ERROR_BAD_PIPE: return EPIPE;
+ case ERROR_NO_DATA: return EPIPE;
+ case ERROR_PIPE_NOT_CONNECTED: return EPIPE;
+ case WSAESHUTDOWN: return EPIPE;
+ case WSAEPROTONOSUPPORT: return EPROTONOSUPPORT;
+ case ERROR_WRITE_PROTECT: return EROFS;
+ case ERROR_SEM_TIMEOUT: return ETIMEDOUT;
+ case WSAETIMEDOUT: return ETIMEDOUT;
+ case ERROR_NOT_SAME_DEVICE: return EXDEV;
+ case ERROR_INVALID_FUNCTION: return EISDIR;
+ case ERROR_META_EXPANSION_TOO_LONG: return E2BIG;
+ default: return EIO;/*unknown*/
+ }
+ }
+ };
+#else
+ using qv_socket_t = int;
+#endif
+
+ inline int setnonblocking(qv_socket_t sockno, int &opt)
+ {
+#ifdef _WIN32
+ ULONG block = 1;
+ if (ioctlsocket(sockno, FIONBIO, &block) == SOCKET_ERROR)
+ {
+ return -1;
+ }
+#else
+ if ((opt = fcntl(sockno, F_GETFL, NULL)) < 0)
+ {
+ // get socket flags
+ return -1;
+ }
+ if (fcntl(sockno, F_SETFL, opt | O_NONBLOCK) < 0)
+ {
+ // set socket non-blocking
+ return -1;
+ }
+#endif
+ return 0;
+ }
+
+ inline int setblocking(qv_socket_t sockno, int &opt)
+ {
+#ifdef _WIN32
+ ULONG block = 0;
+ if (ioctlsocket(sockno, FIONBIO, &block) == SOCKET_ERROR)
+ {
+ return -1;
+ }
+#else
+ if (fcntl(sockno, F_SETFL, opt) < 0)
+ {
+ // reset socket flags
+ return -1;
+ }
+#endif
+ return 0;
+ }
+
+ int connect_wait(qv_socket_t sockno, struct sockaddr *addr, size_t addrlen, int timeout_sec = 5)
+ {
+ int res;
+ int opt;
+ timeval tv = { 0, 0 };
+#ifdef _WIN32
+ TranslateWSAError _translateWSAError_;
+#endif
+ tv.tv_sec = timeout_sec;
+ tv.tv_usec = 0;
+ if ((res = setnonblocking(sockno, opt)) != 0)
+ return -1;
+ if ((res = ::connect(sockno, addr, addrlen)) < 0)
+ {
+#ifdef _WIN32
+ if (WSAGetLastError() == WSAEWOULDBLOCK)
+ {
+#else
+ if (errno == EINPROGRESS)
+ {
+#endif
+ // connecting
+ fd_set wait_set;
+ FD_ZERO(&wait_set);
+ FD_SET(sockno, &wait_set);
+ res = select(sockno + 1, NULL, &wait_set, NULL, &tv);
+ }
+ }
+ else
+ // connect immediately
+ res = 1;
+
+ if (setblocking(sockno, opt) != 0)
+ return -1;
+
+ if (res < 0)
+ // an error occured
+ return -1;
+ else if (res == 0)
+ {
+ // timeout
+ errno = ETIMEDOUT;
+ return 1;
+ }
+ else
+ {
+ socklen_t len = sizeof(opt);
+ if (getsockopt(sockno, SOL_SOCKET, SO_ERROR, (char *) (&opt), &len) < 0)
+ return -1;
+ }
+ return 0;
+ }
+
+ int resolveHost(const QString &host, int port, addrinfo **res)
+ {
+ addrinfo hints;
+#ifdef Q_OS_WIN
+ 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.toStdString().c_str(), std::to_string(port).c_str(), &hints, res);
+ }
+
+ int testLatency(struct addrinfo *addr, system_clock::time_point *start, system_clock::time_point *end)
+ {
+ qv_socket_t fd;
+
+ const int on = 1;
+
+ /* 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)))
+ {
+ goto next_addr0;
+ }
+
+ // Windows needs special conversion.
+#ifdef _WIN32
+ 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;
+ *start = system_clock::now();
+ if (connect_wait(fd, addr->ai_addr, addr->ai_addrlen) == 0)
+ {
+ *end = system_clock::now();
+#ifdef Q_OS_WIN
+ closesocket(fd);
+#else
+ close(fd);
+#endif
+ return 0;
+ }
+
+ next_addr1:
+#ifdef _WIN32
+ closesocket(fd);
+#else
+ close(fd);
+#endif
+ next_addr0:
+ addr = addr->ai_next;
+ }
+
+ return -1;
+ }
+
+ LatencyTestResult TestTCPLatency(const QString &host, int port, int testCount)
+ {
+ LatencyTestResult data;
+ int successCount = 0;
+ addrinfo *resolved;
+ int errcode;
+
+ if ((errcode = resolveHost(host, port, &resolved)) != 0)
+ {
+#ifdef Q_OS_WIN
+ data.errorMessage = QString::fromStdWString(gai_strerror(errcode));
+#else
+ data.errorMessage = gai_strerror(errcode);
+#endif
+ return data;
+ }
+
+ int currentCount = 0;
+
+ data.avg = 0;
+ data.worst = 0;
+ data.best = 0;
+
+ while (currentCount < testCount)
+ {
+ system_clock::time_point start;
+ system_clock::time_point end;
+
+ if ((errcode = testLatency(resolved, &start, &end)) != 0)
+ {
+ LOG(MODULE_NETWORK, "error connecting to host: " + host + ":" + QSTRN(port) + " " + strerror(errno))
+ }
+ else
+ {
+ successCount++;
+ auto milliseconds = std::chrono::duration_cast(end - start);
+ long ms = milliseconds.count();
+ data.avg += ms;
+#ifdef Q_OS_WIN
+ // Is it Windows?
+ #undef min
+ #undef max
+#endif
+ data.worst = std::min(data.worst, ms);
+ data.best = std::max(data.best, ms);
+
+ if (ms > 1000)
+ {
+ LOG(MODULE_NETWORK, "Stop the test on the first long connect()")
+ break; /* Stop the test on the first long connect() */
+ }
+ }
+
+ currentCount++;
+ QThread::msleep(200);
+ }
+ freeaddrinfo(resolved);
+ if (successCount > 0)
+ {
+ data.avg = data.avg / successCount;
+ }
+ else
+ {
+ data.avg = LATENCY_TEST_VALUE_ERROR;
+ data.worst = LATENCY_TEST_VALUE_ERROR;
+ data.best = LATENCY_TEST_VALUE_ERROR;
+ data.errorMessage = QObject::tr("Timeout");
+ }
+ return data;
+ }
+} // namespace Qv2ray::components::latency::tcping
diff --git a/src/components/latency/TCPing.hpp b/src/components/latency/TCPing.hpp
new file mode 100644
index 00000000..134c8df8
--- /dev/null
+++ b/src/components/latency/TCPing.hpp
@@ -0,0 +1,7 @@
+#pragma once
+#include "LatencyTest.hpp"
+#include "base/Qv2rayBase.hpp"
+namespace Qv2ray::components::latency::tcping
+{
+ LatencyTestResult TestTCPLatency(const QString &host, int port, int testCount);
+} // namespace Qv2ray::components::latency::tcping
diff --git a/src/components/latency/unix/ICMPPing.cpp b/src/components/latency/unix/ICMPPing.cpp
new file mode 100644
index 00000000..7f899e37
--- /dev/null
+++ b/src/components/latency/unix/ICMPPing.cpp
@@ -0,0 +1,156 @@
+/* Author: Maciek Muszkowski
+ *
+ * This is a simple ping implementation for Linux.
+ * It will work ONLY on kernels 3.x+ and you need
+ * to set allowed groups in /proc/sys/net/ipv4/ping_group_range */
+#include "ICMPPing.hpp"
+
+#include
+#ifdef Q_OS_UNIX
+ #include
+ //
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+ #ifdef Q_OS_MAC
+ #define SOL_IP 0
+ #endif
+
+namespace Qv2ray::components::latency::icmping
+{
+ /// 1s complementary checksum
+ uint16_t ping_checksum(const char *buf, size_t size)
+ {
+ size_t i;
+ uint64_t sum = 0;
+
+ for (i = 0; i < size; i += 2)
+ {
+ sum += *(uint16_t *) buf;
+ buf += 2;
+ }
+ if (size - i > 0)
+ {
+ sum += *(uint8_t *) buf;
+ }
+
+ while ((sum >> 16) != 0)
+ {
+ sum = (sum & 0xffff) + (sum >> 16);
+ }
+
+ return (uint16_t) ~sum;
+ }
+ void ICMPPing::deinit()
+ {
+ if (socketId >= 0)
+ {
+ close(socketId);
+ socketId = -1;
+ }
+ }
+
+ ICMPPing::ICMPPing(int ttl)
+ {
+ auto timeout_s = 5;
+ // create socket
+ if ( //((sd = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) &&
+ ((socketId = socket(PF_INET, SOCK_DGRAM, IPPROTO_ICMP)) < 0))
+ {
+ initErrorMessage = "EPING_SOCK: " + QObject::tr("Socket creation failed");
+ return;
+ }
+ // set TTL
+ if (setsockopt(socketId, SOL_IP, IP_TTL, &ttl, sizeof(ttl)) != 0)
+ {
+ deinit();
+ initErrorMessage = "EPING_TTL: " + QObject::tr("Failed to setup TTL value");
+ return;
+ }
+
+ // set timeout in secs (do not use secs - BUGGY)
+ timeval timeout;
+ timeout.tv_sec = timeout_s;
+ timeout.tv_usec = 0;
+ if (setsockopt(socketId, SOL_SOCKET, SO_RCVTIMEO, (char *) &timeout, sizeof(timeout)) != 0)
+ {
+ deinit();
+ initErrorMessage = "EPING_SETTO: " + QObject::tr("Setting timeout failed");
+ return;
+ }
+ initialized = true;
+ }
+
+ /// @return value < 0 on error, response time in ms on success
+ QPair ICMPPing::ping(const QString &address)
+ {
+ if (!initialized)
+ {
+ return { 0, initErrorMessage };
+ }
+ timeval start, end;
+ socklen_t slen;
+ // not initialized
+ if (socketId < 0)
+ return { 0, "EPING_SOCK:" + QObject::tr("Socket creation failed") };
+
+ // resolve hostname
+ hostent *resolvedAddress = gethostbyname(address.toStdString().c_str());
+ if (!resolvedAddress)
+ return { 0, "EPING_HOST: " + QObject::tr("Unresolvable hostname") };
+
+ // set IP address to ping
+ sockaddr_in targetAddress;
+ memset(&targetAddress, 0, sizeof(targetAddress));
+ targetAddress.sin_family = resolvedAddress->h_addrtype;
+ targetAddress.sin_port = 0;
+ memcpy(&targetAddress.sin_addr, resolvedAddress->h_addr, resolvedAddress->h_length);
+
+ // prepare echo request packet
+ icmp _icmp_request;
+ memset(&_icmp_request, 0, sizeof(_icmp_request));
+ _icmp_request.icmp_type = ICMP_ECHO;
+ _icmp_request.icmp_hun.ih_idseq.icd_id = 0; // SOCK_DGRAM & 0 => id will be set by kernel
+ unsigned short sent_seq;
+ _icmp_request.icmp_hun.ih_idseq.icd_seq = sent_seq = seq++;
+ _icmp_request.icmp_cksum = ping_checksum(reinterpret_cast(&_icmp_request), sizeof(_icmp_request));
+
+ // send echo request
+ gettimeofday(&start, NULL);
+ if (sendto(socketId, &_icmp_request, sizeof(icmp), 0, (struct sockaddr *) &targetAddress, sizeof(targetAddress)) <= 0)
+ return { 0, "EPING_SEND: " + QObject::tr("Sending echo request failed") };
+
+ // receive response (if any)
+ sockaddr_in remove_addr;
+ slen = sizeof(remove_addr);
+ int rlen;
+ icmp resp;
+ while ((rlen = recvfrom(socketId, &resp, sizeof(icmp), 0, (struct sockaddr *) &remove_addr, &slen)) > 0)
+ {
+ gettimeofday(&end, NULL);
+
+ // skip malformed
+ if (rlen != sizeof(icmp))
+ continue;
+
+ // skip the ones we didn't send
+ if (resp.icmp_hun.ih_idseq.icd_seq != sent_seq)
+ continue;
+
+ switch (resp.icmp_type)
+ {
+ case ICMP_ECHOREPLY: return { 1000000 * (end.tv_sec - start.tv_sec) + (end.tv_usec - start.tv_usec), {} };
+ case ICMP_UNREACH: return { 0, "EPING_DST: " + QObject::tr("Destination unreachable") };
+ case ICMP_TIMXCEED: return { 0, "EPING_TIME: " + QObject::tr("Timeout") };
+ default: return { 0, "EPING_UNK: " + QObject::tr("Unknown error") };
+ }
+ }
+ return { 0, "EPING_TIME: " + QObject::tr("Timeout") };
+ }
+} // namespace Qv2ray::components::latency::icmping
+#endif
diff --git a/src/components/latency/unix/ICMPPing.hpp b/src/components/latency/unix/ICMPPing.hpp
new file mode 100644
index 00000000..987086fc
--- /dev/null
+++ b/src/components/latency/unix/ICMPPing.hpp
@@ -0,0 +1,28 @@
+#pragma once
+#include
+#ifdef Q_OS_UNIX
+ #include
+ #include
+namespace Qv2ray::components::latency::icmping
+{
+ class ICMPPing
+ {
+ public:
+ explicit ICMPPing(int ttl);
+ ~ICMPPing()
+ {
+ deinit();
+ }
+ QPair ping(const QString &address);
+
+ private:
+ void deinit();
+ // number incremented with every echo request packet send
+ unsigned short seq = 1;
+ // socket
+ int socketId = -1;
+ bool initialized = false;
+ QString initErrorMessage;
+ };
+} // namespace Qv2ray::components::latency::icmping
+#endif
diff --git a/src/components/latency/win/ICMPPing.cpp b/src/components/latency/win/ICMPPing.cpp
new file mode 100644
index 00000000..74342742
--- /dev/null
+++ b/src/components/latency/win/ICMPPing.cpp
@@ -0,0 +1,87 @@
+#include "ICMPPing.hpp"
+#ifdef Q_OS_WIN
+//
+ #include
+//
+ #include
+//
+ #include
+//
+ #include
+//
+ #include
+ #include
+namespace Qv2ray::components::latency::icmping
+{
+ ICMPPing::ICMPPing(uint64_t timeout)
+ {
+
+ // remember the timeout
+ // UNUSED
+ this->timeout = timeout;
+ }
+
+ ICMPPing ::~ICMPPing()
+ {
+ }
+ QPair ICMPPing::ping(const QString &ipAddr)
+ {
+ HANDLE hIcmpFile;
+ // create icmp handle
+ if ((hIcmpFile = IcmpCreateFile()) == INVALID_HANDLE_VALUE)
+ {
+ throw "IcmpCreateFile failed";
+ }
+ QList addresses;
+ {
+ QEventLoop loop;
+ QHostInfo::fromName(ipAddr).lookupHost(ipAddr, &loop, [&](const QHostInfo &h) {
+ for (const auto &addr : h.addresses())
+ {
+ if (addr.protocol() == QAbstractSocket::IPv4Protocol)
+ {
+ addresses << addr;
+ }
+ }
+ loop.quit();
+ });
+ loop.exec();
+ }
+ if (addresses.isEmpty())
+ {
+ return { 0, QObject::tr("DNS lookup failed.") };
+ }
+ // Parse the destination IP address.
+ IN_ADDR dest_ip{};
+ if (1 != InetPtonA(AF_INET, addresses.first().toString().toStdString().c_str(), &dest_ip))
+ {
+ return { 255, "Cannot convert IP address: " + addresses.first().toString() };
+ }
+
+ // Payload to send.
+ constexpr WORD payload_size = 1;
+ unsigned char payload[payload_size]{ 42 };
+
+ // Reply buffer for exactly 1 echo reply, payload data, and 8 bytes for ICMP error message.
+ constexpr DWORD reply_buf_size = sizeof(ICMP_ECHO_REPLY) + payload_size + 8;
+ unsigned char reply_buf[reply_buf_size]{};
+
+ // Make the echo request.
+ DWORD reply_count = IcmpSendEcho(hIcmpFile, dest_ip.S_un.S_addr, payload, payload_size, NULL, reply_buf, reply_buf_size, 10000);
+
+ // Return value of 0 indicates failure, try to get error info.
+ if (reply_count == 0)
+ {
+ auto e = GetLastError();
+ DWORD buf_size = 1000;
+ TCHAR buf[1000];
+ GetIpErrorString(e, buf, &buf_size);
+ return { 255, "IcmpSendEcho returned error (" + QString::fromStdWString(buf) + ")" };
+ }
+ // release the handle on destruction
+ IcmpCloseHandle(hIcmpFile);
+ const ICMP_ECHO_REPLY *r = (const ICMP_ECHO_REPLY *) reply_buf;
+ return QPair(r->RoundTripTime * 1000, QString{});
+ }
+} // namespace Qv2ray::components::latency::icmping
+#endif
diff --git a/src/components/latency/win/ICMPPing.hpp b/src/components/latency/win/ICMPPing.hpp
new file mode 100644
index 00000000..9b9b922b
--- /dev/null
+++ b/src/components/latency/win/ICMPPing.hpp
@@ -0,0 +1,35 @@
+#pragma once
+
+/**
+ * ICMPPinger - An Implementation of ICMPPing on Windows Platform
+ * Required Windows Version: 2000 / XP / 7 / Vista+
+ * License: WTFPL
+ */
+#include
+#ifdef Q_OS_WIN
+
+ #include
+ #include
+ #include
+ #include
+ #include
+
+namespace Qv2ray::components::latency::icmping
+{
+ class ICMPPing
+ {
+ public:
+ ICMPPing(uint64_t timeout = DEFAULT_TIMEOUT);
+ ~ICMPPing();
+
+ public:
+ static const uint64_t DEFAULT_TIMEOUT = 10000U;
+
+ public:
+ QPair ping(const QString &ipAddr);
+
+ private:
+ uint64_t timeout = DEFAULT_TIMEOUT;
+ };
+} // namespace Qv2ray::components::latency::icmping
+#endif
diff --git a/src/components/latency/win/ICMPPinger.cpp b/src/components/latency/win/ICMPPinger.cpp
deleted file mode 100644
index f57cee55..00000000
--- a/src/components/latency/win/ICMPPinger.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-#include "ICMPPinger.hpp"
-#if 0
-
-ICMPPinger::ICMPPinger(UINT64 timeout = DEFAULT_TIMEOUT)
-{
- // create icmp handle
- if ((this->hIcmpFile = IcmpCreateFile()) == INVALID_HANDLE_VALUE)
- {
- throw "IcmpCreateFile failed";
- }
-
- // remember the timeout
- this->timeout = timeout;
-}
-
-ICMPPinger::~ICMPPinger()
-{
- // release the handle on destruction
- IcmpCloseHandle(this->hIcmpFile);
-}
-
-std::pair, std::optional> ICMPPinger::ping(const std::string &ipAddr)
-{
- // convert network address
- const auto addr = inet_addr(ipAddr.c_str());
- if (addr == INADDR_NONE)
- {
- return std::pair(std::nullopt, "invalid ip address: " + ipAddr);
- }
-
- // request buffer
- const static char bufRequest[] = "echo test";
-
- // response buffer
- const auto responseSize = sizeof(ICMP_ECHO_REPLY) + sizeof(bufRequest);
- const std::unique_ptr bufRecv(new (char[responseSize]));
-
- // send echo
- auto ret = IcmpSendEcho(this->hIcmpFile, addr, (LPVOID) bufRequest, sizeof(bufRequest), NULL, bufRecv.get(), responseSize, this->timeout);
-
- // ret == 0: failed
- if (ret == 0)
- {
- return std::pair(std::nullopt, "IcmpSendEcho returned error");
- }
-
- // read round-trip time
- PICMP_ECHO_REPLY pReply = (PICMP_ECHO_REPLY) bufRecv.get();
- return std::pair(pReply->RoundTripTime, std::nullopt);
-}
-
-#endif
diff --git a/src/components/latency/win/ICMPPinger.hpp b/src/components/latency/win/ICMPPinger.hpp
deleted file mode 100644
index 5c5ca460..00000000
--- a/src/components/latency/win/ICMPPinger.hpp
+++ /dev/null
@@ -1,34 +0,0 @@
-#pragma once
-#include
-#include
-#if 0
-/**
- * ICMPPinger - An Implementation of ICMPPing on Windows Platform
- * Required Windows Version: 2000 / XP / 7 / Vista+
- * License: WTFPL
- */
-
- #include
- #include
- #include
- #include
- #include
- #include
-
-class ICMPPinger
-{
- public:
- ICMPPinger(UINT64 timeout = DEFAULT_TIMEOUT);
- ~ICMPPinger();
-
- public:
- static const UINT64 DEFAULT_TIMEOUT = 10000U;
-
- public:
- std::pair, std::optional> ping(const std::string &ipAddr);
-
- private:
- HANDLE hIcmpFile;
- UINT64 timeout = DEFAULT_TIMEOUT;
-};
-#endif
diff --git a/src/components/ntp/QvNTPClient.cpp b/src/components/ntp/QvNTPClient.cpp
new file mode 100644
index 00000000..9611d4ce
--- /dev/null
+++ b/src/components/ntp/QvNTPClient.cpp
@@ -0,0 +1,216 @@
+/* This file from part of QNtp, a library that implements NTP protocol.
+ *
+ * Copyright (C) 2011 Alexander Fokin
+ *
+ * QNtp is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * QNtp 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with QNtp. If not, see . */
+
+#include "QvNTPClient.hpp"
+
+#include
+
+namespace Qv2ray::components::ntp
+{
+
+ NtpTimestamp NtpTimestamp::fromDateTime(const QDateTime &dateTime)
+ {
+ qint64 ntpMSecs = dateTime.toMSecsSinceEpoch() - january_1_1900;
+
+ quint32 seconds = ntpMSecs / 1000;
+ quint32 fraction = 0x100000000ll * (ntpMSecs % 1000) / 1000;
+
+ NtpTimestamp result;
+ result.seconds = qToBigEndian(seconds);
+ result.fraction = qToBigEndian(fraction);
+ return result;
+ }
+
+ QDateTime NtpTimestamp::toDateTime(const NtpTimestamp &ntpTime)
+ {
+ /* Convert to local-endian. */
+ quint32 seconds = qFromBigEndian(ntpTime.seconds);
+ quint32 fraction = qFromBigEndian(ntpTime.fraction);
+
+ /* Convert NTP timestamp to number of milliseconds passed since Jan 1 1900. */
+ qint64 ntpMSecs = seconds * 1000ll + fraction * 1000ll / 0x100000000ll;
+
+ /* Construct Qt date time. */
+ return QDateTime::fromMSecsSinceEpoch(ntpMSecs + january_1_1900);
+ }
+
+ NtpReply::NtpReply() : d(new NtpReplyPrivate())
+ {
+ /* We don't use shared null because NtpReplyPrivate isn't a POD type and
+ * allocation overhead is negligible here. */
+ memset(&d->packet, 0, sizeof(d->packet));
+ }
+
+ NtpReply::NtpReply(NtpReplyPrivate *dd) : d(dd)
+ {
+ Q_ASSERT(dd != NULL);
+ }
+
+ NtpReply::NtpReply(const NtpReply &other) : d(other.d)
+ {
+ }
+
+ NtpReply::~NtpReply()
+ {
+ }
+
+ NtpReply &NtpReply::operator=(const NtpReply &other)
+ {
+ d = other.d;
+
+ return *this;
+ }
+
+ NtpLeapIndicator NtpReply::leapIndicator() const
+ {
+ return static_cast(d->packet.basic.flags.leapIndicator);
+ }
+
+ quint8 NtpReply::versionNumber() const
+ {
+ return d->packet.basic.flags.versionNumber;
+ }
+
+ NtpMode NtpReply::mode() const
+ {
+ return static_cast(d->packet.basic.flags.mode);
+ }
+
+ quint8 NtpReply::stratum() const
+ {
+ return d->packet.basic.stratum;
+ }
+
+ qreal NtpReply::pollInterval() const
+ {
+ return std::pow(static_cast(2), static_cast(d->packet.basic.poll));
+ }
+
+ qreal NtpReply::precision() const
+ {
+ return std::pow(static_cast(2), static_cast(d->packet.basic.precision));
+ }
+
+ QDateTime NtpReply::referenceTime() const
+ {
+ return NtpTimestamp::toDateTime(d->packet.basic.referenceTimestamp);
+ }
+
+ QDateTime NtpReply::originTime() const
+ {
+ return NtpTimestamp::toDateTime(d->packet.basic.originateTimestamp);
+ }
+
+ QDateTime NtpReply::receiveTime() const
+ {
+ return NtpTimestamp::toDateTime(d->packet.basic.receiveTimestamp);
+ }
+
+ QDateTime NtpReply::transmitTime() const
+ {
+ return NtpTimestamp::toDateTime(d->packet.basic.transmitTimestamp);
+ }
+
+ QDateTime NtpReply::destinationTime() const
+ {
+ return d->destinationTime;
+ }
+
+ qint64 NtpReply::roundTripDelay() const
+ {
+ return originTime().msecsTo(destinationTime()) - receiveTime().msecsTo(transmitTime());
+ }
+
+ qint64 NtpReply::localClockOffset() const
+ {
+ return (originTime().msecsTo(receiveTime()) + destinationTime().msecsTo(transmitTime())) / 2;
+ }
+
+ bool NtpReply::isNull() const
+ {
+ return d->destinationTime.isNull();
+ }
+
+ NtpClient::NtpClient(QObject *parent) : QObject(parent)
+ {
+ init(QHostAddress::Any, 0);
+ }
+
+ NtpClient::NtpClient(const QHostAddress &bindAddress, quint16 bindPort, QObject *parent) : QObject(parent)
+ {
+ init(bindAddress, bindPort);
+ }
+
+ void NtpClient::init(const QHostAddress &bindAddress, quint16 bindPort)
+ {
+ mSocket = new QUdpSocket(this);
+ mSocket->bind(bindAddress, bindPort);
+
+ connect(mSocket, SIGNAL(readyRead()), this, SLOT(readPendingDatagrams()));
+ }
+
+ NtpClient::~NtpClient()
+ {
+ return;
+ }
+
+ bool NtpClient::sendRequest(const QHostAddress &address, quint16 port)
+ {
+ if (mSocket->state() != QAbstractSocket::BoundState)
+ return false;
+
+ /* Initialize the NTP packet. */
+ NtpPacket packet;
+ memset(&packet, 0, sizeof(packet));
+ packet.flags.mode = ClientMode;
+ packet.flags.versionNumber = 4;
+ packet.transmitTimestamp = NtpTimestamp::fromDateTime(QDateTime::currentDateTimeUtc());
+
+ /* Send it. */
+ if (mSocket->writeDatagram(reinterpret_cast(&packet), sizeof(packet), address, port) < 0)
+ return false;
+
+ return true;
+ }
+
+ void NtpClient::readPendingDatagrams()
+ {
+ while (mSocket->hasPendingDatagrams())
+ {
+ NtpFullPacket packet;
+ memset(&packet, 0, sizeof(packet));
+
+ QHostAddress address;
+ quint16 port;
+
+ if (mSocket->readDatagram(reinterpret_cast(&packet), sizeof(packet), &address, &port) < (qint64) sizeof(NtpPacket))
+ continue;
+
+ QDateTime now = QDateTime::currentDateTime();
+
+ /* Prepare reply. */
+ NtpReplyPrivate *replyPrivate = new NtpReplyPrivate();
+ replyPrivate->packet = packet;
+ replyPrivate->destinationTime = now;
+ NtpReply reply(replyPrivate);
+
+ /* Notify. */
+ Q_EMIT replyReceived(address, port, reply);
+ }
+ }
+
+} // namespace Qv2ray::components::ntp
diff --git a/src/components/ntp/QvNTPClient.hpp b/src/components/ntp/QvNTPClient.hpp
new file mode 100644
index 00000000..b42d1827
--- /dev/null
+++ b/src/components/ntp/QvNTPClient.hpp
@@ -0,0 +1,165 @@
+/* This file from part of QNtp, a library that implements NTP protocol.
+ *
+ * Copyright (C) 2011 Alexander Fokin
+ *
+ * QNtp is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * QNtp 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with QNtp. If not, see . */
+
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace Qv2ray::components::ntp
+{
+ const qint64 january_1_1900 = -2208988800000ll;
+
+ enum NtpLeapIndicator
+ {
+ NoWarning = 0, /**< No warning. */
+ LastMinute61Warning = 1, /**< Last minute has 61 seconds. */
+ LastMinute59Warning = 2, /**< Last minute has 59 seconds. */
+ UnsynchronizedWarning = 3, /**< Alarm condition (clock not synchronized). */
+ };
+
+ enum NtpMode
+ {
+ ReservedMode = 0, /**< Reserved. */
+ SymmetricActiveMode = 1, /**< Symmetric active. */
+ SymmetricPassiveMode = 2, /**< Symmetric passive. */
+ ClientMode = 3, /**< Client. */
+ ServerMode = 4, /**< Server. */
+ BroadcastMode = 5, /**< Broadcast. */
+ ControlMode = 6, /**< NTP control message. */
+ PrivateMode = 7, /**< Reserved for private use. */
+ };
+
+ enum NtpStratum
+ {
+ UnspecifiedStratum = 0, /**< Unspecified or unavailable. */
+ PrimaryStratum = 1, /**< Primary reference (e.g. radio-clock). */
+ SecondaryStratumFirst = 2, /**< Secondary reference (via NTP or SNTP). */
+ SecondaryStratumLast = 15,
+ UnsynchronizedStratum = 16, /**< Unsynchronized. */
+ /* 17-255 are reserved. */
+ };
+
+ struct NtpPacketFlags
+ {
+ unsigned char mode : 3;
+ unsigned char versionNumber : 3;
+ unsigned char leapIndicator : 2;
+ };
+
+#pragma pack(push, 1)
+
+ struct NtpTimestamp
+ {
+ quint32 seconds;
+ quint32 fraction;
+ static inline NtpTimestamp fromDateTime(const QDateTime &dateTime);
+ static inline QDateTime toDateTime(const NtpTimestamp &ntpTime);
+ };
+
+ struct NtpPacket
+ {
+ NtpPacketFlags flags;
+ quint8 stratum;
+ qint8 poll;
+ qint8 precision;
+ qint32 rootDelay;
+ qint32 rootDispersion;
+ qint8 referenceID[4];
+ NtpTimestamp referenceTimestamp;
+ NtpTimestamp originateTimestamp;
+ NtpTimestamp receiveTimestamp;
+ NtpTimestamp transmitTimestamp;
+ };
+
+ struct NtpAuthenticationInfo
+ {
+ quint32 keyId;
+ quint8 messageDigest[16];
+ };
+
+ struct NtpFullPacket
+ {
+ NtpPacket basic;
+ NtpAuthenticationInfo auth;
+ };
+
+#pragma pack(pop)
+
+ class NtpReplyPrivate : public QSharedData
+ {
+ public:
+ NtpFullPacket packet;
+ QDateTime destinationTime;
+ };
+
+ class NtpReply
+ {
+ public:
+ NtpReply();
+ NtpReply(const NtpReply &other);
+ ~NtpReply();
+ NtpReply &operator=(const NtpReply &other);
+ NtpLeapIndicator leapIndicator() const;
+ quint8 versionNumber() const;
+ NtpMode mode() const;
+ quint8 stratum() const;
+ qreal pollInterval() const;
+ qreal precision() const;
+ QDateTime referenceTime() const;
+ QDateTime originTime() const;
+ QDateTime receiveTime() const;
+ QDateTime transmitTime() const;
+ QDateTime destinationTime() const;
+ qint64 roundTripDelay() const;
+ qint64 localClockOffset() const;
+ bool isNull() const;
+
+ protected:
+ friend class NtpClient;
+ NtpReply(NtpReplyPrivate *dd);
+
+ private:
+ QSharedDataPointer d;
+ };
+
+ class NtpClient : public QObject
+ {
+ Q_OBJECT
+
+ public:
+ NtpClient(QObject *parent = NULL);
+ NtpClient(const QHostAddress &bindAddress, quint16 bindPort, QObject *parent = NULL);
+ virtual ~NtpClient();
+ bool sendRequest(const QHostAddress &address, quint16 port);
+
+ Q_SIGNALS:
+ void replyReceived(const QHostAddress &address, quint16 port, const NtpReply &reply);
+
+ private Q_SLOTS:
+ void readPendingDatagrams();
+
+ private:
+ void init(const QHostAddress &bindAddress, quint16 bindPort);
+
+ QUdpSocket *mSocket;
+ };
+} // namespace Qv2ray::components::ntp
diff --git a/src/components/plugins/QvPluginHost.cpp b/src/components/plugins/QvPluginHost.cpp
index 2bb40117..6da577a8 100644
--- a/src/components/plugins/QvPluginHost.cpp
+++ b/src/components/plugins/QvPluginHost.cpp
@@ -55,23 +55,23 @@ namespace Qv2ray::components::plugins
info.pluginLoader->unload();
continue;
}
- info.metadata = info.pluginInterface->GetMetadata();
- if (plugins.contains(info.metadata.InternalName))
- {
- LOG(MODULE_PLUGINHOST, "Found another plugin with the same internal name: " + info.metadata.InternalName + ". Skipped")
- continue;
- }
if (info.pluginInterface->QvPluginInterfaceVersion != QV2RAY_PLUGIN_INTERFACE_VERSION)
{
// The plugin was built for a not-compactable version of Qv2ray. Don't load the plugin by default.
- LOG(MODULE_PLUGINHOST, info.metadata.InternalName + " is built with an older Interface, ignoring")
+ LOG(MODULE_PLUGINHOST, info.libraryPath + " is built with an older Interface, ignoring")
QvMessageBoxWarn(nullptr, tr("Cannot load plugin"),
- info.metadata.Name + " " + tr("cannot be loaded.") + NEWLINE NEWLINE +
+ tr("The plugin located here cannot be loaded: ") + NEWLINE + info.libraryPath + NEWLINE NEWLINE +
tr("This plugin was built against an older/newer version of the Plugin Interface.") + NEWLINE +
tr("Please contact the plugin provider or report the issue to Qv2ray Workgroup."));
continue;
}
+ info.metadata = info.pluginInterface->GetMetadata();
+ if (plugins.contains(info.metadata.InternalName))
+ {
+ LOG(MODULE_PLUGINHOST, "Found another plugin with the same internal name: " + info.metadata.InternalName + ". Skipped")
+ continue;
+ }
connect(plugin, SIGNAL(PluginLog(const QString &)), this, SLOT(QvPluginLog(const QString &)));
connect(plugin, SIGNAL(PluginErrorMessageBox(const QString &)), this, SLOT(QvPluginMessageBox(const QString &)));
LOG(MODULE_PLUGINHOST, "Loaded plugin: \"" + info.metadata.Name + "\" made by: \"" + info.metadata.Author + "\"")
@@ -120,6 +120,7 @@ namespace Qv2ray::components::plugins
{
// Load plugin if it haven't been loaded.
InitializePlugin(internalName);
+
QvMessageBoxInfo(nullptr, tr("Enabling a plugin"), tr("The plugin will become fully functional after restarting Qv2ray."));
}
}
@@ -241,14 +242,14 @@ namespace Qv2ray::components::plugins
return data;
}
- const QMultiHash> QvPluginHost::TryDeserializeShareLink(const QString &sharelink, //
- QString *prefix, //
+ const QList> QvPluginHost::TryDeserializeShareLink(const QString &sharelink, //
+ QString *aliasPrefix, //
QString *errMessage, //
QString *newGroupName, //
bool *status) const
{
Q_UNUSED(newGroupName)
- QMultiHash> data;
+ QList> data;
*status = true;
for (const auto &plugin : plugins)
{
@@ -262,9 +263,9 @@ namespace Qv2ray::components::plugins
}
if (thisPluginCanHandle)
{
- auto [protocol, outboundSettings] = serializer->DeserializeOutbound(sharelink, prefix, errMessage);
+ const auto &[protocol, outboundSettings] = serializer->DeserializeOutbound(sharelink, aliasPrefix, errMessage);
*status = *status && errMessage->isEmpty();
- data.insert(*prefix, { protocol, outboundSettings });
+ data << std::tuple{ *aliasPrefix, protocol, outboundSettings };
}
}
}
@@ -278,7 +279,7 @@ namespace Qv2ray::components::plugins
if (plugin.isLoaded && plugin.metadata.SpecialPluginType.contains(SPECIAL_TYPE_SERIALIZOR))
{
auto serializer = plugin.pluginInterface->GetSerializer();
- if (serializer->OutboundProtocols().contains(protocol))
+ if (serializer && serializer->OutboundProtocols().contains(protocol))
{
auto info = serializer->GetOutboundInfo(protocol, o);
*status = true;
@@ -300,7 +301,7 @@ namespace Qv2ray::components::plugins
if (plugin.isLoaded && plugin.metadata.SpecialPluginType.contains(SPECIAL_TYPE_SERIALIZOR))
{
auto serializer = plugin.pluginInterface->GetSerializer();
- if (serializer->OutboundProtocols().contains(protocol))
+ if (serializer && serializer->OutboundProtocols().contains(protocol))
{
auto link = serializer->SerializeOutbound(protocol, alias, groupName, outboundSettings);
*status = true;
@@ -311,18 +312,31 @@ namespace Qv2ray::components::plugins
return "";
}
- const QMap> QvPluginHost::GetPluginKernels() const
+ const std::unique_ptr QvPluginHost::CreatePluginKernel(const QString &pluginInternalName) const
{
- QMap> kernels;
+ if (!plugins.contains(pluginInternalName))
+ return nullptr;
+ const auto &plugin = plugins.value(pluginInternalName);
+ if (plugin.isLoaded && plugin.metadata.SpecialPluginType.contains(SPECIAL_TYPE_KERNEL))
+ {
+ return plugin.pluginInterface->CreateKernel();
+ }
+ return nullptr;
+ }
+
+ const QMap> QvPluginHost::GetPluginKernels() const
+ {
+ QMap> kernels;
for (const auto &plugin : plugins)
{
if (plugin.isLoaded && plugin.metadata.SpecialPluginType.contains(SPECIAL_TYPE_KERNEL))
{
- auto kern = plugin.pluginInterface->GetKernel();
- for (const auto &cap : kern->KernelOutboundCapabilities())
+ QStringList outbounds;
+ for (const auto &info : plugin.metadata.KernelOutboundCapabilities)
{
- kernels.insert(cap.protocol, kern);
+ outbounds << info.protocol;
}
+ kernels.insert(plugin.metadata.InternalName, outbounds);
}
}
return kernels;
diff --git a/src/components/plugins/QvPluginHost.hpp b/src/components/plugins/QvPluginHost.hpp
index a0c7a981..9bdc5fcb 100644
--- a/src/components/plugins/QvPluginHost.hpp
+++ b/src/components/plugins/QvPluginHost.hpp
@@ -1,5 +1,5 @@
#pragma once
-#include "components/plugins/interface/QvPluginInterface.hpp"
+#include "src/plugin-interface/QvPluginInterface.hpp"
#include