Merge branch 'dev' into dev-master

This commit is contained in:
DuckSoft 2020-07-05 17:42:12 +08:00 committed by GitHub
commit b9fdbdd1a4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
233 changed files with 36955 additions and 12745 deletions

View File

@ -45,7 +45,7 @@ Please paste your Qv2ray log here:
<!-- Please check all the operating systems and installation sources that you confirmed to have problems. -->
### 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
<!-- Feel free to write down any info you consider helpful to resolve the bug. -->
*Please hide your server address and UUID if you wish to post the vmess string or your connection setting.*

View File

@ -82,3 +82,6 @@ assignees: ''
## 附加信息
<!-- 你认为对修复 bug 有帮助的任何信息都可以在此写出来。 -->
*请注意打码隐私相关信息。*

View File

@ -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'

View File

@ -21,6 +21,7 @@ jobs:
linux:
strategy:
fail-fast: false
matrix:
distro: [stable, unstable]
needs: check_commit_msg
@ -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

View File

@ -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 }}

16
.gitmodules vendored
View File

@ -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

View File

@ -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

@ -1 +1 @@
Subproject commit db07dd4ffcbfdd62431584d499928e45b5864f40
Subproject commit 427b6fe13049aae62855b893b524f17278e8c06f

@ -1 +1 @@
Subproject commit 4baf2e74f64c9a6ce36d456491bb41d0f2ae999e
Subproject commit bae7c331ca7203a242e4533ba859c0c6016521ba

1
3rdparty/backward-cpp vendored Submodule

@ -0,0 +1 @@
Subproject commit 29e4061494e1ac4e40b2b09fd3e35c310ca137fc

1
3rdparty/uistyles vendored Submodule

@ -0,0 +1 @@
Subproject commit ccbc5a7ec83229e18804bf6b650b181f5279ca74

1
3rdparty/x2struct vendored

@ -1 +0,0 @@
Subproject commit 4539764671509655370b2ff4fae90bfd8a12df40

2
3rdparty/zxing-cpp vendored

@ -1 +1 @@
Subproject commit 66cc26b25633cb7f1e20f2bf7711960c321e3a7b
Subproject commit 6d40262e7293666909f7325075d452637f2fca75

View File

@ -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()
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)
if (ANDROID)
else()
include(cmake/singleapplication.cmake)
include(cmake/protobuf.cmake)
include(cmake/cpp-httplib.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
if(QV2RAY_AUTO_DEPLOY)
add_custom_command(TARGET qv2ray POST_BUILD
COMMAND ${Qt5_DIR}/../../../bin/macdeployqt ${CMAKE_BINARY_DIR}/qv2ray.app
)
set(APPS "\${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}.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 .)
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/
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()
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()

View File

@ -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

View File

@ -34,5 +34,20 @@
<string>NSApplication</string>
<key>NSHighResolutionCapable</key>
<string>True</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLIconFile</key>
<string>Icon</string>
<key>CFBundleURLName</key>
<string>com.qv2ray.qv2ray</string>
<key>CFBundleURLSchemes</key>
<array>
<string>qv2ray</string>
</array>
</dict>
</array>
</dict>
</plist>

View File

@ -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

View File

@ -1,15 +1,23 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
p, li { white-space: pre-wrap; }
</style></head><body style=" font-family:'WenQuanYi Micro Hei'; font-size:10pt; font-weight:400; font-style:normal;">
<html>
<head>
<meta name="qrichtext" content="1" />
<style type="text/css">
p,
li {
white-space: pre-wrap;
}
</style>
</head>
<body style=" font-family:'WenQuanYi Micro Hei'; font-size:10pt; font-weight:400; font-style:normal;">
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MonoSpace'; font-weight:600; color:#d68952;">This program comes with ABSOLUTELY NO WARRANTY.</span></p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MonoSpace'; font-weight:600; color:#d68952;">This is free software, and you are welcome to redistribute it under certain conditions.</span></p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MonoSpace'; color:#d68952;">Copyright (c) 2019-2020 Qv2ray Development Group.</span></p>
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Noto Sans'; color:#d68952;"><br /></p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MonoSpace'; font-weight:600; color:#d68952;">Libraries that have been used in Qv2ray are listed below (Sorted by date added):</span></p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MonoSpace'; color:#d68952;">Copyright (c) 2020 xyz347 (xyz347): X2Struct (Apache)</span></p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MonoSpace'; color:#d68952;">Copyright (c) 2011 SCHUTZ Sacha (@dridk): QJsonModel (MIT)</span></p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MonoSpace'; color:#d68952;">Copyright (c) 2020 Nikolaos Ftylitakis (@ftylitak): QZXing (Apache2)</span></p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MonoSpace'; color:#d68952;">Copyright (c) 2016 Singein (@Singein): ScreenShot (MIT)</span></p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MonoSpace'; color:#d68952;">Copyright (c) 2020 Itay Grudev (@itay-grudev): SingleApplication (MIT)</span></p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MonoSpace'; color:#d68952;">Copyright (c) 2020 paceholder (@paceholder): nodeeditor (Qv2ray group modified version) (BSD-3-Clause)</span></p>
@ -18,4 +26,7 @@ p, li { white-space: pre-wrap; }
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MonoSpace'; color:#d68952;">Copyright (c) 2019 ShadowSocks (@shadowsocks): libQtShadowsocks (LGPLv3)</span></p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MonoSpace'; color:#d68952;">Copyright (c) 2015-2020 qBittorrent (Anton Lashkov) (@qBittorrent): speedplotview (GPLv2)</span></p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MonoSpace'; color:#d68952;">Copyright (c) 2020 Diffusions Nu-book Inc. (nu-book): zxing-cpp (Apache)</span></p>
</body></html>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MonoSpace'; color:#d68952;">Copyright (c) 2020 feiyangqingyun: QWidgetDemo (Mulan PSL v1)</span></p>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 KiB

View File

@ -0,0 +1,237 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
inkscape:export-ydpi="512"
inkscape:export-xdpi="512"
sodipodi:docname="Applogo_Bird_b.svg"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
id="svg8"
version="1.1"
viewBox="0 0 12.699999 12.7"
height="48"
width="48">
<defs
id="defs2">
<linearGradient
id="linearGradient908"
inkscape:collect="always">
<stop
id="stop904"
offset="0"
style="stop-color:#6e7678;stop-opacity:1" />
<stop
id="stop906"
offset="1"
style="stop-color:#2e3235;stop-opacity:1" />
</linearGradient>
<linearGradient
id="linearGradient847"
inkscape:collect="always">
<stop
id="stop843"
offset="0"
style="stop-color:#00e0ff;stop-opacity:1" />
<stop
id="stop845"
offset="1"
style="stop-color:#1ba7f9;stop-opacity:1" />
</linearGradient>
<linearGradient
id="linearGradient5057"
inkscape:collect="always">
<stop
id="stop5053"
offset="0"
style="stop-color:#3c3c3c;stop-opacity:1;" />
<stop
id="stop5055"
offset="1"
style="stop-color:#282728;stop-opacity:0.6574803" />
</linearGradient>
<linearGradient
id="linearGradient4759"
inkscape:collect="always">
<stop
id="stop4755"
offset="0"
style="stop-color:#00b7ff;stop-opacity:1" />
<stop
id="stop4757"
offset="1"
style="stop-color:#1c90ff;stop-opacity:1" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient4713">
<stop
style="stop-color:#3de256;stop-opacity:1"
offset="0"
id="stop4709" />
<stop
style="stop-color:#2fbeba;stop-opacity:1"
offset="1"
id="stop4711" />
</linearGradient>
<linearGradient
gradientTransform="translate(38.574145,1.3181723)"
gradientUnits="userSpaceOnUse"
y2="39.934193"
x2="108.00179"
y1="19.458199"
x1="89.965836"
id="linearGradient4707"
xlink:href="#linearGradient4713"
inkscape:collect="always" />
<linearGradient
gradientUnits="userSpaceOnUse"
y2="94.022179"
x2="140.26065"
y1="48.940449"
x1="92.227142"
id="linearGradient5059"
xlink:href="#linearGradient5057"
inkscape:collect="always" />
<linearGradient
y2="35.515835"
x2="113.08728"
y1="28.293249"
x1="85.229973"
gradientTransform="matrix(0.34597512,0.06144722,-0.06655638,0.31941658,-25.1488,274.41205)"
gradientUnits="userSpaceOnUse"
id="linearGradient953"
xlink:href="#linearGradient4759"
inkscape:collect="always" />
<linearGradient
y2="208.23155"
x2="72.363083"
y1="196.65211"
x1="39.630962"
gradientTransform="matrix(0.15666471,0,0,0.15666471,-5.1793298,256.3406)"
gradientUnits="userSpaceOnUse"
id="linearGradient955"
xlink:href="#linearGradient847"
inkscape:collect="always" />
<clipPath
id="clipPath922"
clipPathUnits="userSpaceOnUse">
<rect
ry="1.0638391"
y="284.56458"
x="9.5367426e-08"
height="12.170834"
width="12.7"
id="rect924"
style="fill:none;fill-opacity:1;stroke:#23d829;stroke-width:0.044;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.72265625" />
</clipPath>
<linearGradient
gradientTransform="matrix(0.87500008,0,0,0.87505491,0.81442048,36.315602)"
gradientUnits="userSpaceOnUse"
y2="297"
x2="12.7"
y1="284.29999"
x1="12.7"
id="linearGradient910"
xlink:href="#linearGradient908"
inkscape:collect="always" />
</defs>
<sodipodi:namedview
inkscape:document-rotation="0"
inkscape:snap-to-guides="false"
inkscape:snap-grids="false"
inkscape:snap-global="false"
inkscape:snap-text-baseline="false"
inkscape:snap-center="false"
inkscape:object-nodes="false"
inkscape:snap-intersection-paths="false"
inkscape:object-paths="false"
inkscape:snap-others="false"
inkscape:snap-nodes="false"
units="px"
inkscape:snap-midpoints="false"
inkscape:snap-bbox-midpoints="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:bbox-nodes="true"
inkscape:bbox-paths="true"
inkscape:snap-bbox="true"
inkscape:guide-bbox="true"
showguides="true"
inkscape:snap-smooth-nodes="false"
inkscape:snap-object-midpoints="false"
inkscape:window-maximized="1"
inkscape:window-y="26"
inkscape:window-x="0"
inkscape:window-height="1028"
inkscape:window-width="1920"
showgrid="false"
inkscape:current-layer="g44"
inkscape:document-units="mm"
inkscape:cy="21.316088"
inkscape:cx="23.875406"
inkscape:zoom="22.599304"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#ffffff"
id="base">
<inkscape:grid
id="grid885"
type="xygrid" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(0,-284.29999)"
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Layer 1">
<g
transform="matrix(0.77674917,0,0,0.77993751,1.4662318,64.012038)"
id="g889">
<g
transform="matrix(1.1963196,0,0,1.1963196,-1.3111729,-57.928707)"
id="g44">
<path
style="fill:#2782bd;fill-opacity:1;stroke:none;stroke-width:0.02480469px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 4.8977933,290.46043 c 0,0 -0.3822449,0.7135 -0.5365115,0.93777 -0.154295,0.22431 -0.5217135,0.4236 -0.5217135,0.4236 l 0.6747961,-0.0816 1.7063455,-0.35106 c 0,0 -0.7913874,-1.07043 -1.3229166,-0.92869 z"
id="path879-7"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cscccc" />
<path
sodipodi:nodetypes="cscc"
inkscape:connector-curvature="0"
id="path4715-0"
d="m 4.1456075,290.71139 c 0,0 1.1173813,-2.75102 3.3019109,-0.59828 2.1845208,2.15274 3.6949266,5.47345 3.6949266,5.47345 0,0 -2.9944646,-4.26031 -6.9968375,-4.87517 z"
style="fill:#008dff;fill-opacity:1;stroke:none;stroke-width:0.08956835px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path4715-6-93"
d="m 2.2137987,290.23705 c 0,0 -0.1508518,-2.94653 2.7747518,-1.81527 2.9256022,1.13127 6.6242245,4.28351 6.6242245,4.28351 0,0 -4.81123,-3.07932 -9.3989763,-2.46824 z"
style="fill:url(#linearGradient953);fill-opacity:1;stroke:none;stroke-width:0.08956835px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
sodipodi:nodetypes="cscc"
inkscape:connector-curvature="0"
id="path4715-6-9-6"
d="m 1.2940265,289.53029 c 0,0 -1.32291659,-2.64583 1.9849644,-2.35592 3.1595803,0.27691 7.8046181,2.09134 7.8046181,2.09134 0,0 -5.6043094,-1.52438 -9.7895825,0.26458 z"
style="fill:url(#linearGradient955);fill-opacity:1;stroke:none;stroke-width:0.08956835px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;opacity:1" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.0 KiB

View File

@ -0,0 +1,237 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
inkscape:export-ydpi="512"
inkscape:export-xdpi="512"
sodipodi:docname="Applogo_Bird_colorful.svg"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
id="svg8"
version="1.1"
viewBox="0 0 12.699999 12.7"
height="48"
width="48">
<defs
id="defs2">
<linearGradient
id="linearGradient908"
inkscape:collect="always">
<stop
id="stop904"
offset="0"
style="stop-color:#6e7678;stop-opacity:1" />
<stop
id="stop906"
offset="1"
style="stop-color:#2e3235;stop-opacity:1" />
</linearGradient>
<linearGradient
id="linearGradient847"
inkscape:collect="always">
<stop
id="stop843"
offset="0"
style="stop-color:#ffacad;stop-opacity:1" />
<stop
id="stop845"
offset="1"
style="stop-color:#ff5c5b;stop-opacity:1" />
</linearGradient>
<linearGradient
id="linearGradient5057"
inkscape:collect="always">
<stop
id="stop5053"
offset="0"
style="stop-color:#3c3c3c;stop-opacity:1;" />
<stop
id="stop5055"
offset="1"
style="stop-color:#282728;stop-opacity:0.6574803" />
</linearGradient>
<linearGradient
id="linearGradient4759"
inkscape:collect="always">
<stop
id="stop4755"
offset="0"
style="stop-color:#ef3cff;stop-opacity:1" />
<stop
id="stop4757"
offset="1"
style="stop-color:#1c90ff;stop-opacity:1" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient4713">
<stop
style="stop-color:#3de256;stop-opacity:1"
offset="0"
id="stop4709" />
<stop
style="stop-color:#2fbeba;stop-opacity:1"
offset="1"
id="stop4711" />
</linearGradient>
<linearGradient
gradientTransform="translate(38.574145,1.3181723)"
gradientUnits="userSpaceOnUse"
y2="39.934193"
x2="108.00179"
y1="19.458199"
x1="89.965836"
id="linearGradient4707"
xlink:href="#linearGradient4713"
inkscape:collect="always" />
<linearGradient
gradientUnits="userSpaceOnUse"
y2="94.022179"
x2="140.26065"
y1="48.940449"
x1="92.227142"
id="linearGradient5059"
xlink:href="#linearGradient5057"
inkscape:collect="always" />
<linearGradient
y2="35.515835"
x2="113.08728"
y1="28.293249"
x1="85.229973"
gradientTransform="matrix(0.34597512,0.06144722,-0.06655638,0.31941658,-25.1488,274.41205)"
gradientUnits="userSpaceOnUse"
id="linearGradient953"
xlink:href="#linearGradient4759"
inkscape:collect="always" />
<linearGradient
y2="208.23155"
x2="72.363083"
y1="196.65211"
x1="39.630962"
gradientTransform="matrix(0.15666471,0,0,0.15666471,-5.1793298,256.3406)"
gradientUnits="userSpaceOnUse"
id="linearGradient955"
xlink:href="#linearGradient847"
inkscape:collect="always" />
<clipPath
id="clipPath922"
clipPathUnits="userSpaceOnUse">
<rect
ry="1.0638391"
y="284.56458"
x="9.5367426e-08"
height="12.170834"
width="12.7"
id="rect924"
style="fill:none;fill-opacity:1;stroke:#23d829;stroke-width:0.044;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.72265625" />
</clipPath>
<linearGradient
gradientTransform="matrix(0.87500008,0,0,0.87505491,0.81442048,36.315602)"
gradientUnits="userSpaceOnUse"
y2="297"
x2="12.7"
y1="284.29999"
x1="12.7"
id="linearGradient910"
xlink:href="#linearGradient908"
inkscape:collect="always" />
</defs>
<sodipodi:namedview
inkscape:document-rotation="0"
inkscape:snap-to-guides="false"
inkscape:snap-grids="false"
inkscape:snap-global="false"
inkscape:snap-text-baseline="false"
inkscape:snap-center="false"
inkscape:object-nodes="false"
inkscape:snap-intersection-paths="false"
inkscape:object-paths="false"
inkscape:snap-others="false"
inkscape:snap-nodes="false"
units="px"
inkscape:snap-midpoints="false"
inkscape:snap-bbox-midpoints="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:bbox-nodes="true"
inkscape:bbox-paths="true"
inkscape:snap-bbox="true"
inkscape:guide-bbox="true"
showguides="true"
inkscape:snap-smooth-nodes="false"
inkscape:snap-object-midpoints="false"
inkscape:window-maximized="1"
inkscape:window-y="26"
inkscape:window-x="0"
inkscape:window-height="1028"
inkscape:window-width="1920"
showgrid="false"
inkscape:current-layer="g44"
inkscape:document-units="mm"
inkscape:cy="25.276329"
inkscape:cx="27.278388"
inkscape:zoom="15.980121"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#ffffff"
id="base">
<inkscape:grid
id="grid885"
type="xygrid" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(0,-284.29999)"
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Layer 1">
<g
transform="matrix(0.77674917,0,0,0.77993751,1.4662318,64.012038)"
id="g889">
<g
transform="matrix(1.1963196,0,0,1.1963196,-1.3111729,-57.928707)"
id="g44">
<path
style="fill:#275bbd;fill-opacity:1;stroke:none;stroke-width:0.02480469px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 4.8977933,290.46043 c 0,0 -0.3822449,0.7135 -0.5365115,0.93777 -0.154295,0.22431 -0.5217135,0.4236 -0.5217135,0.4236 l 0.6747961,-0.0816 1.7063455,-0.35106 c 0,0 -0.7913874,-1.07043 -1.3229166,-0.92869 z"
id="path879-7"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cscccc" />
<path
sodipodi:nodetypes="cscc"
inkscape:connector-curvature="0"
id="path4715-0"
d="m 4.1456075,290.71139 c 0,0 1.1173813,-2.75102 3.3019109,-0.59828 2.1845208,2.15274 3.6949266,5.47345 3.6949266,5.47345 0,0 -2.9944646,-4.26031 -6.9968375,-4.87517 z"
style="fill:#008dff;fill-opacity:1;stroke:none;stroke-width:0.08956835px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path4715-6-93"
d="m 2.2137987,290.23705 c 0,0 -0.1508518,-2.94653 2.7747518,-1.81527 2.9256022,1.13127 6.6242245,4.28351 6.6242245,4.28351 0,0 -4.81123,-3.07932 -9.3989763,-2.46824 z"
style="fill:url(#linearGradient953);fill-opacity:1;stroke:none;stroke-width:0.08956835px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
sodipodi:nodetypes="cscc"
inkscape:connector-curvature="0"
id="path4715-6-9-6"
d="m 1.2940265,289.53029 c 0,0 -1.32291659,-2.64583 1.9849644,-2.35592 3.1595803,0.27691 7.8046181,2.09134 7.8046181,2.09134 0,0 -5.6043094,-1.52438 -9.7895825,0.26458 z"
style="fill:url(#linearGradient955);fill-opacity:1;stroke:none;stroke-width:0.08956835px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;opacity:1" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.0 KiB

View File

@ -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" }
]
}

View File

@ -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

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop-application">
<id>com.github.Qv2ray</id>
<metadata_license>CC0-1.0</metadata_license>
<project_license>GPL-3.0</project_license>
<metadata_license>GPL-3.0+</metadata_license>
<project_license>GPL-3.0+</project_license>
<name>Qv2ray</name>
<summary>Qv2ray is a cross-platform v2ray graphical frontend written in Qt.</summary>

View File

@ -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"

View File

@ -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'

View File

@ -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")

46
cmake/backward-cpp.cmake Normal file
View File

@ -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 $<TARGET_OBJECTS:backward_object> 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})

View File

@ -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
)

View File

@ -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
)

View File

@ -1 +0,0 @@
set(cpp-httplib_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/3rdparty/cpp-httplib)

View File

@ -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)

4
cmake/libsemver.cmake Normal file
View File

@ -0,0 +1,4 @@
set(LIBSEMVER_SOURCES
${CMAKE_SOURCE_DIR}/3rdparty/libsemver/version.cpp
${CMAKE_SOURCE_DIR}/3rdparty/libsemver/version.hpp
)

View File

@ -1,5 +1,5 @@
find_package(Protobuf REQUIRED)
protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS ${CMAKE_SOURCE_DIR}/tools/v2ray_geosite.proto)
protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS ${CMAKE_SOURCE_DIR}/assets/v2ray_geosite.proto)
set(QV2RAY_PROTOBUF_LIBRARY
protobuf::libprotobuf
)

View File

@ -24,11 +24,13 @@ if(QV2RAY_QNODEEDITOR_PROVIDER STREQUAL "module")
${QNODEEDITOR_DIR}/src/Properties.cpp
${QNODEEDITOR_DIR}/src/StyleCollection.cpp
)
set(QNODEEDITOR_INCLUDE_PATH
${QNODEEDITOR_DIR}/include
${QNODEEDITOR_DIR}/include/nodes/internal
)
set(HEADERS_TO_MOC
set(QNODEEDITOR_HEADERS
${QNODEEDITOR_DIR}/include/nodes/internal/Compiler.hpp
${QNODEEDITOR_DIR}/include/nodes/internal/Connection.hpp
${QNODEEDITOR_DIR}/include/nodes/internal/ConnectionGeometry.hpp
@ -58,15 +60,27 @@ if(QV2RAY_QNODEEDITOR_PROVIDER STREQUAL "module")
${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()

View File

@ -1,6 +1,11 @@
add_definitions(-DQAPPLICATION_CLASS=QApplication)
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()

View File

@ -137,6 +137,13 @@ if(QV2RAY_ZXING_PROVIDER STREQUAL "module")
${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)

44
debian/changelog vendored
View File

@ -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 <ymshenyu@gmail.com> 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 <ymshenyu@gmail.com> 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 <ymshenyu@gmail.com> 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 <ymshenyu@gmail.com> Sun, 17 May 2020 20:23:35 +0800
qv2ray (2.5.0-1) unstable; urgency=medium
* updating translations

6
debian/control vendored
View File

@ -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.

4
debian/rules vendored
View File

@ -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

12
hooks/pre-commit Executable file
View File

@ -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

1
libs/QJsonStruct Submodule

@ -0,0 +1 @@
Subproject commit 91c3ca1c3279448052b6be19dc8157517c35a7ca

View File

@ -1 +1 @@
5335
5697

View File

@ -1 +1 @@
2.5.0
2.6.0

View File

@ -1 +1 @@
-rc4

View File

@ -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
- 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

541
src/Qv2rayApplication.cpp Normal file
View File

@ -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 <QUrl>
#include <QUrlQuery>
#ifdef Q_OS_WIN
#include <Winbase.h>
#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<QvTranslator>();
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<QString, QString> 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

108
src/Qv2rayApplication.hpp Normal file
View File

@ -0,0 +1,108 @@
#pragma once
#include "libs/QJsonStruct/QJsonStruct.hpp"
#include <QSystemTrayIcon>
#ifdef Q_OS_ANDROID
#include <QApplication>
#else
#include <SingleApplication>
#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<Argument> arguments;
QString version;
QString data;
QList<QString> links;
QList<QString> 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<Qv2ray::Qv2rayApplication *>(QCoreApplication::instance()))
#define qvAppTrayIcon (*qvApp->GetTrayIcon())

91
src/StackTraceHelper.cpp Normal file
View File

@ -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<size_t>(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

36
src/StackTraceHelper.hpp Normal file
View File

@ -0,0 +1,36 @@
#pragma once
#include <QString>
#ifdef Q_OS_UNIX
#include "backward.hpp"
#include <vector>
#endif
#ifdef Q_OS_WIN
#include <Windows.h>
//
#include <DbgHelp.h>
#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

View File

@ -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); \
}

View File

@ -1,9 +1,7 @@
#pragma once
//
#include <QApplication>
#include <QMap>
#include <QtCore>
#include <QtGui>
#include <algorithm>
#include <ctime>
#include <iostream>
@ -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);
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

View File

@ -1,9 +1,22 @@
#pragma once
#include <QtGlobal>
// 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)

View File

@ -4,6 +4,14 @@
#include <iostream>
#ifdef Q_OS_ANDROID
#include <android/log.h>
#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);

View File

@ -1,7 +1,6 @@
#pragma once
#include <QString>
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));

View File

@ -1,5 +1,6 @@
#pragma once
#include "3rdparty/x2struct/x2struct.hpp"
#include "libs/QJsonStruct/QJsonIO.hpp"
#include "libs/QJsonStruct/QJsonStruct.hpp"
#include <QList>
#include <QMap>
@ -7,13 +8,49 @@
namespace Qv2ray::base::objects
{
struct DNSObject
{
struct DNSServerObject
{
bool QV2RAY_DNS_IS_COMPLEX_DNS;
QString address;
int port;
QList<QString> domains;
QList<QString> 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<QString, QString> hosts;
QList<DNSServerObject> 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<QString> 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<QString, LevelPolicyObject> level;
QList<SystemPolicyObject> 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<QString> 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<QString> path;
QMap<QString, QList<QString>> 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<QString, QList<QString>> 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<QString, QString> 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<QString> 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<QString> certificate;
QList<QString> 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<QString> alpn;
QList<CertificateObject> 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<QString> 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<UserObject> 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<UserObject> 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<UserObject> 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

View File

@ -1,52 +1,180 @@
#pragma once
#include "3rdparty/x2struct/x2struct.hpp"
#include "QvCoreSettings.hpp"
#include "libs/QJsonStruct/QJsonStruct.hpp"
#include <QHash>
#include <QHashFunctions>
#include <QString>
#include <QtCore>
namespace Qv2ray::base
{
constexpr unsigned int QVTCPING_VALUE_ERROR = 99999;
constexpr unsigned int QVTCPING_VALUE_NODATA = QVTCPING_VALUE_ERROR - 1;
using namespace std::chrono;
// Common struct for Groups and Subscriptions
struct GroupObject_Config
template<typename T>
class IDType
{
QString displayName;
QList<QString> connections;
int64_t importDate;
GroupObject_Config() : displayName(), connections(), importDate()
public:
explicit IDType() : m_id("null"){};
explicit IDType(const QString &id) : m_id(id){};
friend bool operator==(const IDType<T> &lhs, const IDType<T> &rhs)
{
return lhs.m_id == rhs.m_id;
}
XTOSTRUCT(O(displayName, connections, importDate))
friend bool operator!=(const IDType<T> &lhs, const IDType<T> &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;
};
struct SubscriptionObject_Config : GroupObject_Config
{
// 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;
//
QString address;
int64_t lastUpdated;
float updateInterval;
SubscriptionObject_Config() : address(""), lastUpdated(system_clock::to_time_t(system_clock::now())), updateInterval(10)
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;
}
XTOSTRUCT(O(lastUpdated, updateInterval, address, connections, displayName, importDate))
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;
struct ConnectionObject_Config
struct __Qv2rayConfigObjectBase
{
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))
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 GroupRoutingConfig : __Qv2rayConfigObjectBase
{
bool overrideDNS;
config::QvConfig_DNS dnsConfig;
//
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))
};
enum SubscriptionFilterRelation
{
RELATION_AND = 0,
RELATION_OR = 1
};
struct SubscriptionConfigObject
{
QString address;
float updateInterval;
SubscriptionFilterRelation IncludeRelation;
SubscriptionFilterRelation ExcludeRelation;
QList<QString> IncludeKeywords;
QList<QString> 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<ConnectionId> 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<typename T>
inline uint qHash(IDType<T> 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)

View File

@ -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<QString> direct;
QList<QString> block;
QList<QString> 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<QString> &_direct, const QList<QString> &_block, const QList<QString> &_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

View File

@ -8,6 +8,7 @@ namespace Qv2ray::base
struct Qv2rayRuntimeConfig
{
bool screenShotHideQv2ray = false;
bool deepinHorribleProxyHint = false;
};
inline base::Qv2rayRuntimeConfig RuntimeConfig = base::Qv2rayRuntimeConfig();
} // namespace Qv2ray::base

View File

@ -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

View File

@ -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 <chrono>
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<QvBarLine> Lines;
QvBarPage() : OffsetYpx(5)
{
}
XTOSTRUCT(O(OffsetYpx, Lines))
};
struct Qv2rayToolBarConfig
{
QList<QvBarPage> 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<QString> recentConnections;
QList<ConnectionGroupPair> 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<QString> direct;
QList<QString> block;
QList<QString> 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<QString> &_direct, const QList<QString> &_block, const QList<QString> &_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<QString, bool> 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<QString> 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
{
QVPROXY_NONE,
QVPROXY_SYSTEM,
QVPROXY_CUSTOM
Qv2rayLatencyTestingMethod latencyTestingMethod;
enum Qv2rayProxyType : int
{
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<QString, GroupObject_Config> groups;
QMap<QString, SubscriptionObject_Config> subscriptions;
/// Connections are used privately.
QMap<QString, ConnectionObject_Config> connections;
// QList<GroupId> groups;
// QList<ConnectionId> 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

View File

@ -1,24 +1,26 @@
#pragma once
namespace Qv2ray
{
namespace base
namespace Qv2ray::base
{
struct QvStartupOptions
{
/// No API subsystem
bool noAPI;
/// Explicitly run as root user.
bool forceRunAsRootUser;
/// Enable Debug Log.
bool debugLog;
/// Enable Network toolbar plugin.
bool enableToolbarPlguin;
/// Disable Qt scale factors support.
bool noScaleFactors;
bool noScaleFactor;
/// Disable all plugin features.
bool noPlugins;
/// Exit existing Qv2ray instance
bool exitQv2ray;
///
};
} // namespace base
inline base::QvStartupOptions StartupOption = base::QvStartupOptions();
} // namespace Qv2ray
} // namespace Qv2ray::base
inline Qv2ray::base::QvStartupOptions StartupOption = Qv2ray::base::QvStartupOptions();

View File

@ -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

View File

@ -1,38 +0,0 @@
#pragma once
#include <QCommandLineParser>
namespace Qv2ray::common
{
enum CommandLineParseResult
{
CommandLineOk,
CommandLineError,
CommandLineVersionRequested,
CommandLineHelpRequested
};
class QvCommandArgParser : public QObject
{
Q_OBJECT
public:
QvCommandArgParser();
CommandLineParseResult ParseCommandLine(QString *errorMessage);
const QCommandLineParser *Parser()
{
return &parser;
}
private:
QCommandLineParser parser;
QCommandLineOption noAPIOption;
QCommandLineOption runAsRootOption;
QCommandLineOption debugOption;
QCommandLineOption noScaleFactorOption;
QCommandLineOption noPluginsOption;
QCommandLineOption withToolbarOption;
QCommandLineOption helpOption;
QCommandLineOption versionOption;
};
} // namespace Qv2ray::common
using namespace Qv2ray::common;

View File

@ -5,39 +5,30 @@
#include <QByteArray>
#include <QNetworkProxy>
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);
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<void(const QByteArray &)> 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);
}
void QvHttpRequestHelper::onRequestFinished_p()
{
if (reply->attribute(QNetworkRequest::HTTP2WasUsedAttribute).toBool())
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)
{
QString error = QMetaEnum::fromType<QNetworkReply::NetworkError>().key(reply->error());
LOG(MODULE_NETWORK, "Network request error string: " + error)
QByteArray empty;
emit OnRequestFinished(empty);
}
else
{
emit OnRequestFinished(this->data);
LOG(MODULE_NETWORK, "Network error: " + QString(QMetaEnum::fromType<QNetworkReply::NetworkError>().key(reply->error())))
funcPtr(reply->readAll());
}
});
}
void QvHttpRequestHelper::onReadyRead_p()
{
DEBUG(MODULE_NETWORK, "A request is now ready read")
this->data += reply->readAll();
}
} // namespace Qv2ray::common
} // namespace Qv2ray::common::network

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QObject>
#include <functional>
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<void(const QByteArray &)> 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;

View File

@ -26,7 +26,6 @@
#include <QDebug>
#include <QFile>
#include <QFont>
QJsonTreeItem::QJsonTreeItem(QJsonTreeItem *parent)
{

View File

@ -25,12 +25,12 @@
#pragma once
#include <QAbstractItemModel>
#include <QIcon>
#include <QByteArray>
#include <QIODevice>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
class QJsonModel;
class QJsonItem;

View File

@ -1,18 +1,13 @@
#include "common/QvHelpers.hpp"
#include "base/Qv2rayBase.hpp"
#include "libs/puresource/src/PureJson.hpp"
#include <QGraphicsEffect>
#include <QGraphicsProxyWidget>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QQueue>
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;
{
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<string> SplitLines_std(const QString &_string)
{
list<string> list;
for (auto line : _string.split(QRegExp("[\r\n]"), QString::SkipEmptyParts))
{
list.push_back(line.toStdString());
}
return list;
#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

View File

@ -1,8 +1,12 @@
#pragma once
#include "base/Qv2rayBase.hpp"
#include <QDateTime>
#include <QDir>
#include <QFile>
#include <QHostAddress>
#include <QJsonDocument>
#include <QMessageBox>
#include <QRegularExpression>
#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<string> SplitLines_std(const QString &_string);
// list<string> 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<typename TYPE>
QString StructToJsonString(const TYPE &t)
{
return QString::fromStdString(x2struct::X::tojson(t, "", 4, ' '));
}
//
template<typename TYPE>
TYPE StructFromJsonString(const QString &str)
{
TYPE v;
x2struct::X::loadjson(str.toStdString(), v, false);
return v;
}
// Misc
template<typename T>
QJsonObject GetRootObject(const T &t)
{
auto json = StructToJsonString(t);
return JsonFromString(json);
}
inline 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;

View File

@ -1,16 +1,8 @@
#include "QvTranslator.hpp"
#include "base/Qv2rayLog.hpp"
#include "base/Qv2rayBase.hpp"
#include "common/QvHelpers.hpp"
#include <QApplication>
#include <QDir>
#include <QStandardPaths>
#include <QString>
#include <QStringBuilder>
#include <QTranslator>
#include <memory>
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;
}
}

View File

@ -1,6 +1,8 @@
#include "QvAutoLaunch.hpp"
#include <QApplication>
#include "base/Qv2rayBase.hpp"
#include <QCoreApplication>
#include <QDir>
#include <QSettings>
#include <QStandardPaths>
@ -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

View File

@ -1,9 +1,10 @@
#include "DarkmodeDetector.hpp"
#include <QtGlobal>
#ifdef Q_OS_LINUX
#include "base/Qv2rayBase.hpp"
#include <QApplication>
#include <QStyle>
#ifdef Q_OS_LINUX
#elif defined(Q_OS_WIN32)
#include <QSettings>
#else
@ -16,7 +17,7 @@ namespace Qv2ray::components::darkmode
// Copyright (C) 2020 KeePassXC Team <team@keepassxc.org>
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
}

View File

@ -1,12 +1,22 @@
#include "QvGeositeReader.hpp"
#ifndef ANDROID
#include "v2ray_geosite.pb.h"
#endif
namespace Qv2ray::components::geosite
{
QMap<QString, QStringList> GeositeEntries;
QStringList ReadGeoSiteFromFile(const QString &filepath)
{
if (GeositeEntries.contains(filepath))
{
return GeositeEntries.value(filepath);
}
else
{
QStringList list;
#ifndef ANDROID
LOG(MODULE_FILEIO, "Reading geosites from: " + filepath)
//
GOOGLE_PROTOBUF_VERIFY_VERSION;
@ -26,7 +36,7 @@ namespace Qv2ray::components::geosite
GeoSiteList sites;
sites.ParseFromArray(content.data(), content.size());
for (auto e : sites.entry())
for (const auto &e : sites.entry())
{
// We want to use lower string.
list << QString::fromStdString(e.country_code()).toLower();
@ -35,7 +45,10 @@ namespace Qv2ray::components::geosite
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;
}
}
} // namespace Qv2ray::components::geosite

View File

@ -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;

View File

@ -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<LatencyTestThread *>(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<ConnectionId>(), result);
}
} // namespace Qv2ray::components::latency

View File

@ -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<LatencyTestThread *> latencyThreads;
};
} // namespace Qv2ray::components::latency
using namespace Qv2ray::components::latency;

View File

@ -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

View File

@ -0,0 +1,30 @@
#pragma once
#include "LatencyTest.hpp"
#include <QThread>
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

View File

@ -1,225 +0,0 @@
#ifdef _WIN32
#include <WS2tcpip.h>
#include <WinSock2.h>
#else
#include <netdb.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <unistd.h>
#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<QvTCPingResultObject>(this);
watcher->setFuture(QtConcurrent::run(&QvTCPingHelper::TestLatency_p, id, count));
pingWorkingThreads.enqueue(watcher);
//
connect(watcher, &QFutureWatcher<QvTCPingResultObject>::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<std::chrono::milliseconds>(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

View File

@ -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<QFutureWatcher<QvTCPingResultObject> *> pingWorkingThreads;
};
} // namespace Qv2ray::components::tcping
using namespace Qv2ray::components::tcping;

View File

@ -0,0 +1,364 @@
#include "TCPing.hpp"
#ifdef _WIN32
#include <WS2tcpip.h>
#include <WinSock2.h>
#else
#include <fcntl.h>
#include <netdb.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <unistd.h>
#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<std::chrono::milliseconds>(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

View File

@ -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

View File

@ -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 <QObject>
#ifdef Q_OS_UNIX
#include <unistd.h>
//
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <resolv.h>
#include <sys/socket.h>
#include <sys/time.h>
#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<int64_t, QString> 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<char *>(&_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

View File

@ -0,0 +1,28 @@
#pragma once
#include <QtGlobal>
#ifdef Q_OS_UNIX
#include <QPair>
#include <QString>
namespace Qv2ray::components::latency::icmping
{
class ICMPPing
{
public:
explicit ICMPPing(int ttl);
~ICMPPing()
{
deinit();
}
QPair<int64_t, QString> 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

View File

@ -0,0 +1,87 @@
#include "ICMPPing.hpp"
#ifdef Q_OS_WIN
//
#include <WS2tcpip.h>
//
#include <Windows.h>
//
#include <iphlpapi.h>
//
#include <IcmpAPI.h>
//
#include <QEventLoop>
#include <QHostInfo>
namespace Qv2ray::components::latency::icmping
{
ICMPPing::ICMPPing(uint64_t timeout)
{
// remember the timeout
// UNUSED
this->timeout = timeout;
}
ICMPPing ::~ICMPPing()
{
}
QPair<long, QString> ICMPPing::ping(const QString &ipAddr)
{
HANDLE hIcmpFile;
// create icmp handle
if ((hIcmpFile = IcmpCreateFile()) == INVALID_HANDLE_VALUE)
{
throw "IcmpCreateFile failed";
}
QList<QHostAddress> 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<long, QString>(r->RoundTripTime * 1000, QString{});
}
} // namespace Qv2ray::components::latency::icmping
#endif

View File

@ -0,0 +1,35 @@
#pragma once
/**
* ICMPPinger - An Implementation of ICMPPing on Windows Platform
* Required Windows Version: 2000 / XP / 7 / Vista+
* License: WTFPL
*/
#include <QtGlobal>
#ifdef Q_OS_WIN
#include <QPair>
#include <QString>
#include <memory>
#include <optional>
#include <utility>
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<long, QString> ping(const QString &ipAddr);
private:
uint64_t timeout = DEFAULT_TIMEOUT;
};
} // namespace Qv2ray::components::latency::icmping
#endif

View File

@ -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<UINT64>, std::optional<std::string>> 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<char> 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

View File

@ -1,34 +0,0 @@
#pragma once
#include <QtGlobal>
#include <optional>
#if 0
/**
* ICMPPinger - An Implementation of ICMPPing on Windows Platform
* Required Windows Version: 2000 / XP / 7 / Vista+
* License: WTFPL
*/
#include <icmpapi.h>
#include <iphlpapi.h>
#include <memory>
#include <optional>
#include <utility>
#include <winsock2.h>
class ICMPPinger
{
public:
ICMPPinger(UINT64 timeout = DEFAULT_TIMEOUT);
~ICMPPinger();
public:
static const UINT64 DEFAULT_TIMEOUT = 10000U;
public:
std::pair<std::optional<UINT64>, std::optional<std::string>> ping(const std::string &ipAddr);
private:
HANDLE hIcmpFile;
UINT64 timeout = DEFAULT_TIMEOUT;
};
#endif

View File

@ -0,0 +1,216 @@
/* This file from part of QNtp, a library that implements NTP protocol.
*
* Copyright (C) 2011 Alexander Fokin <apfokin@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>. */
#include "QvNTPClient.hpp"
#include <cmath>
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<NtpLeapIndicator>(d->packet.basic.flags.leapIndicator);
}
quint8 NtpReply::versionNumber() const
{
return d->packet.basic.flags.versionNumber;
}
NtpMode NtpReply::mode() const
{
return static_cast<NtpMode>(d->packet.basic.flags.mode);
}
quint8 NtpReply::stratum() const
{
return d->packet.basic.stratum;
}
qreal NtpReply::pollInterval() const
{
return std::pow(static_cast<qreal>(2), static_cast<qreal>(d->packet.basic.poll));
}
qreal NtpReply::precision() const
{
return std::pow(static_cast<qreal>(2), static_cast<qreal>(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<const char *>(&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<char *>(&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

View File

@ -0,0 +1,165 @@
/* This file from part of QNtp, a library that implements NTP protocol.
*
* Copyright (C) 2011 Alexander Fokin <apfokin@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>. */
#pragma once
#include <QDateTime>
#include <QHostAddress>
#include <QObject>
#include <QUdpSocket>
#include <QtEndian>
#include <QtGlobal>
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<NtpReplyPrivate> 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

View File

@ -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<QString, QPair<QString, QJsonObject>> QvPluginHost::TryDeserializeShareLink(const QString &sharelink, //
QString *prefix, //
const QList<std::tuple<QString, QString, QJsonObject>> QvPluginHost::TryDeserializeShareLink(const QString &sharelink, //
QString *aliasPrefix, //
QString *errMessage, //
QString *newGroupName, //
bool *status) const
{
Q_UNUSED(newGroupName)
QMultiHash<QString, QPair<QString, QJsonObject>> data;
QList<std::tuple<QString, QString, QJsonObject>> 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<QString, std::shared_ptr<QvPluginKernel>> QvPluginHost::GetPluginKernels() const
const std::unique_ptr<QvPluginKernel> QvPluginHost::CreatePluginKernel(const QString &pluginInternalName) const
{
QMap<QString, std::shared_ptr<QvPluginKernel>> 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<QString, QList<QString>> QvPluginHost::GetPluginKernels() const
{
QMap<QString, QList<QString>> 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;

Some files were not shown because too many files have changed in this diff Show More