30
.github/workflows/build-qv2ray-cmake.yml
vendored
@ -81,10 +81,7 @@ jobs:
|
||||
- name: macOS - ${{ matrix.qt_version }} - Build preparation - Install Packages
|
||||
if: matrix.platform == 'macos-latest'
|
||||
run: |
|
||||
brew install protobuf grpc ninja wget
|
||||
wget https://github.com/phracker/MacOSX-SDKs/releases/download/10.15/MacOSX10.14.sdk.tar.xz
|
||||
tar -xf MacOSX10.14.sdk.tar.xz
|
||||
sudo mv -v ./MacOSX10.14.sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk
|
||||
brew install protobuf grpc ninja
|
||||
# --------------------------------------------------------
|
||||
- name: Win-${{ matrix.arch }} - ${{ matrix.qt_version }} - Build preparation - Download Dependencies
|
||||
shell: bash
|
||||
@ -99,18 +96,23 @@ jobs:
|
||||
pathSource: ./libs/Qv2ray-deps-grpc-${{ matrix.arch }}-windows.7z
|
||||
pathTarget: ./libs
|
||||
# ========================================================================================================= Generate MakeFile and Build
|
||||
|
||||
- uses: actions/setup-node@v1
|
||||
if: matrix.platform == 'macos-latest'
|
||||
with:
|
||||
node-version: '10.x'
|
||||
- run: npm install -g appdmg
|
||||
if: matrix.platform == 'macos-latest'
|
||||
- name: macOS - ${{ matrix.qt_version }} - Generate Dependencies and Build
|
||||
shell: bash
|
||||
if: matrix.platform == 'macos-latest'
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_SYSROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk -DCMAKE_OSX_DEPLOYMENT_TARGET=10.14
|
||||
sudo cmake --build . --target package --parallel $(sysctl -n hw.logicalcpu)
|
||||
cp qv2ray-*.dmg ../
|
||||
- name: macOS - Get package name
|
||||
id: get_package
|
||||
run: echo ::set-output name=NAME::$(basename qv2ray-*.dmg)
|
||||
cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_DEPLOYMENT_TARGET=10.13 -DDS_STORE_SCRIPT=ON
|
||||
sudo cmake --build . --parallel $(sysctl -n hw.logicalcpu)
|
||||
sudo cmake --install .
|
||||
sudo appdmg ../assets/package_dmg.json ../Qv2ray.dmg
|
||||
# --------------------------------------------------------
|
||||
- name: Windows - ${{ matrix.qt_version }} - Generate Dependencies and Build
|
||||
shell: bash
|
||||
@ -177,15 +179,15 @@ jobs:
|
||||
if: matrix.platform == 'macos-latest'
|
||||
uses: actions/upload-artifact@master
|
||||
with:
|
||||
name: ${{ steps.get_package.outputs.NAME }}
|
||||
path: ${{ steps.get_package.outputs.NAME }}
|
||||
name: Qv2ray-${{ github.sha }}.macOS-${{ matrix.arch }}.qt${{ matrix.qt_version }}.dmg
|
||||
path: Qv2ray.dmg
|
||||
- name: macOS - ${{ matrix.qt_version }} - Upload binaries to release
|
||||
uses: svenstaro/upload-release-action@v1-release
|
||||
if: github.event_name == 'release' && matrix.platform == 'macos-latest' && matrix.qt_version == '5.14.2'
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: ${{ steps.get_package.outputs.NAME }}
|
||||
asset_name: ${{ steps.get_package.outputs.NAME }}
|
||||
file: Qv2ray.dmg
|
||||
asset_name: Qv2ray-${{ steps.get_version.outputs.VERSION }}.macOS-${{ matrix.arch }}.dmg
|
||||
tag: ${{ github.ref }}
|
||||
overwrite: true
|
||||
# --------------------------------------------------------
|
||||
|
6
.gitmodules
vendored
@ -10,12 +10,12 @@
|
||||
[submodule "libs/libqvb"]
|
||||
path = libs/libqvb
|
||||
url = https://github.com/Qv2ray/QvRPCBridge
|
||||
[submodule "3rdparty/cpp-httplib"]
|
||||
path = 3rdparty/cpp-httplib
|
||||
url = https://github.com/yhirose/cpp-httplib
|
||||
[submodule "libs/puresource"]
|
||||
path = libs/puresource
|
||||
url = https://github.com/Qv2ray/PureSource/
|
||||
[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
|
||||
url = https://github.com/Qv2ray/QvPlugin-Interface/
|
||||
|
41
.travis.yml
@ -1,17 +1,6 @@
|
||||
language: shell
|
||||
os: linux
|
||||
dist: bionic
|
||||
arch:
|
||||
- amd64
|
||||
- arm64
|
||||
|
||||
git:
|
||||
depth: false
|
||||
|
||||
branches:
|
||||
only:
|
||||
- dev
|
||||
- /^v\d+\.\d+(\.\d+)?(-\S*)?$/
|
||||
|
||||
env:
|
||||
global:
|
||||
@ -23,30 +12,22 @@ addons:
|
||||
- name: snapcraft
|
||||
channel: stable
|
||||
confinement: classic
|
||||
|
||||
before_install:
|
||||
- echo "deb http://ppa.launchpad.net/ymshenyu/qv2ray-deps/ubuntu bionic main" | sudo tee -a /etc/apt/sources.list
|
||||
- echo "deb http://archive.neon.kde.org/unstable bionic main" | sudo tee -a /etc/apt/sources.list
|
||||
- sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 281F24E574404629AA3BDA1A4F10C386C55CDB04
|
||||
- sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys E6D4736255751E5D
|
||||
- sudo apt-get update -qq
|
||||
- name: lxd
|
||||
channel: stable
|
||||
|
||||
script:
|
||||
- snapcraft --destructive-mode
|
||||
- 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
|
||||
|
||||
deploy:
|
||||
- provider: snap
|
||||
snap: qv2ray_*.snap
|
||||
channel: edge
|
||||
skip_cleanup: true
|
||||
- provider: launchpad
|
||||
slug: "~ymshenyu/qv2ray/+git/trunk"
|
||||
oauth_token: $LAUNCHPAD_OAUTH_TOKEN
|
||||
oauth_token_secret: $LAUNCHPAD_OAUTH_TOKEN_SECRET
|
||||
on:
|
||||
branch: dev
|
||||
- provider: snap
|
||||
snap: qv2ray_*.snap
|
||||
channel: beta
|
||||
skip_cleanup: true
|
||||
on:
|
||||
branch: /^v\d+\.\d+(\.\d+)?(-\S*)?$/
|
||||
all_branches: true
|
2
3rdparty/QNodeEditor
vendored
@ -1 +1 @@
|
||||
Subproject commit f3f17e9a04e3db67e4a717fd2984754fd4555c24
|
||||
Subproject commit db07dd4ffcbfdd62431584d499928e45b5864f40
|
2
3rdparty/SingleApplication
vendored
@ -1 +1 @@
|
||||
Subproject commit 4abe20afbfa5695ac7a9bce1298943b645aeffe9
|
||||
Subproject commit 4baf2e74f64c9a6ce36d456491bb41d0f2ae999e
|
1
3rdparty/cpp-httplib
vendored
@ -1 +0,0 @@
|
||||
Subproject commit e1acb949e74c663dc9dedc04e41d8bb0dfafb7c7
|
505
CMakeLists.txt
@ -42,7 +42,7 @@ set(CMAKE_AUTOUIC ON)
|
||||
cmake_policy(SET CMP0071 NEW)
|
||||
|
||||
if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.17.0")
|
||||
cmake_policy(SET CMP0100 NEW)
|
||||
cmake_policy(SET CMP0100 NEW)
|
||||
endif()
|
||||
|
||||
message(" ")
|
||||
@ -63,21 +63,21 @@ message("|-------------------------------------------------|")
|
||||
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)
|
||||
set(GUI_TYPE WIN32)
|
||||
if(NOT DEFINED CMAKE_TOOLCHAIN_FILE)
|
||||
if(CMAKE_CL_64)
|
||||
include(${CMAKE_SOURCE_DIR}/libs/x64-windows/scripts/buildsystems/vcpkg.cmake)
|
||||
else()
|
||||
include(${CMAKE_SOURCE_DIR}/libs/x86-windows/scripts/buildsystems/vcpkg.cmake)
|
||||
endif()
|
||||
endif()
|
||||
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)
|
||||
set(GUI_TYPE WIN32)
|
||||
if(NOT DEFINED CMAKE_TOOLCHAIN_FILE)
|
||||
if(CMAKE_CL_64)
|
||||
include(${CMAKE_SOURCE_DIR}/libs/x64-windows/scripts/buildsystems/vcpkg.cmake)
|
||||
else()
|
||||
include(${CMAKE_SOURCE_DIR}/libs/x86-windows/scripts/buildsystems/vcpkg.cmake)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
|
||||
endif()
|
||||
|
||||
set(QV2RAY_QNODEEDITOR_PROVIDER "module" CACHE STRING "qnodeeditor provider")
|
||||
@ -120,176 +120,185 @@ 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")
|
||||
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")
|
||||
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}")
|
||||
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}")
|
||||
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}")
|
||||
add_definitions(-DQV2RAY_TRANSLATION_PATH="${QV2RAY_TRANSLATION_PATH}")
|
||||
endif()
|
||||
|
||||
if(QV2RAY_USE_BUILTIN_DARKTHEME)
|
||||
add_definitions(-DQV2RAY_USE_BUILTIN_DARKTHEME=true)
|
||||
add_definitions(-DQV2RAY_USE_BUILTIN_DARKTHEME=true)
|
||||
endif()
|
||||
|
||||
if(QV2RAY_DISABLE_AUTO_UPDATE)
|
||||
add_definitions(-DDISABLE_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
|
||||
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/pac/QvGFWPACConverter.cpp
|
||||
src/components/pac/QvPACHandler.cpp
|
||||
src/components/plugins/toolbar/QvToolbar.cpp
|
||||
src/components/plugins/toolbar/QvToolbar_linux.cpp
|
||||
src/components/plugins/toolbar/QvToolbar_win.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/V2rayInstanceHandler.cpp
|
||||
src/core/kernel/APIBackend.cpp
|
||||
src/core/kernel/KernelInteractions.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_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_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/pac/QvPACHandler.hpp
|
||||
src/components/plugins/toolbar/QvToolbar.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/kernel/APIBackend.hpp
|
||||
src/core/kernel/KernelInteractions.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_ScreenShot_Core.hpp
|
||||
src/ui/w_SubscriptionManager.hpp
|
||||
assets/qv2ray.rc
|
||||
)
|
||||
${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)
|
||||
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
|
||||
@ -305,123 +314,125 @@ set(QT_LIBRARY
|
||||
Qt5::Gui
|
||||
Qt5::Widgets
|
||||
Qt5::Network
|
||||
)
|
||||
)
|
||||
|
||||
add_executable(${PROJECT_NAME}
|
||||
${GUI_TYPE}
|
||||
${QV2RAY_SOURCES}
|
||||
${QNODEEDITOR_SOURCES}
|
||||
${QNODEEDITOR_QRC_RESOURCES}
|
||||
${SINGLEAPPLICATION_SOURCES}
|
||||
${ZXING_SOURCES}
|
||||
${PROTO_SRCS}
|
||||
${PROTO_HDRS}
|
||||
${API_GRPC_SRCS}
|
||||
${API_PROTO_SRCS}
|
||||
${QRC_RESOURCES}
|
||||
${QM_FILES}
|
||||
)
|
||||
${GUI_TYPE}
|
||||
${QV2RAY_SOURCES}
|
||||
${QNODEEDITOR_SOURCES}
|
||||
${QNODEEDITOR_QRC_RESOURCES}
|
||||
${SINGLEAPPLICATION_SOURCES}
|
||||
${ZXING_SOURCES}
|
||||
${PROTO_SRCS}
|
||||
${PROTO_HDRS}
|
||||
${API_GRPC_SRCS}
|
||||
${API_PROTO_SRCS}
|
||||
${QRC_RESOURCES}
|
||||
${QM_FILES}
|
||||
)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME}
|
||||
${QT_LIBRARY}
|
||||
${QV2RAY_PROTOBUF_LIBRARY}
|
||||
${QV2RAY_BACKEND_LIBRARIES}
|
||||
${QNODEEDITOR_LIBRARY}
|
||||
${ZXING_LIBRARY}
|
||||
)
|
||||
${QT_LIBRARY}
|
||||
${QV2RAY_PROTOBUF_LIBRARY}
|
||||
${QV2RAY_BACKEND_LIBRARIES}
|
||||
${QNODEEDITOR_LIBRARY}
|
||||
${ZXING_LIBRARY}
|
||||
)
|
||||
|
||||
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE
|
||||
${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}
|
||||
)
|
||||
${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}
|
||||
)
|
||||
|
||||
if(APPLE)
|
||||
find_package(Iconv REQUIRED)
|
||||
target_link_libraries(${PROJECT_NAME}
|
||||
Iconv::Iconv
|
||||
"-framework Carbon"
|
||||
"-framework Cocoa"
|
||||
"-framework Security"
|
||||
)
|
||||
Iconv::Iconv
|
||||
"-framework Carbon"
|
||||
"-framework Cocoa"
|
||||
"-framework Security"
|
||||
)
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE
|
||||
${Iconv_INCLUDE_DIR}
|
||||
)
|
||||
${Iconv_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
set(MACOSX_ICON "${CMAKE_SOURCE_DIR}/assets/icons/qv2ray.icns")
|
||||
set(MACOSX_PLIST "${CMAKE_SOURCE_DIR}/assets/MacOSXBundleInfo.plist.in")
|
||||
set_source_files_properties(${QM_FILES}
|
||||
PROPERTIES
|
||||
MACOSX_PACKAGE_LOCATION Resources/lang
|
||||
)
|
||||
PROPERTIES
|
||||
MACOSX_PACKAGE_LOCATION Resources/lang
|
||||
)
|
||||
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
${MACOSX_ICON}
|
||||
)
|
||||
${MACOSX_ICON}
|
||||
)
|
||||
|
||||
set_target_properties(${PROJECT_NAME}
|
||||
PROPERTIES
|
||||
MACOSX_BUNDLE TRUE
|
||||
MACOSX_BUNDLE_BUNDLE_NAME "Qv2ray"
|
||||
MACOSX_BUNDLE_BUNDLE_VERSION ${QV2RAY_VERSION_STRING}
|
||||
MACOSX_BUNDLE_COPYRIGHT "Copyright (c) 2019-2020 Qv2ray Development Group"
|
||||
MACOSX_BUNDLE_GUI_IDENTIFIER "com.github.qv2ray"
|
||||
MACOSX_BUNDLE_ICON_FILE "qv2ray.icns"
|
||||
MACOSX_BUNDLE_INFO_STRING "Created by Qv2ray development team"
|
||||
MACOSX_BUNDLE_LONG_VERSION_STRING ${QV2RAY_VERSION_STRING}
|
||||
MACOSX_BUNDLE_SHORT_VERSION_STRING ${QV2RAY_VERSION_STRING}
|
||||
RESOURCE
|
||||
${MACOSX_ICON}
|
||||
)
|
||||
PROPERTIES
|
||||
MACOSX_BUNDLE TRUE
|
||||
MACOSX_BUNDLE_INFO_PLIST ${MACOSX_PLIST}
|
||||
MACOSX_BUNDLE_BUNDLE_NAME "Qv2ray"
|
||||
MACOSX_BUNDLE_BUNDLE_VERSION ${QV2RAY_VERSION_STRING}
|
||||
MACOSX_BUNDLE_COPYRIGHT "Copyright (c) 2019-2020 Qv2ray Development Group"
|
||||
MACOSX_BUNDLE_GUI_IDENTIFIER "com.github.qv2ray"
|
||||
MACOSX_BUNDLE_ICON_FILE "qv2ray.icns"
|
||||
MACOSX_BUNDLE_INFO_STRING "Created by Qv2ray development team"
|
||||
MACOSX_BUNDLE_LONG_VERSION_STRING ${QV2RAY_VERSION_STRING}
|
||||
MACOSX_BUNDLE_SHORT_VERSION_STRING ${QV2RAY_VERSION_STRING}
|
||||
RESOURCE
|
||||
${MACOSX_ICON}
|
||||
)
|
||||
|
||||
# Destination paths below are relative to ${CMAKE_INSTALL_PREFIX}
|
||||
install(TARGETS ${PROJECT_NAME}
|
||||
BUNDLE DESTINATION . COMPONENT Runtime
|
||||
RUNTIME DESTINATION bin COMPONENT Runtime
|
||||
)
|
||||
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
|
||||
)
|
||||
COMMAND macdeployqt ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.app
|
||||
)
|
||||
set(APPS "\${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}.app")
|
||||
include(cmake/deployment.cmake)
|
||||
endif()
|
||||
|
||||
if(UNIX AND NOT APPLE AND NOT WIN32)
|
||||
install(TARGETS ${PROJECT_NAME} 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)
|
||||
if(NOT EMBED_TRANSLATIONS)
|
||||
install(FILES ${QM_FILES} DESTINATION share/qv2ray/lang)
|
||||
endif()
|
||||
install(TARGETS ${PROJECT_NAME} 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)
|
||||
if(NOT EMBED_TRANSLATIONS)
|
||||
install(FILES ${QM_FILES} DESTINATION share/qv2ray/lang)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
target_link_libraries(${PROJECT_NAME} wininet wsock32 ws2_32 user32)
|
||||
install(TARGETS ${PROJECT_NAME} 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()
|
||||
target_link_libraries(${PROJECT_NAME} wininet wsock32 ws2_32 user32)
|
||||
install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION .)
|
||||
if(NOT EMBED_TRANSLATIONS)
|
||||
install(FILES ${QM_FILES} DESTINATION lang)
|
||||
endif()
|
||||
|
||||
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
|
||||
COMMAND windeployqt ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.exe --compiler-runtime --verbose 2 --dir ${CMAKE_BINARY_DIR}/winqt/
|
||||
)
|
||||
install(DIRECTORY ${CMAKE_BINARY_DIR}/winqt/ DESTINATION .)
|
||||
set(APPS "\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}.exe")
|
||||
include(cmake/deployment.cmake)
|
||||
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/
|
||||
)
|
||||
install(DIRECTORY ${CMAKE_BINARY_DIR}/winqt/ DESTINATION .)
|
||||
set(APPS "\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}.exe")
|
||||
include(cmake/deployment.cmake)
|
||||
endif()
|
||||
|
BIN
assets/CMakeDMGBackground.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
assets/CMakeDMGBackground.tif
Normal file
BIN
assets/DS_Store
Normal file
38
assets/MacOSXBundleInfo.plist.in
Normal file
@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleGetInfoString</key>
|
||||
<string>${MACOSX_BUNDLE_INFO_STRING}</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>${MACOSX_BUNDLE_ICON_FILE}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleLongVersionString</key>
|
||||
<string>${MACOSX_BUNDLE_LONG_VERSION_STRING}</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
|
||||
<key>CSResourcesFileMapped</key>
|
||||
<true/>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<string>True</string>
|
||||
</dict>
|
||||
</plist>
|
238
assets/icons/designs/Applogo_Bird.svg
Normal file
@ -0,0 +1,238 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<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"
|
||||
width="48"
|
||||
height="48"
|
||||
viewBox="0 0 12.699999 12.7"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="0.92.4 5da689c313, 2019-01-14"
|
||||
sodipodi:docname="Applogo_Frameless_Plain.svg"
|
||||
inkscape:export-xdpi="512"
|
||||
inkscape:export-ydpi="512">
|
||||
<defs
|
||||
id="defs2">
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient908">
|
||||
<stop
|
||||
style="stop-color:#6e7678;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop904" />
|
||||
<stop
|
||||
style="stop-color:#2e3235;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop906" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient847">
|
||||
<stop
|
||||
style="stop-color:#c1fe6f;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop843" />
|
||||
<stop
|
||||
style="stop-color:#60fe6f;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop845" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient5057">
|
||||
<stop
|
||||
style="stop-color:#3c3c3c;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop5053" />
|
||||
<stop
|
||||
style="stop-color:#282728;stop-opacity:0.6574803"
|
||||
offset="1"
|
||||
id="stop5055" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient4759">
|
||||
<stop
|
||||
style="stop-color:#2eec71;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop4755" />
|
||||
<stop
|
||||
style="stop-color:#1cdc9a;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop4757" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient4713"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop4709"
|
||||
offset="0"
|
||||
style="stop-color:#3de256;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop4711"
|
||||
offset="1"
|
||||
style="stop-color:#2fbeba;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4713"
|
||||
id="linearGradient4707"
|
||||
x1="89.965836"
|
||||
y1="19.458199"
|
||||
x2="108.00179"
|
||||
y2="39.934193"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(38.574145,1.3181723)" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5057"
|
||||
id="linearGradient5059"
|
||||
x1="92.227142"
|
||||
y1="48.940449"
|
||||
x2="140.26065"
|
||||
y2="94.022179"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4759"
|
||||
id="linearGradient953"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.34597512,0.06144722,-0.06655638,0.31941658,-25.1488,274.41205)"
|
||||
x1="85.229973"
|
||||
y1="28.293249"
|
||||
x2="113.08728"
|
||||
y2="35.515835" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient847"
|
||||
id="linearGradient955"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.15666471,0,0,0.15666471,-5.1793298,256.3406)"
|
||||
x1="39.630962"
|
||||
y1="196.65211"
|
||||
x2="56.496174"
|
||||
y2="208.57474" />
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath922">
|
||||
<rect
|
||||
style="fill:none;fill-opacity:1;stroke:#23d829;stroke-width:0.044;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.72265625"
|
||||
id="rect924"
|
||||
width="12.7"
|
||||
height="12.170834"
|
||||
x="9.5367426e-08"
|
||||
y="284.56458"
|
||||
ry="1.0638391" />
|
||||
</clipPath>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient908"
|
||||
id="linearGradient910"
|
||||
x1="12.7"
|
||||
y1="284.29999"
|
||||
x2="12.7"
|
||||
y2="297"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.87500008,0,0,0.87505491,0.81442048,36.315602)" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="7.9900606"
|
||||
inkscape:cx="26.213373"
|
||||
inkscape:cy="25.087073"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="g889"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1028"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="26"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:snap-object-midpoints="false"
|
||||
inkscape:snap-smooth-nodes="false"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:bbox-paths="true"
|
||||
inkscape:bbox-nodes="true"
|
||||
inkscape:snap-bbox-edge-midpoints="true"
|
||||
inkscape:snap-bbox-midpoints="true"
|
||||
inkscape:snap-midpoints="false"
|
||||
units="px"
|
||||
inkscape:snap-nodes="false"
|
||||
inkscape:snap-others="false"
|
||||
inkscape:object-paths="false"
|
||||
inkscape:snap-intersection-paths="false"
|
||||
inkscape:object-nodes="false"
|
||||
inkscape:snap-center="false"
|
||||
inkscape:snap-text-baseline="false"
|
||||
inkscape:snap-global="false"
|
||||
inkscape:snap-grids="false"
|
||||
inkscape:snap-to-guides="false">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid885" />
|
||||
</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 />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-284.29999)">
|
||||
<g
|
||||
id="g889"
|
||||
transform="matrix(0.77674917,0,0,0.77993751,1.4662318,64.012038)">
|
||||
<g
|
||||
id="g44"
|
||||
transform="matrix(1.1963196,0,0,1.1963196,-1.3111729,-57.928707)">
|
||||
<path
|
||||
sodipodi:nodetypes="cscccc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path879-7"
|
||||
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"
|
||||
style="fill:#27b181;fill-opacity:1;stroke:none;stroke-width:0.02480469px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<path
|
||||
style="fill:#1cd28e;fill-opacity:1;stroke:none;stroke-width:0.08956835px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
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"
|
||||
id="path4715-0"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cscc" />
|
||||
<path
|
||||
style="fill:url(#linearGradient953);fill-opacity:1;stroke:none;stroke-width:0.08956835px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
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"
|
||||
id="path4715-6-93"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:url(#linearGradient955);fill-opacity:1;stroke:none;stroke-width:0.08956835px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
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"
|
||||
id="path4715-6-9-6"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cscc" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 8.0 KiB |
310
assets/icons/ui_dark/design/tray-connected.svg
Normal file
@ -0,0 +1,310 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
|
||||
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"
|
||||
width="22"
|
||||
height="22"
|
||||
viewBox="0 0 5.8208333 5.8208333"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="0.92.4 5da689c313, 2019-01-14"
|
||||
sodipodi:docname="tray-connected.svg"
|
||||
inkscape:export-xdpi="558.54999"
|
||||
inkscape:export-ydpi="558.54999"
|
||||
inkscape:export-filename="/media/Storage/Projects/Qv2ray/assets/icons/ui_dark/design/tray-connected.svg.png">
|
||||
<defs
|
||||
id="defs2">
|
||||
<linearGradient
|
||||
id="linearGradient841"
|
||||
osb:paint="solid">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop839" />
|
||||
</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
|
||||
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" />
|
||||
<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>
|
||||
<clipPath
|
||||
id="clipPath922-6"
|
||||
clipPathUnits="userSpaceOnUse">
|
||||
<rect
|
||||
ry="1.0638391"
|
||||
y="284.56458"
|
||||
x="9.5367426e-08"
|
||||
height="12.170834"
|
||||
width="12.7"
|
||||
id="rect924-2"
|
||||
style="fill:none;fill-opacity:1;stroke:#23d829;stroke-width:0.044;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.72265625" />
|
||||
</clipPath>
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
inkscape:label="Drop Shadow"
|
||||
id="filter1510">
|
||||
<feFlood
|
||||
flood-opacity="0.976471"
|
||||
flood-color="rgb(0,0,0)"
|
||||
result="flood"
|
||||
id="feFlood1500" />
|
||||
<feComposite
|
||||
in="flood"
|
||||
in2="SourceGraphic"
|
||||
operator="in"
|
||||
result="composite1"
|
||||
id="feComposite1502" />
|
||||
<feGaussianBlur
|
||||
in="composite1"
|
||||
stdDeviation="0.1"
|
||||
result="blur"
|
||||
id="feGaussianBlur1504" />
|
||||
<feOffset
|
||||
dx="0"
|
||||
dy="0"
|
||||
result="offset"
|
||||
id="feOffset1506" />
|
||||
<feComposite
|
||||
in="offset"
|
||||
in2="SourceGraphic"
|
||||
operator="out"
|
||||
result="composite2"
|
||||
id="feComposite1508" />
|
||||
</filter>
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
inkscape:label="Drop Shadow"
|
||||
id="filter1859">
|
||||
<feFlood
|
||||
flood-opacity="0.266667"
|
||||
flood-color="rgb(0,0,0)"
|
||||
result="flood"
|
||||
id="feFlood1849" />
|
||||
<feComposite
|
||||
in="flood"
|
||||
in2="SourceGraphic"
|
||||
operator="in"
|
||||
result="composite1"
|
||||
id="feComposite1851" />
|
||||
<feGaussianBlur
|
||||
in="composite1"
|
||||
stdDeviation="0.2"
|
||||
result="blur"
|
||||
id="feGaussianBlur1853" />
|
||||
<feOffset
|
||||
dx="0"
|
||||
dy="0"
|
||||
result="offset"
|
||||
id="feOffset1855" />
|
||||
<feComposite
|
||||
in="offset"
|
||||
in2="SourceGraphic"
|
||||
operator="out"
|
||||
result="composite2"
|
||||
id="feComposite1857" />
|
||||
</filter>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="22.627417"
|
||||
inkscape:cx="-5.2209634"
|
||||
inkscape:cy="5.0686426"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
units="px"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1028"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="26"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:bbox-paths="true"
|
||||
inkscape:bbox-nodes="true"
|
||||
inkscape:snap-bbox-edge-midpoints="true"
|
||||
inkscape:snap-bbox-midpoints="true"
|
||||
fit-margin-top="5"
|
||||
fit-margin-left="5"
|
||||
fit-margin-bottom="5"
|
||||
fit-margin-right="5"
|
||||
inkscape:snap-global="true"
|
||||
inkscape:object-paths="false"
|
||||
inkscape:snap-intersection-paths="false"
|
||||
inkscape:snap-smooth-nodes="true"
|
||||
inkscape:snap-midpoints="true"
|
||||
inkscape:snap-grids="false"
|
||||
inkscape:snap-object-midpoints="true"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true"
|
||||
scale-x="1"
|
||||
guidecolor="#00ffff"
|
||||
guideopacity="0.49803922"
|
||||
guidehicolor="#ff5184"
|
||||
guidehiopacity="0.62745098"
|
||||
inkscape:snap-nodes="true"
|
||||
inkscape:snap-others="false">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid815"
|
||||
originx="0"
|
||||
originy="0"
|
||||
color="#3f513e"
|
||||
opacity="0.04313725"
|
||||
empcolor="#3f3742"
|
||||
empopacity="0.16470588"
|
||||
empspacing="5"
|
||||
dotted="false" />
|
||||
<sodipodi:guide
|
||||
position="28.284675,2.8909227"
|
||||
orientation="1,0"
|
||||
id="guide924"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
position="26.141,6.3542733"
|
||||
orientation="1,0"
|
||||
id="guide932"
|
||||
inkscape:locked="false"
|
||||
inkscape:label=""
|
||||
inkscape:color="rgb(0,255,236)" />
|
||||
<sodipodi:guide
|
||||
position="0.79375,4.4979166"
|
||||
orientation="1,0"
|
||||
id="guide1884"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
position="3.96875,5.0270833"
|
||||
orientation="0,1"
|
||||
id="guide1886"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
position="5.0270833,0.26458333"
|
||||
orientation="1,0"
|
||||
id="guide1888"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
position="4.4979166,0.79375"
|
||||
orientation="0,1"
|
||||
id="guide1890"
|
||||
inkscape:locked="false" />
|
||||
</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 />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-1.9173392,-290.74464)">
|
||||
<path
|
||||
id="path1962"
|
||||
transform="matrix(0.26458333,0,0,0.26458333,1.9173392,290.74464)"
|
||||
style="fill:#e6e6e6;fill-opacity:1;stroke:none;stroke-width:0.99999994px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="M 7,11 6.2929688,11.707031 8.5859375,14 H 18 V 13 H 9 Z M 6.2929688,11.707031 5,13 H 4 v 1 H 5.4140625 L 7,12.414062 Z M 3,3 V 18.999988 H 19 V 3 Z M 3.9999999,3.9999875 H 18 V 18 H 3.9999999 Z" />
|
||||
<rect
|
||||
style="opacity:0.5;fill:none;fill-opacity:1;stroke:none;stroke-width:0.04583338;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect844"
|
||||
width="5.8208332"
|
||||
height="5.8208447"
|
||||
x="1.9173392"
|
||||
y="290.74463"
|
||||
inkscape:export-xdpi="64"
|
||||
inkscape:export-ydpi="64" />
|
||||
<path
|
||||
style="fill:#ececec;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 4.5631726,292.06756 -0.2645834,0.26459 v 0.52916 l 0.2645834,-0.26458 v 0.26458 0.52917 l 0.5291666,0.79375 0.5291667,1.32291 v -0.79374 l 0.2645833,-0.26459 -0.2645833,-0.52916 v -0.79375 l -0.2645833,-0.52917 z"
|
||||
id="path1934"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccccccccccccc" />
|
||||
<g
|
||||
id="g868" />
|
||||
<g
|
||||
id="g873"
|
||||
transform="translate(0.24804686,0.57052501)" />
|
||||
<circle
|
||||
style="fill:#999999;stroke-width:0.18761367;fill-opacity:1"
|
||||
id="path43"
|
||||
cx="6.2250867"
|
||||
cy="295.03583"
|
||||
r="0.96738303" />
|
||||
<ellipse
|
||||
cy="295.03583"
|
||||
cx="6.2250867"
|
||||
id="path43-3"
|
||||
style="fill:#2edf46;fill-opacity:1;stroke-width:0.15462632"
|
||||
rx="0.79291928"
|
||||
ry="0.80168903" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 9.3 KiB |
309
assets/icons/ui_dark/design/tray-systemproxy.svg
Normal file
@ -0,0 +1,309 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
|
||||
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"
|
||||
width="22"
|
||||
height="22"
|
||||
viewBox="0 0 5.8208333 5.8208333"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="0.92.4 5da689c313, 2019-01-14"
|
||||
sodipodi:docname="tray-systemproxy.svg"
|
||||
inkscape:export-xdpi="558.54999"
|
||||
inkscape:export-ydpi="558.54999">
|
||||
<defs
|
||||
id="defs2">
|
||||
<linearGradient
|
||||
id="linearGradient841"
|
||||
osb:paint="solid">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop839" />
|
||||
</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
|
||||
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" />
|
||||
<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>
|
||||
<clipPath
|
||||
id="clipPath922-6"
|
||||
clipPathUnits="userSpaceOnUse">
|
||||
<rect
|
||||
ry="1.0638391"
|
||||
y="284.56458"
|
||||
x="9.5367426e-08"
|
||||
height="12.170834"
|
||||
width="12.7"
|
||||
id="rect924-2"
|
||||
style="fill:none;fill-opacity:1;stroke:#23d829;stroke-width:0.044;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.72265625" />
|
||||
</clipPath>
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
inkscape:label="Drop Shadow"
|
||||
id="filter1510">
|
||||
<feFlood
|
||||
flood-opacity="0.976471"
|
||||
flood-color="rgb(0,0,0)"
|
||||
result="flood"
|
||||
id="feFlood1500" />
|
||||
<feComposite
|
||||
in="flood"
|
||||
in2="SourceGraphic"
|
||||
operator="in"
|
||||
result="composite1"
|
||||
id="feComposite1502" />
|
||||
<feGaussianBlur
|
||||
in="composite1"
|
||||
stdDeviation="0.1"
|
||||
result="blur"
|
||||
id="feGaussianBlur1504" />
|
||||
<feOffset
|
||||
dx="0"
|
||||
dy="0"
|
||||
result="offset"
|
||||
id="feOffset1506" />
|
||||
<feComposite
|
||||
in="offset"
|
||||
in2="SourceGraphic"
|
||||
operator="out"
|
||||
result="composite2"
|
||||
id="feComposite1508" />
|
||||
</filter>
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
inkscape:label="Drop Shadow"
|
||||
id="filter1859">
|
||||
<feFlood
|
||||
flood-opacity="0.266667"
|
||||
flood-color="rgb(0,0,0)"
|
||||
result="flood"
|
||||
id="feFlood1849" />
|
||||
<feComposite
|
||||
in="flood"
|
||||
in2="SourceGraphic"
|
||||
operator="in"
|
||||
result="composite1"
|
||||
id="feComposite1851" />
|
||||
<feGaussianBlur
|
||||
in="composite1"
|
||||
stdDeviation="0.2"
|
||||
result="blur"
|
||||
id="feGaussianBlur1853" />
|
||||
<feOffset
|
||||
dx="0"
|
||||
dy="0"
|
||||
result="offset"
|
||||
id="feOffset1855" />
|
||||
<feComposite
|
||||
in="offset"
|
||||
in2="SourceGraphic"
|
||||
operator="out"
|
||||
result="composite2"
|
||||
id="feComposite1857" />
|
||||
</filter>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="32"
|
||||
inkscape:cx="5.778172"
|
||||
inkscape:cy="7.0536028"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
units="px"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1028"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="26"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:bbox-paths="true"
|
||||
inkscape:bbox-nodes="true"
|
||||
inkscape:snap-bbox-edge-midpoints="true"
|
||||
inkscape:snap-bbox-midpoints="true"
|
||||
fit-margin-top="5"
|
||||
fit-margin-left="5"
|
||||
fit-margin-bottom="5"
|
||||
fit-margin-right="5"
|
||||
inkscape:snap-global="true"
|
||||
inkscape:object-paths="false"
|
||||
inkscape:snap-intersection-paths="false"
|
||||
inkscape:snap-smooth-nodes="true"
|
||||
inkscape:snap-midpoints="true"
|
||||
inkscape:snap-grids="false"
|
||||
inkscape:snap-object-midpoints="true"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true"
|
||||
scale-x="1"
|
||||
guidecolor="#00ffff"
|
||||
guideopacity="0.49803922"
|
||||
guidehicolor="#ff5184"
|
||||
guidehiopacity="0.62745098"
|
||||
inkscape:snap-nodes="true"
|
||||
inkscape:snap-others="false">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid815"
|
||||
originx="0"
|
||||
originy="0"
|
||||
color="#3f513e"
|
||||
opacity="0.04313725"
|
||||
empcolor="#3f3742"
|
||||
empopacity="0.16470588"
|
||||
empspacing="5"
|
||||
dotted="false" />
|
||||
<sodipodi:guide
|
||||
position="28.284675,2.8909227"
|
||||
orientation="1,0"
|
||||
id="guide924"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
position="26.141,6.3542733"
|
||||
orientation="1,0"
|
||||
id="guide932"
|
||||
inkscape:locked="false"
|
||||
inkscape:label=""
|
||||
inkscape:color="rgb(0,255,236)" />
|
||||
<sodipodi:guide
|
||||
position="0.79375,4.4979166"
|
||||
orientation="1,0"
|
||||
id="guide1884"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
position="3.96875,5.0270833"
|
||||
orientation="0,1"
|
||||
id="guide1886"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
position="5.0270833,0.26458333"
|
||||
orientation="1,0"
|
||||
id="guide1888"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
position="4.4979166,0.79375"
|
||||
orientation="0,1"
|
||||
id="guide1890"
|
||||
inkscape:locked="false" />
|
||||
</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 />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-1.9173392,-290.74464)">
|
||||
<path
|
||||
id="path1962"
|
||||
transform="matrix(0.26458333,0,0,0.26458333,1.9173392,290.74464)"
|
||||
style="fill:#e6e6e6;fill-opacity:1;stroke:none;stroke-width:0.99999994px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="M 7,11 6.2929688,11.707031 8.5859375,14 H 18 V 13 H 9 Z M 6.2929688,11.707031 5,13 H 4 v 1 H 5.4140625 L 7,12.414062 Z M 3,3 V 18.999988 H 19 V 3 Z M 3.9999999,3.9999875 H 18 V 18 H 3.9999999 Z" />
|
||||
<rect
|
||||
style="opacity:0.5;fill:none;fill-opacity:1;stroke:none;stroke-width:0.04583338;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect844"
|
||||
width="5.8208332"
|
||||
height="5.8208447"
|
||||
x="1.9173392"
|
||||
y="290.74463"
|
||||
inkscape:export-xdpi="64"
|
||||
inkscape:export-ydpi="64" />
|
||||
<path
|
||||
style="fill:#ececec;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 4.5631726,292.06756 -0.2645834,0.26459 v 0.52916 l 0.2645834,-0.26458 v 0.26458 0.52917 l 0.5291666,0.79375 0.5291667,1.32291 v -0.79374 l 0.2645833,-0.26459 -0.2645833,-0.52916 v -0.79375 l -0.2645833,-0.52917 z"
|
||||
id="path1934"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccccccccccccc" />
|
||||
<g
|
||||
id="g868" />
|
||||
<g
|
||||
id="g873"
|
||||
transform="translate(0.24804686,0.57052501)" />
|
||||
<circle
|
||||
style="fill:#dfdfdf;stroke-width:0.18761367;fill-opacity:1"
|
||||
id="path43"
|
||||
cx="6.2250867"
|
||||
cy="295.03583"
|
||||
r="0.96738303" />
|
||||
<ellipse
|
||||
cy="295.03583"
|
||||
cx="6.2250867"
|
||||
id="path43-3"
|
||||
style="fill:#4ddbf3;fill-opacity:1;stroke-width:0.15462632"
|
||||
rx="0.79291928"
|
||||
ry="0.80168903" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 9.2 KiB |
BIN
assets/icons/ui_dark/tray-connected.png
Normal file
After Width: | Height: | Size: 3.0 KiB |
BIN
assets/icons/ui_dark/tray-systemproxy.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
356
assets/icons/ui_light/design/tray-connected.svg
Normal file
@ -0,0 +1,356 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
|
||||
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"
|
||||
width="22"
|
||||
height="22"
|
||||
viewBox="0 0 5.8208333 5.8208333"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="0.92.4 5da689c313, 2019-01-14"
|
||||
sodipodi:docname="tray-connected.svg"
|
||||
inkscape:export-xdpi="558.54999"
|
||||
inkscape:export-ydpi="558.54999">
|
||||
<defs
|
||||
id="defs2">
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient876">
|
||||
<stop
|
||||
style="stop-color:#31363b;stop-opacity:0.58823532"
|
||||
offset="0"
|
||||
id="stop872" />
|
||||
<stop
|
||||
style="stop-color:#31363b;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop874" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient866">
|
||||
<stop
|
||||
style="stop-color:#31363b;stop-opacity:0.58823532"
|
||||
offset="0"
|
||||
id="stop862" />
|
||||
<stop
|
||||
style="stop-color:#31363b;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop864" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient841"
|
||||
osb:paint="solid">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop839" />
|
||||
</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
|
||||
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" />
|
||||
<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>
|
||||
<clipPath
|
||||
id="clipPath922-6"
|
||||
clipPathUnits="userSpaceOnUse">
|
||||
<rect
|
||||
ry="1.0638391"
|
||||
y="284.56458"
|
||||
x="9.5367426e-08"
|
||||
height="12.170834"
|
||||
width="12.7"
|
||||
id="rect924-2"
|
||||
style="fill:none;fill-opacity:1;stroke:#23d829;stroke-width:0.044;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.72265625" />
|
||||
</clipPath>
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
inkscape:label="Drop Shadow"
|
||||
id="filter1510">
|
||||
<feFlood
|
||||
flood-opacity="0.976471"
|
||||
flood-color="rgb(0,0,0)"
|
||||
result="flood"
|
||||
id="feFlood1500" />
|
||||
<feComposite
|
||||
in="flood"
|
||||
in2="SourceGraphic"
|
||||
operator="in"
|
||||
result="composite1"
|
||||
id="feComposite1502" />
|
||||
<feGaussianBlur
|
||||
in="composite1"
|
||||
stdDeviation="0.1"
|
||||
result="blur"
|
||||
id="feGaussianBlur1504" />
|
||||
<feOffset
|
||||
dx="0"
|
||||
dy="0"
|
||||
result="offset"
|
||||
id="feOffset1506" />
|
||||
<feComposite
|
||||
in="offset"
|
||||
in2="SourceGraphic"
|
||||
operator="out"
|
||||
result="composite2"
|
||||
id="feComposite1508" />
|
||||
</filter>
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
inkscape:label="Drop Shadow"
|
||||
id="filter1859">
|
||||
<feFlood
|
||||
flood-opacity="0.266667"
|
||||
flood-color="rgb(0,0,0)"
|
||||
result="flood"
|
||||
id="feFlood1849" />
|
||||
<feComposite
|
||||
in="flood"
|
||||
in2="SourceGraphic"
|
||||
operator="in"
|
||||
result="composite1"
|
||||
id="feComposite1851" />
|
||||
<feGaussianBlur
|
||||
in="composite1"
|
||||
stdDeviation="0.2"
|
||||
result="blur"
|
||||
id="feGaussianBlur1853" />
|
||||
<feOffset
|
||||
dx="0"
|
||||
dy="0"
|
||||
result="offset"
|
||||
id="feOffset1855" />
|
||||
<feComposite
|
||||
in="offset"
|
||||
in2="SourceGraphic"
|
||||
operator="out"
|
||||
result="composite2"
|
||||
id="feComposite1857" />
|
||||
</filter>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient866"
|
||||
id="linearGradient868"
|
||||
x1="4.2985892"
|
||||
y1="292.06757"
|
||||
x2="7.7381635"
|
||||
y2="295.50714"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient876"
|
||||
id="linearGradient878"
|
||||
x1="5.6215057"
|
||||
y1="294.44879"
|
||||
x2="6.6798391"
|
||||
y2="295.50714"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="32"
|
||||
inkscape:cx="4.561514"
|
||||
inkscape:cy="3.2123107"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
units="px"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1028"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="26"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:bbox-paths="true"
|
||||
inkscape:bbox-nodes="true"
|
||||
inkscape:snap-bbox-edge-midpoints="true"
|
||||
inkscape:snap-bbox-midpoints="true"
|
||||
fit-margin-top="5"
|
||||
fit-margin-left="5"
|
||||
fit-margin-bottom="5"
|
||||
fit-margin-right="5"
|
||||
inkscape:snap-global="true"
|
||||
inkscape:object-paths="false"
|
||||
inkscape:snap-intersection-paths="false"
|
||||
inkscape:snap-smooth-nodes="true"
|
||||
inkscape:snap-midpoints="true"
|
||||
inkscape:snap-grids="false"
|
||||
inkscape:snap-object-midpoints="true"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true"
|
||||
scale-x="1"
|
||||
guidecolor="#00ffff"
|
||||
guideopacity="0.49803922"
|
||||
guidehicolor="#ff5184"
|
||||
guidehiopacity="0.62745098"
|
||||
inkscape:snap-nodes="true"
|
||||
inkscape:snap-others="false">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid815"
|
||||
originx="0"
|
||||
originy="0"
|
||||
color="#3f513e"
|
||||
opacity="0.04313725"
|
||||
empcolor="#3f3742"
|
||||
empopacity="0.16470588"
|
||||
empspacing="5"
|
||||
dotted="false" />
|
||||
<sodipodi:guide
|
||||
position="11.244792,0.79375"
|
||||
orientation="1,0"
|
||||
id="guide924"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
position="-1.6867187,5.0270833"
|
||||
orientation="1,0"
|
||||
id="guide932"
|
||||
inkscape:locked="false"
|
||||
inkscape:label=""
|
||||
inkscape:color="rgb(0,255,236)" />
|
||||
<sodipodi:guide
|
||||
position="0.79375,4.4979166"
|
||||
orientation="1,0"
|
||||
id="guide1884"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
position="3.96875,5.0270833"
|
||||
orientation="0,1"
|
||||
id="guide1886"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
position="5.0270833,0.26458333"
|
||||
orientation="1,0"
|
||||
id="guide1888"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
position="4.4979166,0.79375"
|
||||
orientation="0,1"
|
||||
id="guide1890"
|
||||
inkscape:locked="false" />
|
||||
</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 />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-1.9173392,-290.74464)">
|
||||
<path
|
||||
style="fill:url(#linearGradient878);stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1;opacity:0.65"
|
||||
d="m 5.6215058,295.50714 v -0.79375 l 0.2645833,-0.26458 0.79375,0.79375 v 0.26458 z"
|
||||
id="path870"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:url(#linearGradient868);stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1;opacity:0.65"
|
||||
d="m 4.2985892,292.86131 2.3812499,2.38125 v -1.32292 l -1.3229166,-1.32292 -0.79375,-0.52916 -0.2645833,0.26458 z"
|
||||
id="path860"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path1962"
|
||||
transform="matrix(0.26458333,0,0,0.26458333,1.9173392,290.74464)"
|
||||
style="fill:#31363b;fill-opacity:1;stroke:none;stroke-width:0.99999994px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="M 7,11 6.2929688,11.707031 8.5859375,14 H 18 V 13 H 9 Z M 6.2929688,11.707031 5,13 H 4 v 1 H 5.4140625 L 7,12.414062 Z M 3,3 V 18.999988 H 19 V 3 Z M 3.9999999,3.9999875 H 18 V 18 H 3.9999999 Z" />
|
||||
<rect
|
||||
style="opacity:0.5;fill:none;fill-opacity:1;stroke:none;stroke-width:0.04583338;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect844"
|
||||
width="5.8208332"
|
||||
height="5.8208447"
|
||||
x="1.9173392"
|
||||
y="290.74463"
|
||||
inkscape:export-xdpi="64"
|
||||
inkscape:export-ydpi="64" />
|
||||
<path
|
||||
style="fill:#31363b;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 4.5631726,292.06756 -0.2645834,0.26459 v 0.52916 l 0.2645834,-0.26458 v 0.26458 0.52917 l 0.5291666,0.79375 0.5291667,1.32291 v -0.79374 l 0.2645833,-0.26459 -0.2645833,-0.52916 v -0.79375 l -0.2645833,-0.52917 z"
|
||||
id="path1934"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccccccccccccc" />
|
||||
<circle
|
||||
style="fill:#595959;fill-opacity:1;stroke-width:0.18761367"
|
||||
id="path43"
|
||||
cx="6.2266774"
|
||||
cy="295.0452"
|
||||
r="0.96738309" />
|
||||
<ellipse
|
||||
cy="295.0452"
|
||||
cx="6.2266774"
|
||||
id="path43-3"
|
||||
style="fill:#2cee14;fill-opacity:1;stroke-width:0.15462632"
|
||||
rx="0.79291928"
|
||||
ry="0.80168903" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 11 KiB |
359
assets/icons/ui_light/design/tray-systemproxy.svg
Normal file
@ -0,0 +1,359 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
|
||||
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"
|
||||
width="22"
|
||||
height="22"
|
||||
viewBox="0 0 5.8208333 5.8208333"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="0.92.4 5da689c313, 2019-01-14"
|
||||
sodipodi:docname="tray-systemproxy.svg"
|
||||
inkscape:export-xdpi="558.54999"
|
||||
inkscape:export-ydpi="558.54999">
|
||||
<defs
|
||||
id="defs2">
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient876">
|
||||
<stop
|
||||
style="stop-color:#31363b;stop-opacity:0.58823532"
|
||||
offset="0"
|
||||
id="stop872" />
|
||||
<stop
|
||||
style="stop-color:#31363b;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop874" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient866">
|
||||
<stop
|
||||
style="stop-color:#31363b;stop-opacity:0.58823532"
|
||||
offset="0"
|
||||
id="stop862" />
|
||||
<stop
|
||||
style="stop-color:#31363b;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop864" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient841"
|
||||
osb:paint="solid">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop839" />
|
||||
</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
|
||||
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" />
|
||||
<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>
|
||||
<clipPath
|
||||
id="clipPath922-6"
|
||||
clipPathUnits="userSpaceOnUse">
|
||||
<rect
|
||||
ry="1.0638391"
|
||||
y="284.56458"
|
||||
x="9.5367426e-08"
|
||||
height="12.170834"
|
||||
width="12.7"
|
||||
id="rect924-2"
|
||||
style="fill:none;fill-opacity:1;stroke:#23d829;stroke-width:0.044;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.72265625" />
|
||||
</clipPath>
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
inkscape:label="Drop Shadow"
|
||||
id="filter1510">
|
||||
<feFlood
|
||||
flood-opacity="0.976471"
|
||||
flood-color="rgb(0,0,0)"
|
||||
result="flood"
|
||||
id="feFlood1500" />
|
||||
<feComposite
|
||||
in="flood"
|
||||
in2="SourceGraphic"
|
||||
operator="in"
|
||||
result="composite1"
|
||||
id="feComposite1502" />
|
||||
<feGaussianBlur
|
||||
in="composite1"
|
||||
stdDeviation="0.1"
|
||||
result="blur"
|
||||
id="feGaussianBlur1504" />
|
||||
<feOffset
|
||||
dx="0"
|
||||
dy="0"
|
||||
result="offset"
|
||||
id="feOffset1506" />
|
||||
<feComposite
|
||||
in="offset"
|
||||
in2="SourceGraphic"
|
||||
operator="out"
|
||||
result="composite2"
|
||||
id="feComposite1508" />
|
||||
</filter>
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
inkscape:label="Drop Shadow"
|
||||
id="filter1859">
|
||||
<feFlood
|
||||
flood-opacity="0.266667"
|
||||
flood-color="rgb(0,0,0)"
|
||||
result="flood"
|
||||
id="feFlood1849" />
|
||||
<feComposite
|
||||
in="flood"
|
||||
in2="SourceGraphic"
|
||||
operator="in"
|
||||
result="composite1"
|
||||
id="feComposite1851" />
|
||||
<feGaussianBlur
|
||||
in="composite1"
|
||||
stdDeviation="0.2"
|
||||
result="blur"
|
||||
id="feGaussianBlur1853" />
|
||||
<feOffset
|
||||
dx="0"
|
||||
dy="0"
|
||||
result="offset"
|
||||
id="feOffset1855" />
|
||||
<feComposite
|
||||
in="offset"
|
||||
in2="SourceGraphic"
|
||||
operator="out"
|
||||
result="composite2"
|
||||
id="feComposite1857" />
|
||||
</filter>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient866"
|
||||
id="linearGradient868"
|
||||
x1="4.2985892"
|
||||
y1="292.06757"
|
||||
x2="7.7381635"
|
||||
y2="295.50714"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient876"
|
||||
id="linearGradient878"
|
||||
x1="5.6215057"
|
||||
y1="294.44879"
|
||||
x2="6.6798391"
|
||||
y2="295.50714"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="32"
|
||||
inkscape:cx="9.920889"
|
||||
inkscape:cy="8.4623107"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
units="px"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1028"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="26"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:bbox-paths="true"
|
||||
inkscape:bbox-nodes="true"
|
||||
inkscape:snap-bbox-edge-midpoints="true"
|
||||
inkscape:snap-bbox-midpoints="true"
|
||||
fit-margin-top="5"
|
||||
fit-margin-left="5"
|
||||
fit-margin-bottom="5"
|
||||
fit-margin-right="5"
|
||||
inkscape:snap-global="true"
|
||||
inkscape:object-paths="false"
|
||||
inkscape:snap-intersection-paths="false"
|
||||
inkscape:snap-smooth-nodes="true"
|
||||
inkscape:snap-midpoints="true"
|
||||
inkscape:snap-grids="false"
|
||||
inkscape:snap-object-midpoints="true"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true"
|
||||
scale-x="1"
|
||||
guidecolor="#00ffff"
|
||||
guideopacity="0.49803922"
|
||||
guidehicolor="#ff5184"
|
||||
guidehiopacity="0.62745098"
|
||||
inkscape:snap-nodes="true"
|
||||
inkscape:snap-others="false">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid815"
|
||||
originx="0"
|
||||
originy="0"
|
||||
color="#3f513e"
|
||||
opacity="0.04313725"
|
||||
empcolor="#3f3742"
|
||||
empopacity="0.16470588"
|
||||
empspacing="5"
|
||||
dotted="false" />
|
||||
<sodipodi:guide
|
||||
position="11.244792,0.79375"
|
||||
orientation="1,0"
|
||||
id="guide924"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
position="-1.6867187,5.0270833"
|
||||
orientation="1,0"
|
||||
id="guide932"
|
||||
inkscape:locked="false"
|
||||
inkscape:label=""
|
||||
inkscape:color="rgb(0,255,236)" />
|
||||
<sodipodi:guide
|
||||
position="0.79375,4.4979166"
|
||||
orientation="1,0"
|
||||
id="guide1884"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
position="3.96875,5.0270833"
|
||||
orientation="0,1"
|
||||
id="guide1886"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
position="5.0270833,0.26458333"
|
||||
orientation="1,0"
|
||||
id="guide1888"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
position="4.4979166,0.79375"
|
||||
orientation="0,1"
|
||||
id="guide1890"
|
||||
inkscape:locked="false" />
|
||||
</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 />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-1.9173392,-290.74464)">
|
||||
<path
|
||||
style="fill:url(#linearGradient878);stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1;opacity:0.65"
|
||||
d="m 5.6215058,295.50714 v -0.79375 l 0.2645833,-0.26458 0.79375,0.79375 v 0.26458 z"
|
||||
id="path870"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:url(#linearGradient868);stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1;opacity:0.65"
|
||||
d="m 4.2985892,292.86131 2.3812499,2.38125 v -1.32292 l -1.3229166,-1.32292 -0.79375,-0.52916 -0.2645833,0.26458 z"
|
||||
id="path860"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path1962"
|
||||
transform="matrix(0.26458333,0,0,0.26458333,1.9173392,290.74464)"
|
||||
style="fill:#31363b;fill-opacity:1;stroke:none;stroke-width:0.99999994px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="M 7,11 6.2929688,11.707031 8.5859375,14 H 18 V 13 H 9 Z M 6.2929688,11.707031 5,13 H 4 v 1 H 5.4140625 L 7,12.414062 Z M 3,3 V 18.999988 H 19 V 3 Z M 3.9999999,3.9999875 H 18 V 18 H 3.9999999 Z" />
|
||||
<rect
|
||||
style="opacity:0.5;fill:none;fill-opacity:1;stroke:none;stroke-width:0.04583338;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect844"
|
||||
width="5.8208332"
|
||||
height="5.8208447"
|
||||
x="1.9173392"
|
||||
y="290.74463"
|
||||
inkscape:export-xdpi="64"
|
||||
inkscape:export-ydpi="64" />
|
||||
<path
|
||||
style="fill:#31363b;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 4.5631726,292.06756 -0.2645834,0.26459 v 0.52916 l 0.2645834,-0.26458 v 0.26458 0.52917 l 0.5291666,0.79375 0.5291667,1.32291 v -0.79374 l 0.2645833,-0.26459 -0.2645833,-0.52916 v -0.79375 l -0.2645833,-0.52917 z"
|
||||
id="path1934"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccccccccccccc" />
|
||||
<circle
|
||||
style="fill:#595959;fill-opacity:1;stroke-width:0.18761367"
|
||||
id="path43"
|
||||
cx="6.2266774"
|
||||
cy="295.0452"
|
||||
r="0.96738309" />
|
||||
<ellipse
|
||||
cy="295.0452"
|
||||
cx="6.2266774"
|
||||
id="path43-3"
|
||||
style="fill:#00c3ff;fill-opacity:1;stroke-width:0.15462632"
|
||||
rx="0.79291928"
|
||||
ry="0.80168903"
|
||||
inkscape:export-filename="/media/Storage/Projects/Qv2ray/assets/icons/ui_light/tray-systemproxy.png"
|
||||
inkscape:export-xdpi="2050.1499"
|
||||
inkscape:export-ydpi="2050.1499" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 11 KiB |
BIN
assets/icons/ui_light/tray-connected.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
assets/icons/ui_light/tray-systemproxy.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
17
assets/package_dmg.json.in
Normal file
@ -0,0 +1,17 @@
|
||||
|
||||
{
|
||||
"title": "Qv2ray @QV2RAY_VERSION_STRING@",
|
||||
"icon": "@CMAKE_SOURCE_DIR@/assets/icons/qv2ray.icns",
|
||||
"background": "@CMAKE_SOURCE_DIR@/assets/CMakeDMGBackground.png",
|
||||
"icon-size": 128,
|
||||
"window": {
|
||||
"size": {
|
||||
"width": 500,
|
||||
"height": 365
|
||||
}
|
||||
},
|
||||
"contents": [
|
||||
{ "x": 130, "y": 205, "type": "file", "path": "@CMAKE_BINARY_DIR@/qv2ray.app" },
|
||||
{ "x": 375, "y": 205, "type": "link", "path": "/Applications" }
|
||||
]
|
||||
}
|
46
azure-pipelines.yml
Normal file
@ -0,0 +1,46 @@
|
||||
# Starter pipeline
|
||||
|
||||
trigger:
|
||||
branches:
|
||||
include:
|
||||
- master
|
||||
- dev
|
||||
tags:
|
||||
include:
|
||||
- v*
|
||||
|
||||
pool:
|
||||
vmImage: 'macOS-10.14'
|
||||
|
||||
steps:
|
||||
- checkout: self
|
||||
submodules: recursive
|
||||
- script: |
|
||||
brew install protobuf grpc ninja qt5
|
||||
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)
|
||||
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'
|
||||
ArtifactName: 'qv2ray-legacy.dmg'
|
||||
publishLocation: 'Container'
|
||||
- task: GitHubRelease@0
|
||||
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags/v')
|
||||
inputs:
|
||||
gitHubConnection: 'GitHub - DuckSoft'
|
||||
assets: 'qv2ray-legacy.dmg'
|
||||
action: edit
|
||||
tag: '$(Build.SourceBranchName)'
|
||||
isPreRelease: true
|
||||
addChangeLog: false
|
57
cmake/CMakeDMGSetup.scpt
Normal file
@ -0,0 +1,57 @@
|
||||
on run argv
|
||||
set image_name to item 1 of argv
|
||||
|
||||
tell application "Finder"
|
||||
tell disk image_name
|
||||
|
||||
-- wait for the image to finish mounting
|
||||
set open_attempts to 0
|
||||
repeat while open_attempts < 4
|
||||
try
|
||||
open
|
||||
delay 1
|
||||
set open_attempts to 5
|
||||
close
|
||||
on error errStr number errorNumber
|
||||
set open_attempts to open_attempts + 1
|
||||
delay 10
|
||||
end try
|
||||
end repeat
|
||||
delay 5
|
||||
|
||||
-- open the image the first time and save a DS_Store with just
|
||||
-- background and icon setup
|
||||
open
|
||||
set current view of container window to icon view
|
||||
set theViewOptions to the icon view options of container window
|
||||
set background picture of theViewOptions to file ".background:background.png"
|
||||
set arrangement of theViewOptions to not arranged
|
||||
set icon size of theViewOptions to 128
|
||||
delay 5
|
||||
close
|
||||
|
||||
-- next setup the position of the app and Applications symlink
|
||||
-- plus hide all the window decoration
|
||||
open
|
||||
update without registering applications
|
||||
tell container window
|
||||
set sidebar width to 0
|
||||
set statusbar visible to false
|
||||
set toolbar visible to false
|
||||
set the bounds to { 400, 100, 900, 465 }
|
||||
set position of item "qv2ray.app" to { 133, 200 }
|
||||
set position of item "Applications" to { 378, 200 }
|
||||
end tell
|
||||
update without registering applications
|
||||
delay 5
|
||||
close
|
||||
|
||||
-- one last open and close so you can see everything looks correct
|
||||
open
|
||||
delay 5
|
||||
close
|
||||
|
||||
end tell
|
||||
delay 1
|
||||
end tell
|
||||
end run
|
@ -61,6 +61,14 @@ endif()
|
||||
|
||||
if(APPLE)
|
||||
set(CPACK_GENERATOR "DragNDrop")
|
||||
if(DS_STORE_SCRIPT)
|
||||
set(CPACK_DMG_DS_STORE_SETUP_SCRIPT "${CMAKE_SOURCE_DIR}/cmake/CMakeDMGSetup.scpt")
|
||||
else()
|
||||
set(CPACK_DMG_DS_STORE "${CMAKE_SOURCE_DIR}/assets/DS_Store")
|
||||
endif()
|
||||
|
||||
set(CPACK_DMG_BACKGROUND_IMAGE "${CMAKE_SOURCE_DIR}/assets/CMakeDMGBackground.png")
|
||||
configure_file("${CMAKE_SOURCE_DIR}/assets/package_dmg.json.in" "${CMAKE_SOURCE_DIR}/assets/package_dmg.json" @ONLY)
|
||||
endif()
|
||||
|
||||
include(CPack)
|
||||
|
10
debian/changelog
vendored
@ -1,3 +1,13 @@
|
||||
qv2ray (2.5.0~pre1-1) unstable; urgency=medium
|
||||
|
||||
* fix: skip saving config when importing multiple connection configs
|
||||
* add: ignore updating subscription by setting interval to 0
|
||||
* fix: fixed a missing signal emit
|
||||
* update: updated label text, fixed a spelling misATke
|
||||
* add: added kernel plugin support
|
||||
|
||||
-- Guobang Bi <ymshenyu@gmail.com> Sat, 11 Apr 2020 23:12:20 +0800
|
||||
|
||||
qv2ray (2.4.1-1) unstable; urgency=medium
|
||||
|
||||
* add: add new semver checker
|
||||
|
@ -1 +1 @@
|
||||
5150
|
||||
5264
|
@ -1 +1 @@
|
||||
2.4.1
|
||||
2.5.0
|
||||
|
@ -1 +1 @@
|
||||
|
||||
-pre1
|
||||
|
@ -32,5 +32,9 @@
|
||||
<file>assets/icons/ui_light/locate.png</file>
|
||||
<file>assets/icons/ui_dark/sort.png</file>
|
||||
<file>assets/icons/ui_light/sort.png</file>
|
||||
<file>assets/icons/ui_dark/tray-connected.png</file>
|
||||
<file>assets/icons/ui_dark/tray-systemproxy.png</file>
|
||||
<file>assets/icons/ui_light/tray-connected.png</file>
|
||||
<file>assets/icons/ui_light/tray-systemproxy.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
@ -83,6 +83,7 @@ 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
|
||||
@ -108,8 +109,21 @@ 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
|
||||
build-packages:
|
||||
- software-properties-common
|
||||
- dirmngr
|
||||
override-build: |
|
||||
sudo add-apt-repository -y ppa:ymshenyu/qv2ray-deps
|
||||
sudo apt-get dist-upgrade -y
|
@ -46,6 +46,7 @@ using namespace Qv2ray::base::objects::transfer;
|
||||
#define QV2RAY_CONFIG_FILE (QV2RAY_CONFIG_DIR + "Qv2ray.conf")
|
||||
#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/")
|
||||
@ -90,9 +91,17 @@ using namespace Qv2ray::base::objects::transfer;
|
||||
|
||||
#define BLACK(obj) obj->setPalette(QWidget::palette());
|
||||
|
||||
#define QV2RAY_UI_COLORSCHEME_ROOT \
|
||||
#ifdef Q_OS_MACOS
|
||||
#define ACCESS_OPTIONAL_VALUE(obj) (*obj)
|
||||
#else
|
||||
#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 \
|
||||
((GlobalConfig.uiConfig.useDarkTheme) ? QStringLiteral(":/assets/icons/ui_dark/") : QStringLiteral(":/assets/icons/ui_light/"))
|
||||
#define QICON_R(file) QIcon(QV2RAY_UI_COLORSCHEME_ROOT + file)
|
||||
#define QICON_R(file) QIcon(QV2RAY_COLORSCHEME_ROOT + file)
|
||||
|
||||
#define QSTRN(num) QString::number(num)
|
||||
|
||||
@ -127,4 +136,27 @@ namespace Qv2ray
|
||||
isExiting = true;
|
||||
QApplication::quit();
|
||||
}
|
||||
|
||||
inline QStringList Qv2rayAssetsPaths(const QString &dirName)
|
||||
{
|
||||
// Configuration Path
|
||||
QStringList list;
|
||||
list << QV2RAY_CONFIG_DIR + dirName;
|
||||
//
|
||||
#ifdef Q_OS_LINUX
|
||||
// Linux platform directories.
|
||||
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);
|
||||
#elif defined(Q_OS_MAC)
|
||||
// macOS platform directories.
|
||||
list << QDir(QApplication::applicationDirPath() + "/../Resources/" + dirName).absolutePath();
|
||||
#endif
|
||||
// This is the default behavior on Windows
|
||||
list << QApplication::applicationDirPath() + "/" + dirName;
|
||||
list.removeDuplicates();
|
||||
return list;
|
||||
};
|
||||
|
||||
} // namespace Qv2ray
|
||||
|
@ -41,6 +41,7 @@ const inline QString MODULE_FILEIO = "COMMON-FILEIO";
|
||||
//
|
||||
const inline QString MODULE_PROXY = "COMPONENT-PROXY";
|
||||
const inline QString MODULE_UPDATE = "COMPONENT-UPDATE";
|
||||
const inline QString MODULE_PLUGIN = "COMPONENT-PLUGIN";
|
||||
const inline QString MODULE_PLUGINHOST = "COMPONENT-PLUGINHOST";
|
||||
const inline QString MODULE_PLUGINCLIENT = "PLUGIN-CLIENT";
|
||||
// ================================================================
|
||||
const inline QString MODULE_CORE_HANDLER = "QV2RAY-CORE";
|
||||
|
@ -257,13 +257,14 @@ namespace Qv2ray::base::objects
|
||||
{
|
||||
QString serverName;
|
||||
bool allowInsecure;
|
||||
bool allowInsecureCiphers;
|
||||
QList<QString> alpn;
|
||||
QList<CertificateObject> certificates;
|
||||
bool disableSystemRoot;
|
||||
TLSObject() : serverName(), allowInsecure(), certificates(), disableSystemRoot()
|
||||
TLSObject() : serverName(), allowInsecure(), allowInsecureCiphers(), certificates(), disableSystemRoot()
|
||||
{
|
||||
}
|
||||
XTOSTRUCT(O(serverName, allowInsecure, alpn, certificates, disableSystemRoot))
|
||||
XTOSTRUCT(O(serverName, allowInsecure, allowInsecureCiphers, alpn, certificates, disableSystemRoot))
|
||||
};
|
||||
} // namespace transfer
|
||||
//
|
||||
|
@ -41,18 +41,6 @@ namespace Qv2ray::base::config
|
||||
XTOSTRUCT(O(Pages))
|
||||
};
|
||||
|
||||
struct Qv2rayPACConfig
|
||||
{
|
||||
bool enablePAC;
|
||||
int port;
|
||||
QString localIP;
|
||||
bool useSocksProxy;
|
||||
Qv2rayPACConfig() : enablePAC(false), port(8989), useSocksProxy(false)
|
||||
{
|
||||
}
|
||||
XTOSTRUCT(O(enablePAC, port, localIP, useSocksProxy))
|
||||
};
|
||||
|
||||
struct Qv2rayForwardProxyConfig
|
||||
{
|
||||
bool enableForwardProxy;
|
||||
@ -73,7 +61,6 @@ namespace Qv2ray::base::config
|
||||
{
|
||||
QString listenip;
|
||||
bool setSystemProxy;
|
||||
Qv2rayPACConfig pacConfig;
|
||||
|
||||
// SOCKS
|
||||
bool useSocks;
|
||||
@ -89,26 +76,27 @@ namespace Qv2ray::base::config
|
||||
objects::AccountObject httpAccount;
|
||||
|
||||
Qv2rayInboundsConfig()
|
||||
: listenip("127.0.0.1"), setSystemProxy(true), pacConfig(), useSocks(true), socks_port(1088), socks_useAuth(false), socksUDP(true),
|
||||
: 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()
|
||||
{
|
||||
}
|
||||
|
||||
XTOSTRUCT(O(setSystemProxy, pacConfig, listenip, useSocks, useHTTP, socks_port, socks_useAuth, socksAccount, socksUDP, socksLocalIP,
|
||||
http_port, http_useAuth, httpAccount))
|
||||
XTOSTRUCT(O(setSystemProxy, listenip, useSocks, useHTTP, socks_port, socks_useAuth, socksAccount, socksUDP, socksLocalIP, http_port,
|
||||
http_useAuth, httpAccount))
|
||||
};
|
||||
|
||||
struct Qv2rayUIConfig
|
||||
{
|
||||
QString theme;
|
||||
QString language;
|
||||
bool quietMode;
|
||||
bool useDarkTheme;
|
||||
bool useDarkTrayIcon;
|
||||
int maximumLogLines;
|
||||
Qv2rayUIConfig() : theme("Fusion"), language("en_US"), useDarkTheme(false), useDarkTrayIcon(true), maximumLogLines(500)
|
||||
{
|
||||
}
|
||||
XTOSTRUCT(O(theme, language, useDarkTheme, useDarkTrayIcon, maximumLogLines))
|
||||
XTOSTRUCT(O(theme, language, quietMode, useDarkTheme, useDarkTrayIcon, maximumLogLines))
|
||||
};
|
||||
|
||||
struct Qv2rayRouteConfig_Impl
|
||||
@ -141,6 +129,15 @@ namespace Qv2ray::base::config
|
||||
XTOSTRUCT(O(domainStrategy, domains, ips))
|
||||
};
|
||||
|
||||
struct Qv2rayPluginConfig
|
||||
{
|
||||
QMap<QString, bool> pluginStates;
|
||||
bool v2rayIntegration;
|
||||
int portAllocationStart;
|
||||
Qv2rayPluginConfig() : pluginStates(), v2rayIntegration(true), portAllocationStart(15000){};
|
||||
XTOSTRUCT(O(pluginStates, v2rayIntegration))
|
||||
};
|
||||
|
||||
struct Qv2rayConnectionConfig
|
||||
{
|
||||
bool bypassCN;
|
||||
@ -218,6 +215,25 @@ namespace Qv2ray::base::config
|
||||
XTOSTRUCT(O(ignoredVersion, updateChannel))
|
||||
};
|
||||
|
||||
struct Qv2rayAdvancedConfig
|
||||
{
|
||||
bool setAllowInsecure;
|
||||
bool setAllowInsecureCiphers;
|
||||
bool testLatencyPeriodcally;
|
||||
XTOSTRUCT(O(setAllowInsecure, setAllowInsecureCiphers, testLatencyPeriodcally))
|
||||
};
|
||||
|
||||
struct Qv2rayNetworkConfig
|
||||
{
|
||||
bool useCustomProxy;
|
||||
QString address;
|
||||
QString type;
|
||||
int port;
|
||||
QString userAgent;
|
||||
Qv2rayNetworkConfig() : address(""), type("http"), port(8000), userAgent("Qv2ray/" QV2RAY_VERSION_STRING " WebRequestHelper"){};
|
||||
XTOSTRUCT(O(useCustomProxy, type, address, port, userAgent))
|
||||
};
|
||||
|
||||
struct Qv2rayConfig
|
||||
{
|
||||
int config_version;
|
||||
@ -234,19 +250,53 @@ namespace Qv2ray::base::config
|
||||
//
|
||||
Qv2rayUIConfig uiConfig;
|
||||
Qv2rayAPIConfig apiConfig;
|
||||
Qv2rayPluginConfig pluginConfig;
|
||||
Qv2rayKernelConfig kernelConfig;
|
||||
Qv2rayUpdateConfig updateConfig;
|
||||
Qv2rayNetworkConfig networkConfig;
|
||||
Qv2rayToolBarConfig toolBarConfig;
|
||||
Qv2rayInboundsConfig inboundConfig;
|
||||
Qv2rayAdvancedConfig advancedConfig;
|
||||
Qv2rayConnectionConfig connectionConfig;
|
||||
|
||||
Qv2rayConfig()
|
||||
: config_version(QV2RAY_CONFIG_VERSION), tProxySupport(false), logLevel(), autoStartId("null"), groups(), subscriptions(),
|
||||
connections(), uiConfig(), apiConfig(), kernelConfig(), updateConfig(), toolBarConfig(), inboundConfig(), connectionConfig()
|
||||
: config_version(QV2RAY_CONFIG_VERSION), //
|
||||
tProxySupport(false), //
|
||||
logLevel(), //
|
||||
autoStartId("null"), //
|
||||
groups(), //
|
||||
subscriptions(), //
|
||||
connections(), //
|
||||
uiConfig(), //
|
||||
apiConfig(), //
|
||||
pluginConfig(), //
|
||||
kernelConfig(), //
|
||||
updateConfig(), //
|
||||
networkConfig(), //
|
||||
toolBarConfig(), //
|
||||
inboundConfig(), //
|
||||
advancedConfig(), //
|
||||
connectionConfig()
|
||||
{
|
||||
}
|
||||
|
||||
XTOSTRUCT(O(config_version, tProxySupport, logLevel, uiConfig, kernelConfig, updateConfig, groups, connections, subscriptions,
|
||||
autoStartId, inboundConfig, connectionConfig, toolBarConfig, apiConfig))
|
||||
XTOSTRUCT(O(config_version, //
|
||||
tProxySupport, //
|
||||
logLevel, //
|
||||
uiConfig, //
|
||||
pluginConfig, //
|
||||
updateConfig, //
|
||||
kernelConfig, //
|
||||
networkConfig, //
|
||||
groups, //
|
||||
connections, //
|
||||
subscriptions, //
|
||||
autoStartId, //
|
||||
inboundConfig, //
|
||||
connectionConfig, //
|
||||
toolBarConfig, //
|
||||
advancedConfig, //
|
||||
apiConfig //
|
||||
))
|
||||
};
|
||||
} // namespace Qv2ray::base::config
|
||||
|
@ -16,6 +16,8 @@ namespace Qv2ray
|
||||
bool enableToolbarPlguin;
|
||||
/// Disable Qt scale factors support.
|
||||
bool noScaleFactors;
|
||||
/// Disable all plugin features.
|
||||
bool noPlugins;
|
||||
};
|
||||
} // namespace base
|
||||
inline base::QvStartupOptions StartupOption = base::QvStartupOptions();
|
||||
|
@ -9,6 +9,7 @@ namespace Qv2ray::common
|
||||
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")
|
||||
@ -20,6 +21,7 @@ namespace Qv2ray::common
|
||||
parser.addOption(runAsRootOption);
|
||||
parser.addOption(debugOption);
|
||||
parser.addOption(noScaleFactorOption);
|
||||
parser.addOption(noPluginsOption);
|
||||
parser.addOption(withToolbarOption);
|
||||
helpOption = parser.addHelpOption();
|
||||
versionOption = parser.addVersionOption();
|
||||
@ -63,6 +65,12 @@ namespace Qv2ray::common
|
||||
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.")
|
||||
|
@ -28,6 +28,7 @@ namespace Qv2ray::common
|
||||
QCommandLineOption runAsRootOption;
|
||||
QCommandLineOption debugOption;
|
||||
QCommandLineOption noScaleFactorOption;
|
||||
QCommandLineOption noPluginsOption;
|
||||
QCommandLineOption withToolbarOption;
|
||||
QCommandLineOption helpOption;
|
||||
QCommandLineOption versionOption;
|
||||
|
@ -16,112 +16,80 @@ namespace Qv2ray::common
|
||||
accessManager.disconnect();
|
||||
}
|
||||
|
||||
bool QvHttpRequestHelper::setUrl(const QString &url)
|
||||
{
|
||||
QUrl qUrl = QUrl(url);
|
||||
|
||||
if (!qUrl.isValid())
|
||||
{
|
||||
LOG(MODULE_NETWORK, "Provided URL is invalid: " + url)
|
||||
return false;
|
||||
}
|
||||
|
||||
request.setUrl(qUrl);
|
||||
return true;
|
||||
}
|
||||
|
||||
void QvHttpRequestHelper::setHeader(const QByteArray &key, const QByteArray &value)
|
||||
{
|
||||
DEBUG(MODULE_NETWORK, "Adding HTTP request header: " + key + ":" + value)
|
||||
request.setRawHeader(key, value);
|
||||
}
|
||||
|
||||
QByteArray QvHttpRequestHelper::syncget(const QString &url, bool useProxy)
|
||||
QByteArray QvHttpRequestHelper::Get(const QString &url, bool useProxy)
|
||||
{
|
||||
this->setUrl(url);
|
||||
|
||||
request.setUrl({ url });
|
||||
if (useProxy)
|
||||
{
|
||||
auto proxy = QNetworkProxyFactory::systemProxyForQuery();
|
||||
accessManager.setProxy(proxy.first());
|
||||
LOG(MODULE_NETWORK, "Sync get is using system proxy settings")
|
||||
auto p = GlobalConfig.networkConfig.useCustomProxy ?
|
||||
QNetworkProxy{
|
||||
GlobalConfig.networkConfig.type == "http" ? QNetworkProxy::HttpProxy : QNetworkProxy::Socks5Proxy, //
|
||||
GlobalConfig.networkConfig.address, //
|
||||
quint16(GlobalConfig.networkConfig.port) //
|
||||
} :
|
||||
QNetworkProxyFactory::systemProxyForQuery().first();
|
||||
accessManager.setProxy(p);
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG(MODULE_NETWORK, "Get without proxy.")
|
||||
accessManager.setProxy(QNetworkProxy(QNetworkProxy::ProxyType::NoProxy));
|
||||
}
|
||||
|
||||
if (accessManager.proxy().type() == QNetworkProxy::Socks5Proxy)
|
||||
{
|
||||
DEBUG(MODULE_NETWORK, "Adding HostNameLookupCapability to proxy.")
|
||||
accessManager.proxy().setCapabilities(accessManager.proxy().capabilities() | QNetworkProxy::HostNameLookupCapability);
|
||||
}
|
||||
request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
|
||||
request.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, true);
|
||||
request.setHeader(QNetworkRequest::KnownHeaders::UserAgentHeader, "Mozilla/5.0 (rv:71.0) Gecko/20100101 Firefox/71.0");
|
||||
request.setHeader(QNetworkRequest::KnownHeaders::UserAgentHeader, GlobalConfig.networkConfig.userAgent);
|
||||
reply = accessManager.get(request);
|
||||
connect(reply, &QNetworkReply::finished, this, &QvHttpRequestHelper::onRequestFinished_p);
|
||||
//
|
||||
QEventLoop loop;
|
||||
connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
||||
loop.exec();
|
||||
//
|
||||
// Data or timeout?
|
||||
auto data = reply->readAll();
|
||||
return data;
|
||||
}
|
||||
|
||||
void QvHttpRequestHelper::get(const QString &url)
|
||||
void QvHttpRequestHelper::AsyncGet(const QString &url)
|
||||
{
|
||||
this->setUrl(url);
|
||||
// request.setRawHeader("Content-Type",
|
||||
// "application/x-www-form-urlencoded");
|
||||
request.setUrl({ url });
|
||||
if (GlobalConfig.networkConfig.useCustomProxy)
|
||||
{
|
||||
QNetworkProxy p{
|
||||
GlobalConfig.networkConfig.type == "http" ? QNetworkProxy::HttpProxy : QNetworkProxy::Socks5Proxy, //
|
||||
GlobalConfig.networkConfig.address, //
|
||||
quint16(GlobalConfig.networkConfig.port) //
|
||||
};
|
||||
accessManager.setProxy(p);
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG(MODULE_NETWORK, "Get without proxy.")
|
||||
accessManager.setProxy(QNetworkProxy(QNetworkProxy::ProxyType::NoProxy));
|
||||
}
|
||||
if (accessManager.proxy().type() == QNetworkProxy::Socks5Proxy)
|
||||
{
|
||||
DEBUG(MODULE_NETWORK, "Adding HostNameLookupCapability to proxy.")
|
||||
accessManager.proxy().setCapabilities(accessManager.proxy().capabilities() | QNetworkProxy::HostNameLookupCapability);
|
||||
}
|
||||
request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
|
||||
request.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, true);
|
||||
request.setHeader(QNetworkRequest::KnownHeaders::UserAgentHeader, GlobalConfig.networkConfig.userAgent);
|
||||
reply = accessManager.get(request);
|
||||
connect(reply, &QNetworkReply::finished, this, &QvHttpRequestHelper::onRequestFinished_p);
|
||||
connect(reply, &QNetworkReply::readyRead, this, &QvHttpRequestHelper::onReadyRead);
|
||||
connect(reply, &QNetworkReply::readyRead, this, &QvHttpRequestHelper::onReadyRead_p);
|
||||
}
|
||||
|
||||
// void QvHttpRequestHelper::post(const QString &url, const QByteArray
|
||||
// &data)
|
||||
//{
|
||||
// this->setUrl(url);
|
||||
// request.setRawHeader("Content-Type", "application/json");
|
||||
// reply = accessManager.post(request, data);
|
||||
// connect(reply, &QNetworkReply::finished, this,
|
||||
// &QvHttpRequestHelper::onRequestFinished); connect(reply,
|
||||
// &QNetworkReply::readyRead, this, &QvHttpRequestHelper::onReadyRead);
|
||||
//}
|
||||
|
||||
// void QvHttpRequestHelper::put(const QString &url, const QByteArray
|
||||
// &data)
|
||||
// {
|
||||
// this->setUrl(url);
|
||||
// request.setRawHeader("Content-Type", "application/json");
|
||||
// reply = accessManager.put(request, data);
|
||||
// connect(reply, &QNetworkReply::finished, this,
|
||||
// &QvHttpRequestHelper::onRequestFinished); connect(reply,
|
||||
// &QNetworkReply::readyRead, this,
|
||||
// &QvHttpRequestHelper::onReadyRead);
|
||||
// }
|
||||
|
||||
// void QvHttpRequestHelper::del(const QString &url)
|
||||
// {
|
||||
// this->setUrl(url);
|
||||
// request.setRawHeader("Content-Type", "application/json");
|
||||
// reply = accessManager.deleteResource(request);
|
||||
// connect(reply, &QNetworkReply::finished, this,
|
||||
// &QvHttpRequestHelper::onRequestFinished); connect(reply,
|
||||
// &QNetworkReply::readyRead, this,
|
||||
// &QvHttpRequestHelper::onReadyRead);
|
||||
// }
|
||||
|
||||
// void QvHttpRequestHelper::login(const QString &url, const QByteArray
|
||||
// &data)
|
||||
// {
|
||||
// this->setUrl(url);
|
||||
// request.setRawHeader("Content-Type",
|
||||
// "application/x-www-form-urlencoded"); reply =
|
||||
// accessManager.post(request, data); connect(reply,
|
||||
// &QNetworkReply::finished, this,
|
||||
// &QvHttpRequestHelper::onRequestFinished); connect(reply,
|
||||
// &QNetworkReply::readyRead, this,
|
||||
// &QvHttpRequestHelper::onReadyRead);
|
||||
// }
|
||||
|
||||
void QvHttpRequestHelper::onRequestFinished_p()
|
||||
{
|
||||
if (reply->attribute(QNetworkRequest::HTTP2WasUsedAttribute).toBool())
|
||||
@ -134,15 +102,15 @@ namespace Qv2ray::common
|
||||
QString error = QMetaEnum::fromType<QNetworkReply::NetworkError>().key(reply->error());
|
||||
LOG(MODULE_NETWORK, "Network request error string: " + error)
|
||||
QByteArray empty;
|
||||
emit httpRequestFinished(empty);
|
||||
emit OnRequestFinished(empty);
|
||||
}
|
||||
else
|
||||
{
|
||||
emit httpRequestFinished(this->data);
|
||||
emit OnRequestFinished(this->data);
|
||||
}
|
||||
}
|
||||
|
||||
void QvHttpRequestHelper::onReadyRead()
|
||||
void QvHttpRequestHelper::onReadyRead_p()
|
||||
{
|
||||
DEBUG(MODULE_NETWORK, "A request is now ready read")
|
||||
this->data += reply->readAll();
|
||||
|
@ -31,26 +31,18 @@ namespace Qv2ray::common
|
||||
public:
|
||||
explicit QvHttpRequestHelper(QObject *parent = nullptr);
|
||||
~QvHttpRequestHelper();
|
||||
bool setUrl(const QString &url);
|
||||
void setHeader(const QByteArray &key, const QByteArray &value);
|
||||
// get
|
||||
QByteArray syncget(const QString &url, bool useProxy);
|
||||
void get(const QString &url);
|
||||
//// insert
|
||||
// void post(const QString &url, const QByteArray &data);
|
||||
//// update
|
||||
// void put(const QString &url, const QByteArray &data);
|
||||
//// delete
|
||||
// void del(const QString &url);
|
||||
// void login(const QString &url, const QByteArray &data);
|
||||
void AsyncGet(const QString &url);
|
||||
QByteArray Get(const QString &url, bool useProxy);
|
||||
signals:
|
||||
void httpRequestFinished(QByteArray &data);
|
||||
void OnRequestFinished(QByteArray &data);
|
||||
|
||||
private slots:
|
||||
void onRequestFinished_p();
|
||||
void onReadyRead();
|
||||
void onReadyRead_p();
|
||||
|
||||
private:
|
||||
void setHeader(const QByteArray &key, const QByteArray &value);
|
||||
QByteArray data;
|
||||
QUrl url;
|
||||
QNetworkReply *reply;
|
||||
|
@ -7,14 +7,16 @@
|
||||
#include "TextUtfEncoding.h"
|
||||
#include "base/Qv2rayBase.hpp"
|
||||
|
||||
namespace Qv2ray::components
|
||||
namespace Qv2ray::common
|
||||
{
|
||||
using namespace ZXing;
|
||||
QString DecodeQRCode(const QImage &source)
|
||||
{
|
||||
if (source.isNull())
|
||||
return "";
|
||||
QImage img = source.copy();
|
||||
auto result =
|
||||
ReadBarcode(img.width(), img.height(), img.bits(), img.width() * 4, 4, 0, 1, 2, { BarcodeFormatFromString("") }, true, true);
|
||||
const auto result =
|
||||
ReadBarcode(img.width(), img.height(), img.bits(), img.width() * 4, 4, 0, 1, 2, { ZXing::BarcodeFormat::QR_CODE }, true, true);
|
||||
|
||||
if (result.isValid())
|
||||
{
|
||||
@ -34,17 +36,15 @@ namespace Qv2ray::components
|
||||
int eccLevel = 1;
|
||||
try
|
||||
{
|
||||
auto barcodeFormat = BarcodeFormatFromString("QR_CODE");
|
||||
|
||||
MultiFormatWriter writer(barcodeFormat);
|
||||
MultiFormatWriter writer(ZXing::BarcodeFormat::QR_CODE);
|
||||
writer.setMargin(1);
|
||||
writer.setEccLevel(eccLevel);
|
||||
|
||||
auto bitmap = writer.encode(content.toStdWString(), size.width(), size.height());
|
||||
auto BM = bitmap.toByteMatrix();
|
||||
const auto bitmap = writer.encode(content.toStdWString(), size.width(), size.height());
|
||||
const auto BM = bitmap.toByteMatrix();
|
||||
//
|
||||
const QRgb black = qRgba(0, 0, 0, 255);
|
||||
const QRgb white = qRgba(255, 255, 255, 255);
|
||||
const auto black = qRgba(0, 0, 0, 255);
|
||||
const auto white = qRgba(255, 255, 255, 255);
|
||||
//
|
||||
auto image = QImage(bitmap.width(), bitmap.width(), QImage::Format_ARGB32);
|
||||
image.fill(white);
|
||||
@ -67,4 +67,4 @@ namespace Qv2ray::components
|
||||
return {};
|
||||
}
|
||||
}
|
||||
} // namespace Qv2ray::components
|
||||
} // namespace Qv2ray::common
|
||||
|
@ -2,9 +2,9 @@
|
||||
|
||||
#include <QImage>
|
||||
#include <QString>
|
||||
namespace Qv2ray::components
|
||||
namespace Qv2ray::common
|
||||
{
|
||||
QString DecodeQRCode(const QImage &img);
|
||||
QImage EncodeQRCode(const QString &content, const QSize &size);
|
||||
} // namespace Qv2ray::components
|
||||
using namespace Qv2ray::components;
|
||||
} // namespace Qv2ray::common
|
||||
using namespace Qv2ray::common;
|
||||
|
@ -17,33 +17,14 @@ using namespace Qv2ray::base;
|
||||
QStringList getLanguageSearchPaths()
|
||||
{
|
||||
// Configuration Path
|
||||
QStringList list;
|
||||
list << QV2RAY_CONFIG_DIR + "lang";
|
||||
//
|
||||
QStringList list = Qv2rayAssetsPaths("lang");
|
||||
#ifdef EMBED_TRANSLATIONS
|
||||
// If the translations have been embedded.
|
||||
list << QString(":/translations/");
|
||||
#endif
|
||||
//
|
||||
//
|
||||
#ifdef QV2RAY_TRANSLATION_PATH
|
||||
// Platform-specific dir, if specified.
|
||||
list << QString(QV2RAY_TRANSLATION_PATH);
|
||||
#endif
|
||||
//
|
||||
//
|
||||
#ifdef Q_OS_LINUX
|
||||
// Linux platform directories.
|
||||
list << QString("/usr/share/qv2ray/lang/");
|
||||
list << QString("/usr/local/share/qv2ray/lang/");
|
||||
list << QStandardPaths::locateAll(QStandardPaths::AppDataLocation, "lang", QStandardPaths::LocateDirectory);
|
||||
list << QStandardPaths::locateAll(QStandardPaths::AppConfigLocation, "lang", QStandardPaths::LocateDirectory);
|
||||
#elif defined(Q_OS_MAC)
|
||||
// macOS platform directories.
|
||||
list << QDir(QApplication::applicationDirPath() + "/../Resources/lang").absolutePath();
|
||||
#else
|
||||
// This is the default behavior on Windows
|
||||
list << QApplication::applicationDirPath() + "/lang";
|
||||
#endif
|
||||
return list;
|
||||
};
|
||||
|
@ -1,137 +0,0 @@
|
||||
/* ORIGINAL LICENSE: Do What The F*ck You Want To Public License
|
||||
* AUTHOR: LBYPatrick
|
||||
*
|
||||
* MODIFIED BY Leroy.H.Y @lhy0403 re-licenced under GPLv3
|
||||
*/
|
||||
|
||||
#include "common/QvHelpers.hpp"
|
||||
|
||||
namespace Qv2ray::components::pac
|
||||
{
|
||||
// Private function
|
||||
string getRawDomain(string originLine)
|
||||
{
|
||||
size_t startPosition = 0;
|
||||
size_t endPosition = originLine.size();
|
||||
string returnBuffer;
|
||||
bool skipRule1 = originLine.find("[") != string::npos; // [Auto xxxx...
|
||||
bool skipRule2 = originLine.find("!") != string::npos; // Comments
|
||||
bool skipRule3 = originLine.find("@") != string::npos; // Non-proxy Lines
|
||||
bool skipRule4 = originLine.find("*") != string::npos;
|
||||
bool passRule1 = originLine.find("|") != string::npos; // Proxy Lines
|
||||
bool passRule2 = originLine.find(".") != string::npos; // Link-Contained Lines
|
||||
|
||||
if (originLine[endPosition] == '\n')
|
||||
{
|
||||
endPosition -= 1;
|
||||
}
|
||||
|
||||
if (originLine.find("http://") != string::npos)
|
||||
{
|
||||
startPosition += 8;
|
||||
}
|
||||
else if (originLine.find("https://") != string::npos)
|
||||
{
|
||||
startPosition += 9;
|
||||
}
|
||||
|
||||
// Skip unrelated lines
|
||||
if (skipRule1 || skipRule2 || skipRule3 || skipRule4)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
else if (passRule2)
|
||||
{
|
||||
if (passRule1)
|
||||
{
|
||||
startPosition += originLine.find_last_of("|") + 1;
|
||||
}
|
||||
|
||||
if (originLine[startPosition] == '\n')
|
||||
startPosition += 1;
|
||||
|
||||
for (size_t i = startPosition; i < endPosition; ++i)
|
||||
{
|
||||
returnBuffer += originLine[i];
|
||||
}
|
||||
}
|
||||
|
||||
return returnBuffer;
|
||||
}
|
||||
|
||||
QString ConvertGFWToPAC(const QString &rawContent, const QString &customProxyString)
|
||||
{
|
||||
auto rawFileContent = Base64Decode(rawContent).toStdString();
|
||||
string readBuffer = ""; // cleanup
|
||||
string writeBuffer;
|
||||
string domainListCache = "";
|
||||
|
||||
for (size_t i = 0; i < rawFileContent.size(); ++i)
|
||||
{
|
||||
readBuffer += rawFileContent[i];
|
||||
|
||||
if (rawFileContent[i + 1] == '\n')
|
||||
{
|
||||
writeBuffer = getRawDomain(readBuffer);
|
||||
|
||||
if (writeBuffer != "")
|
||||
{
|
||||
domainListCache += writeBuffer + "\n";
|
||||
}
|
||||
|
||||
readBuffer = "";
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
size_t rotatorTwo = 0;
|
||||
string readDomainBuffer = "";
|
||||
bool isFirstLine = true;
|
||||
string outputContent = "";
|
||||
// Header
|
||||
outputContent += "var domains = {\n";
|
||||
|
||||
// Read and process output content line by line
|
||||
while (rotatorTwo < domainListCache.size())
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
// Get Domain
|
||||
readDomainBuffer += domainListCache[rotatorTwo];
|
||||
|
||||
if (domainListCache[rotatorTwo + 1] == '\n')
|
||||
{
|
||||
rotatorTwo += 2;
|
||||
break;
|
||||
}
|
||||
|
||||
rotatorTwo++;
|
||||
}
|
||||
|
||||
// Format
|
||||
if (!isFirstLine)
|
||||
outputContent += ",\n";
|
||||
else
|
||||
isFirstLine = false;
|
||||
|
||||
outputContent += "\t\"";
|
||||
outputContent += readDomainBuffer;
|
||||
outputContent += "\" : 1";
|
||||
readDomainBuffer = "";
|
||||
}
|
||||
|
||||
// End Message
|
||||
outputContent += NEWLINE "};" NEWLINE "" NEWLINE " var proxy = \"" + customProxyString.toStdString() + ";\";" +
|
||||
NEWLINE " var direct = 'DIRECT;';" NEWLINE " function FindProxyForURL(url, host) {" NEWLINE
|
||||
" var suffix;" NEWLINE " var pos = host.lastIndexOf('.');" NEWLINE
|
||||
" pos = host.lastIndexOf('.', pos - 1);" NEWLINE " //" NEWLINE " while (1) {" NEWLINE
|
||||
" if (domains[host] != undefined) {" NEWLINE " return proxy;" NEWLINE
|
||||
" }" NEWLINE " else if (pos <= 0) {" NEWLINE
|
||||
" return domains['.' + host] != undefined ? proxy : direct;" NEWLINE " }" NEWLINE
|
||||
" suffix = host.substring(pos);" NEWLINE " if (domains[suffix] != undefined) {" NEWLINE
|
||||
" return proxy;" NEWLINE " }" NEWLINE
|
||||
" pos = host.lastIndexOf('.', pos - 1);" NEWLINE " }" NEWLINE " }";
|
||||
//
|
||||
return QString::fromStdString(outputContent);
|
||||
}
|
||||
} // namespace Qv2ray::components::pac
|
@ -1,77 +0,0 @@
|
||||
#include "QvPACHandler.hpp"
|
||||
|
||||
#include "3rdparty/cpp-httplib/httplib.h"
|
||||
#include "common/QvHelpers.hpp"
|
||||
#include "core/CoreUtils.hpp"
|
||||
|
||||
namespace Qv2ray::components::pac
|
||||
{
|
||||
PACServer::PACServer(QObject *parent) : QThread(parent)
|
||||
{
|
||||
server = new httplib::Server();
|
||||
connect(this, &QThread::finished, this, &QThread::deleteLater);
|
||||
}
|
||||
PACServer::~PACServer()
|
||||
{
|
||||
wait();
|
||||
DEBUG(MODULE_PROXY, "~PACServer")
|
||||
delete server;
|
||||
}
|
||||
void PACServer::stopServer()
|
||||
{
|
||||
if (server->is_running())
|
||||
server->stop();
|
||||
quit();
|
||||
LOG(MODULE_UI, "Stopping PAC server")
|
||||
}
|
||||
void PACServer::run()
|
||||
{
|
||||
LOG(MODULE_PROXY, "Starting PAC listener")
|
||||
//
|
||||
auto address = GlobalConfig.inboundConfig.listenip;
|
||||
auto port = GlobalConfig.inboundConfig.pacConfig.port;
|
||||
//
|
||||
DEBUG(MODULE_PROXY, "PAC Listening local endpoint: " + address + ":" + QSTRN(port))
|
||||
//
|
||||
QString gfwContent = StringFromFile(QV2RAY_RULES_GFWLIST_PATH);
|
||||
pacContent = ConvertGFWToPAC(gfwContent, proxyString);
|
||||
//
|
||||
server->Get("/pac", pacRequestHandler);
|
||||
auto result = server->listen(address.toStdString().c_str(), static_cast<ushort>(port));
|
||||
if (result)
|
||||
{
|
||||
DEBUG(MODULE_PROXY, "PAC handler stopped.")
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(MODULE_PROXY, "Failed to listen on port " + QSTRN(port) + ", possible permission denied.")
|
||||
QvMessageBoxWarn(nullptr, tr("PAC Handler"), tr("Failed to listen PAC request on this port, please verify the permissions"));
|
||||
}
|
||||
}
|
||||
|
||||
void PACServer::pacRequestHandler(const httplib::Request &req, httplib::Response &rsp)
|
||||
{
|
||||
rsp.set_header("Server", "Qv2ray/" QV2RAY_VERSION_STRING " PAC_Handler");
|
||||
if (req.method == "GET")
|
||||
{
|
||||
if (req.path == "/pac")
|
||||
{
|
||||
DEBUG(MODULE_PROXY, "Serving PAC file request.")
|
||||
//
|
||||
rsp.status = 200;
|
||||
rsp.set_content(pacContent.toStdString(), "application/javascript; charset=utf-8");
|
||||
DEBUG(MODULE_PROXY, "Serving a pac file...")
|
||||
}
|
||||
else
|
||||
{
|
||||
rsp.status = 404;
|
||||
rsp.set_content("NOT FOUND", "text/plain; charset=utf-8");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rsp.status = 405;
|
||||
rsp.set_content("PAC ONLY SUPPORT GET", "text/plain; charset=utf-8");
|
||||
}
|
||||
}
|
||||
} // namespace Qv2ray::components::pac
|
@ -1,41 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QThread>
|
||||
#include <memory>
|
||||
|
||||
namespace httplib
|
||||
{
|
||||
class Server;
|
||||
struct Request;
|
||||
struct Response;
|
||||
} // namespace httplib
|
||||
|
||||
namespace Qv2ray::components::pac
|
||||
{
|
||||
QString ConvertGFWToPAC(const QString &rawContent, const QString &customProxyString);
|
||||
class PACServer : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit PACServer(QObject *parent = nullptr);
|
||||
~PACServer();
|
||||
inline void setPACProxyString(const QString &proxyStr)
|
||||
{
|
||||
proxyString = proxyStr;
|
||||
}
|
||||
void stopServer();
|
||||
|
||||
private:
|
||||
void run() override;
|
||||
QString proxyString;
|
||||
|
||||
private:
|
||||
httplib::Server *server;
|
||||
static void pacRequestHandler(const httplib::Request &req, httplib::Response &rsp);
|
||||
static inline QString pacContent;
|
||||
};
|
||||
} // namespace Qv2ray::components::pac
|
||||
|
||||
using namespace Qv2ray::components;
|
||||
using namespace Qv2ray::components::pac;
|
370
src/components/plugins/QvPluginHost.cpp
Normal file
@ -0,0 +1,370 @@
|
||||
#include "QvPluginHost.hpp"
|
||||
|
||||
#include "base/Qv2rayBase.hpp"
|
||||
#include "base/Qv2rayLog.hpp"
|
||||
#include "common/QvHelpers.hpp"
|
||||
#include "core/settings/SettingsBackend.hpp"
|
||||
|
||||
#include <QPluginLoader>
|
||||
|
||||
namespace Qv2ray::components::plugins
|
||||
{
|
||||
QvPluginHost::QvPluginHost(QObject *parent) : QObject(parent)
|
||||
{
|
||||
if (!StartupOption.noPlugins)
|
||||
{
|
||||
if (auto dir = QDir(QV2RAY_PLUGIN_SETTINGS_DIR); !dir.exists())
|
||||
{
|
||||
dir.mkpath(QV2RAY_PLUGIN_SETTINGS_DIR);
|
||||
}
|
||||
InitializePluginHost();
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(MODULE_PLUGINHOST, "PluginHost initilization skipped by command line option.")
|
||||
}
|
||||
}
|
||||
|
||||
int QvPluginHost::RefreshPluginList()
|
||||
{
|
||||
ClearPlugins();
|
||||
LOG(MODULE_PLUGINHOST, "Reloading plugin list")
|
||||
for (const auto &pluginDirPath : Qv2rayAssetsPaths("plugins"))
|
||||
{
|
||||
const QStringList entries = QDir(pluginDirPath).entryList(QDir::Files);
|
||||
for (const auto &fileName : entries)
|
||||
{
|
||||
DEBUG(MODULE_PLUGINHOST, "Loading plugin: " + fileName + " from: " + pluginDirPath)
|
||||
//
|
||||
QvPluginInfo info;
|
||||
auto pluginFullPath = QDir(pluginDirPath).absoluteFilePath(fileName);
|
||||
info.libraryPath = pluginFullPath;
|
||||
info.pluginLoader = new QPluginLoader(pluginFullPath, this);
|
||||
// auto meta = pluginLoader.metaData();
|
||||
// You should not call "delete" on this object, it's handled by the QPluginLoader
|
||||
QObject *plugin = info.pluginLoader->instance();
|
||||
if (plugin == nullptr)
|
||||
{
|
||||
LOG(MODULE_PLUGINHOST, info.pluginLoader->errorString());
|
||||
continue;
|
||||
}
|
||||
info.pluginInterface = qobject_cast<Qv2rayInterface *>(plugin);
|
||||
if (info.pluginInterface == nullptr)
|
||||
{
|
||||
LOG(MODULE_PLUGINHOST, "Failed to cast from QObject to Qv2rayPluginInterface")
|
||||
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")
|
||||
QvMessageBoxWarn(nullptr, tr("Cannot load plugin"),
|
||||
info.metadata.Name + " " + tr("cannot be loaded.") + 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;
|
||||
}
|
||||
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 + "\"")
|
||||
plugins.insert(info.metadata.InternalName, info);
|
||||
}
|
||||
}
|
||||
return plugins.count();
|
||||
}
|
||||
|
||||
void QvPluginHost::QvPluginLog(const QString &log)
|
||||
{
|
||||
auto _sender = sender();
|
||||
if (auto _interface = qobject_cast<Qv2rayInterface *>(_sender); _interface)
|
||||
{
|
||||
LOG(MODULE_PLUGINCLIENT + "-" + _interface->GetMetadata().InternalName, log)
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(MODULE_PLUGINHOST, "UNKNOWN CLIENT: " + log)
|
||||
}
|
||||
}
|
||||
|
||||
void QvPluginHost::QvPluginMessageBox(const QString &msg)
|
||||
{
|
||||
auto _sender = sender();
|
||||
if (auto _interface = qobject_cast<Qv2rayInterface *>(_sender); _interface)
|
||||
{
|
||||
QvMessageBoxWarn(nullptr, _interface->GetMetadata().Name, msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
QvMessageBoxWarn(nullptr, "Unknown Plugin", msg);
|
||||
}
|
||||
}
|
||||
|
||||
bool QvPluginHost::GetPluginEnableState(const QString &internalName) const
|
||||
{
|
||||
return GlobalConfig.pluginConfig.pluginStates[internalName];
|
||||
}
|
||||
|
||||
void QvPluginHost::SetPluginEnableState(const QString &internalName, bool isEnabled)
|
||||
{
|
||||
LOG(MODULE_PLUGINHOST, "Set plugin: \"" + internalName + "\" enable state: " + (isEnabled ? "true" : "false"))
|
||||
GlobalConfig.pluginConfig.pluginStates[internalName] = isEnabled;
|
||||
if (isEnabled && !plugins[internalName].isLoaded)
|
||||
{
|
||||
// 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."));
|
||||
}
|
||||
}
|
||||
|
||||
void QvPluginHost::InitializePluginHost()
|
||||
{
|
||||
RefreshPluginList();
|
||||
for (auto &plugin : plugins.keys())
|
||||
{
|
||||
InitializePlugin(plugin);
|
||||
}
|
||||
}
|
||||
|
||||
void QvPluginHost::ClearPlugins()
|
||||
{
|
||||
for (auto &&plugin : plugins)
|
||||
{
|
||||
DEBUG(MODULE_PLUGINHOST, "Unloading: \"" + plugin.metadata.Name + "\"")
|
||||
plugin.pluginLoader->unload();
|
||||
plugin.pluginLoader->deleteLater();
|
||||
}
|
||||
plugins.clear();
|
||||
}
|
||||
bool QvPluginHost::InitializePlugin(const QString &internalName)
|
||||
{
|
||||
auto &plugin = plugins[internalName];
|
||||
if (plugin.isLoaded)
|
||||
{
|
||||
LOG(MODULE_PLUGINHOST, "The plugin: \"" + internalName + "\" has already been loaded.")
|
||||
return true;
|
||||
}
|
||||
if (!GlobalConfig.pluginConfig.pluginStates.contains(internalName))
|
||||
{
|
||||
// If not contained, default to disable.
|
||||
GlobalConfig.pluginConfig.pluginStates[internalName] = false;
|
||||
}
|
||||
// If the plugin is disabled
|
||||
if (!GlobalConfig.pluginConfig.pluginStates[internalName])
|
||||
{
|
||||
LOG(MODULE_PLUGINHOST, "Cannot load a plugin that's been disabled.")
|
||||
return false;
|
||||
}
|
||||
|
||||
auto conf = JsonFromString(StringFromFile(QV2RAY_PLUGIN_SETTINGS_DIR + internalName + ".conf"));
|
||||
plugins[internalName].pluginInterface->Initialize(QV2RAY_PLUGIN_SETTINGS_DIR + internalName + "/", conf);
|
||||
plugins[internalName].isLoaded = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
QvPluginHost::~QvPluginHost()
|
||||
{
|
||||
for (auto name : plugins.keys())
|
||||
{
|
||||
if (plugins[name].isLoaded)
|
||||
{
|
||||
LOG(MODULE_PLUGINHOST, "Saving plugin settings for: \"" + name + "\"")
|
||||
auto &conf = plugins[name].pluginInterface->GetSettngs();
|
||||
StringToFile(JsonToString(conf), QV2RAY_PLUGIN_SETTINGS_DIR + name + ".conf");
|
||||
}
|
||||
}
|
||||
ClearPlugins();
|
||||
}
|
||||
|
||||
// ================== BEGIN SEND EVENTS ==================
|
||||
void QvPluginHost::Send_ConnectionStatsEvent(const Events::ConnectionStats::EventObject &object)
|
||||
{
|
||||
for (auto &plugin : plugins)
|
||||
{
|
||||
if (plugin.isLoaded && plugin.metadata.Capabilities.contains(CAPABILITY_STATS))
|
||||
{
|
||||
plugin.pluginInterface->GetEventHandler()->ProcessEvent_ConnectionStats(object);
|
||||
}
|
||||
}
|
||||
}
|
||||
void QvPluginHost::Send_ConnectivityEvent(const Events::Connectivity::EventObject &object)
|
||||
{
|
||||
for (auto &plugin : plugins)
|
||||
{
|
||||
if (plugin.isLoaded && plugin.metadata.Capabilities.contains(CAPABILITY_CONNECTIVITY))
|
||||
{
|
||||
plugin.pluginInterface->GetEventHandler()->ProcessEvent_Connectivity(object);
|
||||
}
|
||||
}
|
||||
}
|
||||
void QvPluginHost::Send_ConnectionEvent(const Events::ConnectionEntry::EventObject &object)
|
||||
{
|
||||
for (auto &plugin : plugins)
|
||||
{
|
||||
if (plugin.isLoaded && plugin.metadata.Capabilities.contains(CAPABILITY_CONNECTION_ENTRY))
|
||||
{
|
||||
plugin.pluginInterface->GetEventHandler()->ProcessEvent_ConnectionEntry(object);
|
||||
}
|
||||
}
|
||||
}
|
||||
void QvPluginHost::Send_SystemProxyEvent(const Events::SystemProxy::EventObject &object)
|
||||
{
|
||||
for (auto &plugin : plugins)
|
||||
{
|
||||
if (plugin.isLoaded && plugin.metadata.Capabilities.contains(CAPABILITY_SYSTEM_PROXY))
|
||||
{
|
||||
plugin.pluginInterface->GetEventHandler()->ProcessEvent_SystemProxy(object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const QList<QvPluginEditor *> QvPluginHost::GetOutboundEditorWidgets() const
|
||||
{
|
||||
QList<QvPluginEditor *> data;
|
||||
for (const auto &plugin : plugins)
|
||||
{
|
||||
if (!plugin.isLoaded)
|
||||
continue;
|
||||
auto editor = plugin.pluginInterface->GetEditorWidget(UI_TYPE::UI_TYPE_OUTBOUND_EDITOR);
|
||||
if (editor)
|
||||
{
|
||||
data.append(editor.release());
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
const QMultiHash<QString, QPair<QString, QJsonObject>> QvPluginHost::TryDeserializeShareLink(const QString &sharelink, //
|
||||
QString *prefix, //
|
||||
QString *errMessage, //
|
||||
QString *newGroupName, //
|
||||
bool *status) const
|
||||
{
|
||||
Q_UNUSED(newGroupName)
|
||||
QMultiHash<QString, QPair<QString, QJsonObject>> data;
|
||||
*status = true;
|
||||
for (const auto &plugin : plugins)
|
||||
{
|
||||
if (plugin.isLoaded && plugin.metadata.SpecialPluginType.contains(SPECIAL_TYPE_SERIALIZOR))
|
||||
{
|
||||
auto serializer = plugin.pluginInterface->GetSerializer();
|
||||
bool thisPluginCanHandle = false;
|
||||
for (const auto &prefix : serializer->ShareLinkPrefixes())
|
||||
{
|
||||
thisPluginCanHandle = thisPluginCanHandle || sharelink.startsWith(prefix);
|
||||
}
|
||||
if (thisPluginCanHandle)
|
||||
{
|
||||
auto [protocol, outboundSettings] = serializer->DeserializeOutbound(sharelink, prefix, errMessage);
|
||||
*status = *status && errMessage->isEmpty();
|
||||
data.insert(*prefix, { protocol, outboundSettings });
|
||||
}
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
const QvPluginOutboundInfoObject QvPluginHost::TryGetOutboundInfo(const QString &protocol, const QJsonObject &o, bool *status) const
|
||||
{
|
||||
*status = false;
|
||||
for (const auto &plugin : plugins)
|
||||
{
|
||||
if (plugin.isLoaded && plugin.metadata.SpecialPluginType.contains(SPECIAL_TYPE_SERIALIZOR))
|
||||
{
|
||||
auto serializer = plugin.pluginInterface->GetSerializer();
|
||||
if (serializer->OutboundProtocols().contains(protocol))
|
||||
{
|
||||
auto info = serializer->GetOutboundInfo(protocol, o);
|
||||
*status = true;
|
||||
return info;
|
||||
}
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
const QString QvPluginHost::TrySerializeShareLink(const QString &protocol, //
|
||||
const QJsonObject &outboundSettings, //
|
||||
const QString &alias, //
|
||||
const QString &groupName, //
|
||||
bool *status) const
|
||||
{
|
||||
*status = false;
|
||||
for (const auto &plugin : plugins)
|
||||
{
|
||||
if (plugin.isLoaded && plugin.metadata.SpecialPluginType.contains(SPECIAL_TYPE_SERIALIZOR))
|
||||
{
|
||||
auto serializer = plugin.pluginInterface->GetSerializer();
|
||||
if (serializer->OutboundProtocols().contains(protocol))
|
||||
{
|
||||
auto link = serializer->SerializeOutbound(protocol, alias, groupName, outboundSettings);
|
||||
*status = true;
|
||||
return link;
|
||||
}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
const QMap<QString, std::shared_ptr<QvPluginKernel>> QvPluginHost::GetPluginKernels() const
|
||||
{
|
||||
QMap<QString, std::shared_ptr<QvPluginKernel>> 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())
|
||||
{
|
||||
kernels.insert(cap.protocol, kern);
|
||||
}
|
||||
}
|
||||
}
|
||||
return kernels;
|
||||
}
|
||||
|
||||
const QString GetPluginTypeString(const SPECIAL_TYPE_FLAGS &types)
|
||||
{
|
||||
QStringList typesList;
|
||||
if (types.isEmpty())
|
||||
{
|
||||
typesList << QObject::tr("Normal Plugin");
|
||||
}
|
||||
for (auto type : types)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case SPECIAL_TYPE_KERNEL: typesList << QObject::tr("Kernel"); break;
|
||||
case SPECIAL_TYPE_SERIALIZOR: typesList << QObject::tr("Share Link Parser"); break;
|
||||
default: typesList << QObject::tr("Unknown type."); break;
|
||||
}
|
||||
}
|
||||
return typesList.join(NEWLINE);
|
||||
}
|
||||
|
||||
const QString GetPluginCapabilityString(const CAPABILITY_FLAGS &caps)
|
||||
{
|
||||
QStringList capsString;
|
||||
if (caps.isEmpty())
|
||||
{
|
||||
capsString << QObject::tr("No Capability");
|
||||
}
|
||||
for (auto cap : caps)
|
||||
{
|
||||
switch (cap)
|
||||
{
|
||||
case CAPABILITY_CONNECTIVITY: capsString << QObject::tr("Connection State Change"); break;
|
||||
case CAPABILITY_CONNECTION_ENTRY: capsString << QObject::tr("Connection Change"); break;
|
||||
case CAPABILITY_STATS: capsString << QObject::tr("Statistics Event"); break;
|
||||
case CAPABILITY_SYSTEM_PROXY: capsString << QObject::tr("System Proxy Event"); break;
|
||||
default: capsString << QObject::tr("Unknown"); break;
|
||||
}
|
||||
}
|
||||
return capsString.join(NEWLINE);
|
||||
}
|
||||
} // namespace Qv2ray::components::plugins
|
99
src/components/plugins/QvPluginHost.hpp
Normal file
@ -0,0 +1,99 @@
|
||||
#pragma once
|
||||
#include "components/plugins/interface/QvPluginInterface.hpp"
|
||||
|
||||
#include <QMap>
|
||||
#include <QObject>
|
||||
#include <memory>
|
||||
|
||||
class QPluginLoader;
|
||||
|
||||
using namespace Qv2rayPlugin;
|
||||
namespace Qv2ray::components::plugins
|
||||
{
|
||||
struct QvPluginInfo
|
||||
{
|
||||
bool isLoaded = false;
|
||||
QString libraryPath;
|
||||
QvPluginMetadata metadata;
|
||||
QPluginLoader *pluginLoader;
|
||||
Qv2rayInterface *pluginInterface;
|
||||
};
|
||||
|
||||
class QvPluginHost : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit QvPluginHost(QObject *parent = nullptr);
|
||||
~QvPluginHost();
|
||||
//
|
||||
bool GetPluginEnableState(const QString &internalName) const;
|
||||
void SetPluginEnableState(const QString &internalName, bool isEnabled);
|
||||
//
|
||||
bool inline GetPluginLoadState(const QString &internalName) const
|
||||
{
|
||||
return plugins.value(internalName).isLoaded;
|
||||
}
|
||||
const inline QString GetPluginLibraryPath(const QString &internalName) const
|
||||
{
|
||||
return plugins.value(internalName).libraryPath;
|
||||
}
|
||||
const inline QStringList AvailablePlugins() const
|
||||
{
|
||||
return plugins.keys();
|
||||
}
|
||||
inline std::unique_ptr<QWidget> GetPluginSettingsWidget(const QString &internalName) const
|
||||
{
|
||||
return plugins.value(internalName).pluginInterface->GetSettingsWidget();
|
||||
}
|
||||
const inline QJsonObject GetPluginSettings(const QString &internalName) const
|
||||
{
|
||||
return plugins.value(internalName).pluginInterface->GetSettngs();
|
||||
}
|
||||
bool inline SetPluginSettings(const QString &internalName, const QJsonObject &settings) const
|
||||
{
|
||||
return plugins.value(internalName).pluginInterface->UpdateSettings(settings);
|
||||
}
|
||||
const inline QvPluginMetadata GetPluginMetadata(const QString &internalName) const
|
||||
{
|
||||
return plugins.value(internalName).metadata;
|
||||
}
|
||||
const QMap<QString, std::shared_ptr<QvPluginKernel>> GetPluginKernels() const;
|
||||
//
|
||||
const QMultiHash<QString, QPair<QString, QJsonObject>> TryDeserializeShareLink(const QString &sharelink, //
|
||||
QString *prefix, //
|
||||
QString *errMessage, //
|
||||
QString *newGroupName, //
|
||||
bool *status) const;
|
||||
//
|
||||
const QString TrySerializeShareLink(const QString &protocol, //
|
||||
const QJsonObject &outboundSettings, //
|
||||
const QString &alias, //
|
||||
const QString &groupName, //
|
||||
bool *status) const;
|
||||
const QvPluginOutboundInfoObject TryGetOutboundInfo(const QString &protocol, const QJsonObject &o, bool *status) const;
|
||||
const QList<QvPluginEditor *> GetOutboundEditorWidgets() const;
|
||||
//
|
||||
void Send_ConnectionStatsEvent(const Events::ConnectionStats::EventObject &object);
|
||||
void Send_ConnectivityEvent(const Events::Connectivity::EventObject &object);
|
||||
void Send_ConnectionEvent(const Events::ConnectionEntry::EventObject &object);
|
||||
void Send_SystemProxyEvent(const Events::SystemProxy::EventObject &object);
|
||||
//
|
||||
private slots:
|
||||
void QvPluginLog(const QString &log);
|
||||
void QvPluginMessageBox(const QString &message);
|
||||
|
||||
private:
|
||||
void InitializePluginHost();
|
||||
int RefreshPluginList();
|
||||
bool InitializePlugin(const QString &internalName);
|
||||
void ClearPlugins();
|
||||
// Internal name, plugin info
|
||||
QHash<QString, QvPluginInfo> plugins;
|
||||
};
|
||||
|
||||
const QString GetPluginTypeString(const SPECIAL_TYPE_FLAGS &types);
|
||||
const QString GetPluginCapabilityString(const CAPABILITY_FLAGS &caps);
|
||||
inline ::Qv2ray::components::plugins::QvPluginHost *PluginHost = nullptr;
|
||||
} // namespace Qv2ray::components::plugins
|
||||
|
||||
using namespace Qv2ray::components::plugins;
|
1
src/components/plugins/interface
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit d37c7ea9459956dc459610e98b821d4a790cb6e8
|
@ -89,15 +89,15 @@ namespace Qv2ray::components::plugins
|
||||
case 104:
|
||||
{
|
||||
// Current Connection Name
|
||||
CL.Message = GetDisplayName(ConnectionManager->CurrentConnection());
|
||||
CL.Message = GetDisplayName(KernelInstance->CurrentConnection());
|
||||
break;
|
||||
}
|
||||
|
||||
case 105:
|
||||
{
|
||||
// Current Connection Status
|
||||
CL.Message = ConnectionManager->CurrentConnection() == NullConnectionId ? QObject::tr("Not connected") :
|
||||
QObject::tr("Connected");
|
||||
CL.Message = KernelInstance->CurrentConnection() == NullConnectionId ? QObject::tr("Not connected") :
|
||||
QObject::tr("Connected");
|
||||
break;
|
||||
}
|
||||
|
||||
@ -132,14 +132,14 @@ namespace Qv2ray::components::plugins
|
||||
case 301:
|
||||
{
|
||||
// Total Upload
|
||||
CL.Message = FormatBytes(get<0>(GetConnectionUsageAmount(ConnectionManager->CurrentConnection())));
|
||||
CL.Message = FormatBytes(get<0>(GetConnectionUsageAmount(KernelInstance->CurrentConnection())));
|
||||
break;
|
||||
}
|
||||
|
||||
case 302:
|
||||
{
|
||||
// Total download
|
||||
CL.Message = FormatBytes(get<1>(GetConnectionUsageAmount(ConnectionManager->CurrentConnection())));
|
||||
CL.Message = FormatBytes(get<1>(GetConnectionUsageAmount(KernelInstance->CurrentConnection())));
|
||||
break;
|
||||
}
|
||||
|
||||
@ -160,7 +160,7 @@ namespace Qv2ray::components::plugins
|
||||
case 305:
|
||||
{
|
||||
// Connection latency
|
||||
CL.Message = QSTRN(GetConnectionLatency(ConnectionManager->CurrentConnection())) + " ms";
|
||||
CL.Message = QSTRN(GetConnectionLatency(KernelInstance->CurrentConnection())) + " ms";
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -43,7 +43,7 @@ namespace Qv2ray::components::plugins::Toolbar
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG(MODULE_PLUGIN, "Closing a broken socket.")
|
||||
LOG(MODULE_PLUGINHOST, "Closing a broken socket.")
|
||||
}
|
||||
}
|
||||
void DataMessageQThread()
|
||||
@ -66,8 +66,8 @@ namespace Qv2ray::components::plugins::Toolbar
|
||||
while (!isExiting)
|
||||
{
|
||||
bool result = server->waitForNewConnection(5000, &timeOut);
|
||||
DEBUG(MODULE_PLUGIN, "Plugin thread listening failed: " + server->errorString())
|
||||
DEBUG(MODULE_PLUGIN, "waitForNewConnection: " + QString(result ? "true" : "false") + ", " + QString(timeOut ? "true" : "false"))
|
||||
DEBUG(MODULE_PLUGINHOST, "Plugin thread listening failed: " + server->errorString())
|
||||
DEBUG(MODULE_PLUGINHOST, "waitForNewConnection: " + QString(result ? "true" : "false") + ", " + QString(timeOut ? "true" : "false"))
|
||||
}
|
||||
|
||||
server->close();
|
||||
@ -85,7 +85,7 @@ namespace Qv2ray::components::plugins::Toolbar
|
||||
|
||||
if (linuxWorkerThread->isRunning())
|
||||
{
|
||||
LOG(MODULE_PLUGIN, "Waiting for linuxWorkerThread to stop.")
|
||||
LOG(MODULE_PLUGINHOST, "Waiting for linuxWorkerThread to stop.")
|
||||
linuxWorkerThread->wait();
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ namespace Qv2ray::components::plugins::Toolbar
|
||||
|
||||
if (hThread == nullptr)
|
||||
{
|
||||
LOG(MODULE_PLUGIN, "CreateThread failed, GLE=" + QSTRN(GetLastError()))
|
||||
LOG(MODULE_PLUGINHOST, "CreateThread failed, GLE=" + QSTRN(GetLastError()))
|
||||
return;
|
||||
}
|
||||
else
|
||||
@ -52,7 +52,7 @@ namespace Qv2ray::components::plugins::Toolbar
|
||||
|
||||
if (hPipe == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
LOG(MODULE_PLUGIN, "CreateNamedPipe failed, GLE=" + QSTRN(GetLastError()))
|
||||
LOG(MODULE_PLUGINHOST, "CreateNamedPipe failed, GLE=" + QSTRN(GetLastError()))
|
||||
return static_cast<DWORD>(-1);
|
||||
}
|
||||
|
||||
@ -60,12 +60,12 @@ namespace Qv2ray::components::plugins::Toolbar
|
||||
|
||||
if (fConnected)
|
||||
{
|
||||
LOG(MODULE_PLUGIN, "Client connected, creating a processing thread")
|
||||
LOG(MODULE_PLUGINHOST, "Client connected, creating a processing thread")
|
||||
ThreadHandle = CreateThread(nullptr, 0, InstanceThread, hPipe, 0, &dwThreadId);
|
||||
|
||||
if (ThreadHandle == nullptr)
|
||||
{
|
||||
LOG(MODULE_PLUGIN, "CreateThread failed, GLE=" + QSTRN(GetLastError()))
|
||||
LOG(MODULE_PLUGINHOST, "CreateThread failed, GLE=" + QSTRN(GetLastError()))
|
||||
return static_cast<DWORD>(-1);
|
||||
}
|
||||
else
|
||||
@ -93,11 +93,11 @@ namespace Qv2ray::components::plugins::Toolbar
|
||||
{
|
||||
if (GetLastError() == ERROR_BROKEN_PIPE)
|
||||
{
|
||||
LOG(MODULE_PLUGIN, "InstanceThread: client disconnected, GLE=" + QSTRN(GetLastError()))
|
||||
LOG(MODULE_PLUGINHOST, "InstanceThread: client disconnected, GLE=" + QSTRN(GetLastError()))
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(MODULE_PLUGIN, "InstanceThread ReadFile failed, GLE=" + QSTRN(GetLastError()))
|
||||
LOG(MODULE_PLUGINHOST, "InstanceThread ReadFile failed, GLE=" + QSTRN(GetLastError()))
|
||||
}
|
||||
|
||||
break;
|
||||
@ -120,7 +120,7 @@ namespace Qv2ray::components::plugins::Toolbar
|
||||
|
||||
if (!fSuccess || cbReplyBytes != cbWritten)
|
||||
{
|
||||
LOG(MODULE_PLUGIN, "InstanceThread WriteFile failed, GLE=" + QSTRN(GetLastError()))
|
||||
LOG(MODULE_PLUGINHOST, "InstanceThread WriteFile failed, GLE=" + QSTRN(GetLastError()))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "QvProxyConfigurator.hpp"
|
||||
|
||||
#include "common/QvHelpers.hpp"
|
||||
#include "components/plugins/QvPluginHost.hpp"
|
||||
#ifdef Q_OS_WIN
|
||||
#include <WinInet.h>
|
||||
#include <Windows.h>
|
||||
@ -188,46 +189,30 @@ namespace Qv2ray::components::proxy
|
||||
}
|
||||
#endif
|
||||
|
||||
void SetSystemProxy(const QString &address, int httpPort, int socksPort, bool usePAC)
|
||||
void SetSystemProxy(const QString &address, int httpPort, int socksPort)
|
||||
{
|
||||
LOG(MODULE_PROXY, "Setting up System Proxy")
|
||||
bool hasHTTP = (httpPort != 0);
|
||||
bool hasSOCKS = (socksPort != 0);
|
||||
|
||||
if (!(hasHTTP || hasSOCKS || usePAC))
|
||||
if (!(hasHTTP || hasSOCKS))
|
||||
{
|
||||
LOG(MODULE_PROXY, "Nothing?")
|
||||
return;
|
||||
}
|
||||
|
||||
if (usePAC)
|
||||
if (hasHTTP)
|
||||
{
|
||||
LOG(MODULE_PROXY, "Qv2ray will set system proxy to use PAC file")
|
||||
LOG(MODULE_PROXY, "Qv2ray will set system proxy to use HTTP")
|
||||
}
|
||||
else
|
||||
{
|
||||
if (hasHTTP)
|
||||
{
|
||||
LOG(MODULE_PROXY, "Qv2ray will set system proxy to use HTTP")
|
||||
}
|
||||
|
||||
if (hasSOCKS)
|
||||
{
|
||||
LOG(MODULE_PROXY, "Qv2ray will set system proxy to use SOCKS")
|
||||
}
|
||||
if (hasSOCKS)
|
||||
{
|
||||
LOG(MODULE_PROXY, "Qv2ray will set system proxy to use SOCKS")
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
QString __a;
|
||||
|
||||
if (usePAC)
|
||||
{
|
||||
__a = address;
|
||||
}
|
||||
else
|
||||
{
|
||||
__a = (hasHTTP ? "http://" : "socks5://") + address + ":" + QSTRN(httpPort);
|
||||
}
|
||||
QString __a = (hasHTTP ? "http://" : "socks5://") + address + ":" + QSTRN(hasHTTP ? httpPort : socksPort);
|
||||
|
||||
LOG(MODULE_PROXY, "Windows proxy string: " + __a)
|
||||
auto proxyStrW = new WCHAR[__a.length() + 1];
|
||||
@ -235,7 +220,7 @@ namespace Qv2ray::components::proxy
|
||||
//
|
||||
__QueryProxyOptions();
|
||||
|
||||
if (!__SetProxyOptions(proxyStrW, usePAC))
|
||||
if (!__SetProxyOptions(proxyStrW, false))
|
||||
{
|
||||
LOG(MODULE_PROXY, "Failed to set proxy.")
|
||||
}
|
||||
@ -243,66 +228,50 @@ namespace Qv2ray::components::proxy
|
||||
__QueryProxyOptions();
|
||||
#elif defined(Q_OS_LINUX)
|
||||
QStringList actions;
|
||||
auto proxyMode = usePAC ? "auto" : "manual";
|
||||
auto proxyMode = "manual";
|
||||
actions << QString("gsettings set org.gnome.system.proxy mode '%1'").arg(proxyMode);
|
||||
bool isKDE = qEnvironmentVariable("XDG_SESSION_DESKTOP") == "KDE";
|
||||
if (isKDE)
|
||||
{
|
||||
LOG(MODULE_PROXY, "KDE detected")
|
||||
}
|
||||
//
|
||||
if (usePAC)
|
||||
if (isKDE)
|
||||
{
|
||||
actions << QString("gsettings set org.gnome.system.proxy autoconfig-url '%1'").arg(address);
|
||||
actions << QString("kwriteconfig5 --file " + QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) +
|
||||
"/kioslaverc --group \"Proxy Settings\" --key ProxyType 1");
|
||||
}
|
||||
if (hasHTTP)
|
||||
{
|
||||
actions << QString("gsettings set org.gnome.system.proxy.http host '%1'").arg(address);
|
||||
actions << QString("gsettings set org.gnome.system.proxy.http port %1").arg(httpPort);
|
||||
//
|
||||
actions << QString("gsettings set org.gnome.system.proxy.https host '%1'").arg(address);
|
||||
actions << QString("gsettings set org.gnome.system.proxy.https port %1").arg(httpPort);
|
||||
if (isKDE)
|
||||
{
|
||||
actions << QString("kwriteconfig5 --file " + QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) +
|
||||
"/kioslaverc --group \"Proxy Settings\" --key ProxyType 2");
|
||||
|
||||
actions << QString("kwriteconfig5 --file " + QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) +
|
||||
"/kioslaverc --group \"Proxy Settings\" --key \"Proxy Config Script\" " + address);
|
||||
// FTP here should be scheme: ftp://
|
||||
for (auto protocol : { "http", "ftp", "https" })
|
||||
{
|
||||
auto str = QString("kwriteconfig5 --file " + QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) +
|
||||
"/kioslaverc --group \"Proxy Settings\" --key %1Proxy \"http://%2 %3\"")
|
||||
.arg(protocol)
|
||||
.arg(address)
|
||||
.arg(QSTRN(httpPort));
|
||||
actions << str;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
if (hasSOCKS)
|
||||
{
|
||||
actions << QString("gsettings set org.gnome.system.proxy.socks host '%1'").arg(address);
|
||||
actions << QString("gsettings set org.gnome.system.proxy.socks port %1").arg(socksPort);
|
||||
if (isKDE)
|
||||
{
|
||||
actions << QString("kwriteconfig5 --file " + QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) +
|
||||
"/kioslaverc --group \"Proxy Settings\" --key ProxyType 1");
|
||||
}
|
||||
if (hasHTTP)
|
||||
{
|
||||
actions << QString("gsettings set org.gnome.system.proxy.http host '%1'").arg(address);
|
||||
actions << QString("gsettings set org.gnome.system.proxy.http port %1").arg(httpPort);
|
||||
//
|
||||
actions << QString("gsettings set org.gnome.system.proxy.https host '%1'").arg(address);
|
||||
actions << QString("gsettings set org.gnome.system.proxy.https port %1").arg(httpPort);
|
||||
if (isKDE)
|
||||
{
|
||||
// FTP here should be scheme: ftp://
|
||||
for (auto protocol : { "http", "ftp", "https" })
|
||||
{
|
||||
auto str = QString("kwriteconfig5 --file " + QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) +
|
||||
"/kioslaverc --group \"Proxy Settings\" --key %1Proxy \"http://%2 %3\"")
|
||||
.arg(protocol)
|
||||
.arg(address)
|
||||
.arg(QSTRN(httpPort));
|
||||
actions << str;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasSOCKS)
|
||||
{
|
||||
actions << QString("gsettings set org.gnome.system.proxy.socks host '%1'").arg(address);
|
||||
actions << QString("gsettings set org.gnome.system.proxy.socks port %1").arg(socksPort);
|
||||
if (isKDE)
|
||||
{
|
||||
actions << QString("kwriteconfig5 --file " + QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) +
|
||||
"/kioslaverc --group \"Proxy Settings\" --key socksProxy \"socks://%1 %2\"")
|
||||
.arg(address)
|
||||
.arg(QSTRN(socksPort));
|
||||
}
|
||||
actions << QString("kwriteconfig5 --file " + QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) +
|
||||
"/kioslaverc --group \"Proxy Settings\" --key socksProxy \"socks://%1 %2\"")
|
||||
.arg(address)
|
||||
.arg(QSTRN(socksPort));
|
||||
}
|
||||
}
|
||||
|
||||
@ -326,30 +295,31 @@ namespace Qv2ray::components::proxy
|
||||
{
|
||||
LOG(MODULE_PROXY, "Setting proxy for interface: " + service)
|
||||
|
||||
if (usePAC)
|
||||
if (hasHTTP)
|
||||
{
|
||||
QProcess::execute("/usr/sbin/networksetup -setautoproxystate " + service + " on");
|
||||
QProcess::execute("/usr/sbin/networksetup -setautoproxyurl " + service + " " + address);
|
||||
QProcess::execute("/usr/sbin/networksetup -setwebproxystate " + service + " on");
|
||||
QProcess::execute("/usr/sbin/networksetup -setsecurewebproxystate " + service + " on");
|
||||
QProcess::execute("/usr/sbin/networksetup -setwebproxy " + service + " " + address + " " + QSTRN(httpPort));
|
||||
QProcess::execute("/usr/sbin/networksetup -setsecurewebproxy " + service + " " + address + " " + QSTRN(httpPort));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (hasHTTP)
|
||||
{
|
||||
QProcess::execute("/usr/sbin/networksetup -setwebproxystate " + service + " on");
|
||||
QProcess::execute("/usr/sbin/networksetup -setsecurewebproxystate " + service + " on");
|
||||
QProcess::execute("/usr/sbin/networksetup -setwebproxy " + service + " " + address + " " + QSTRN(httpPort));
|
||||
QProcess::execute("/usr/sbin/networksetup -setsecurewebproxy " + service + " " + address + " " + QSTRN(httpPort));
|
||||
}
|
||||
|
||||
if (hasSOCKS)
|
||||
{
|
||||
QProcess::execute("/usr/sbin/networksetup -setsocksfirewallproxystate " + service + " on");
|
||||
QProcess::execute("/usr/sbin/networksetup -setsocksfirewallproxy " + service + " " + address + " " + QSTRN(socksPort));
|
||||
}
|
||||
if (hasSOCKS)
|
||||
{
|
||||
QProcess::execute("/usr/sbin/networksetup -setsocksfirewallproxystate " + service + " on");
|
||||
QProcess::execute("/usr/sbin/networksetup -setsocksfirewallproxy " + service + " " + address + " " + QSTRN(socksPort));
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
//
|
||||
// Trigger plugin events
|
||||
QMap<Events::SystemProxy::SystemProxyType, int> portSettings;
|
||||
if (hasHTTP)
|
||||
portSettings.insert(Events::SystemProxy::SystemProxyType::SystemProxy_HTTP, httpPort);
|
||||
if (hasSOCKS)
|
||||
portSettings.insert(Events::SystemProxy::SystemProxyType::SystemProxy_SOCKS, socksPort);
|
||||
PluginHost->Send_SystemProxyEvent(
|
||||
Events::SystemProxy::EventObject{ portSettings, Events::SystemProxy::SystemProxyStateType::SystemProxyState_SetProxy });
|
||||
}
|
||||
|
||||
void ClearSystemProxy()
|
||||
@ -403,5 +373,9 @@ namespace Qv2ray::components::proxy
|
||||
}
|
||||
|
||||
#endif
|
||||
//
|
||||
// Trigger plugin events
|
||||
PluginHost->Send_SystemProxyEvent(
|
||||
Events::SystemProxy::EventObject{ {}, Events::SystemProxy::SystemProxyStateType::SystemProxyState_ClearProxy });
|
||||
}
|
||||
} // namespace Qv2ray::components::proxy
|
||||
|
@ -5,7 +5,7 @@
|
||||
namespace Qv2ray::components::proxy
|
||||
{
|
||||
void ClearSystemProxy();
|
||||
void SetSystemProxy(const QString &address, int http_port, int socks_port, bool usePAC);
|
||||
void SetSystemProxy(const QString &address, int http_port, int socks_port);
|
||||
} // namespace Qv2ray::components::proxy
|
||||
|
||||
using namespace Qv2ray::components;
|
||||
|
@ -142,9 +142,8 @@ namespace Qv2ray::components::route::presets::v2rayN
|
||||
|
||||
const inline QList<QString> DomainsBlock{ "geosite:category-ads-all" };
|
||||
const inline QList<QString> DomainsProxy{ "geosite:google", "geosite:github", "geosite:netflix", "geosite:steam",
|
||||
"geosite:telegram", "geosite:tumblr", "geosite:speedtest", "geosite:bbc",
|
||||
"domain:gvt1.com", "domain:textnow.com", "domain:twitch.tv", "domain:wikileaks.org",
|
||||
"domain:naver.com" };
|
||||
"geosite:telegram", "geosite:tumblr", "domain:naver.com", "geosite:bbc",
|
||||
"domain:gvt1.com", "domain:textnow.com", "domain:twitch.tv", "domain:wikileaks.org" };
|
||||
|
||||
const inline QList<QString> IPsProxy{
|
||||
"91.108.4.0/22", "91.108.8.0/22", "91.108.12.0/22", "91.108.20.0/22", "91.108.36.0/23",
|
||||
|
@ -19,7 +19,7 @@ namespace Qv2ray::components
|
||||
QvUpdateChecker::QvUpdateChecker(QObject *parent) : QObject(parent)
|
||||
{
|
||||
requestHelper = new QvHttpRequestHelper(this);
|
||||
connect(requestHelper, &QvHttpRequestHelper::httpRequestFinished, this, &QvUpdateChecker::VersionUpdate);
|
||||
connect(requestHelper, &QvHttpRequestHelper::OnRequestFinished, this, &QvUpdateChecker::VersionUpdate);
|
||||
}
|
||||
QvUpdateChecker::~QvUpdateChecker()
|
||||
{
|
||||
@ -29,7 +29,7 @@ namespace Qv2ray::components
|
||||
#ifndef DISABLE_AUTO_UPDATE
|
||||
auto updateChannel = GlobalConfig.updateConfig.updateChannel;
|
||||
LOG(MODULE_NETWORK, "Start checking update for channel ID: " + QSTRN(updateChannel))
|
||||
requestHelper->get(UpdateChannelLink[updateChannel]);
|
||||
requestHelper->AsyncGet(UpdateChannelLink[updateChannel]);
|
||||
#endif
|
||||
}
|
||||
void QvUpdateChecker::VersionUpdate(QByteArray &data)
|
||||
|
@ -56,7 +56,11 @@ namespace Qv2ray::core
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
bool status;
|
||||
auto info = PluginHost->TryGetOutboundInfo(*protocol, out["settings"].toObject(), &status);
|
||||
*host = info.hostName;
|
||||
*port = info.port;
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
@ -159,4 +163,23 @@ namespace Qv2ray::core
|
||||
return ConnectionManager->GetConnectionMetaObject(id).groupId;
|
||||
}
|
||||
|
||||
const QMap<QString, int> GetConfigInboundPorts(const CONFIGROOT &root)
|
||||
{
|
||||
if (!root.contains("inbounds"))
|
||||
{
|
||||
return {};
|
||||
}
|
||||
QMap<QString, int> inboundPorts;
|
||||
for (const auto &inboundVal : root["inbounds"].toArray())
|
||||
{
|
||||
const auto &inbound = inboundVal.toObject();
|
||||
inboundPorts.insert(inbound["protocol"].toString(), inbound["port"].toInt());
|
||||
}
|
||||
return inboundPorts;
|
||||
}
|
||||
|
||||
const QMap<QString, int> GetConfigInboundPorts(const ConnectionId &id)
|
||||
{
|
||||
return GetConfigInboundPorts(ConnectionManager->GetConnectionRoot(id));
|
||||
}
|
||||
} // namespace Qv2ray::core
|
||||
|
@ -43,9 +43,11 @@ namespace Qv2ray::core
|
||||
const QString GetDisplayName(const ConnectionId &id, int limit = -1);
|
||||
const QString GetDisplayName(const GroupId &id, int limit = -1);
|
||||
//
|
||||
|
||||
const GroupId GetConnectionGroupId(const ConnectionId &id);
|
||||
//
|
||||
const QMap<QString, int> GetConfigInboundPorts(const CONFIGROOT &root);
|
||||
const QMap<QString, int> GetConfigInboundPorts(const ConnectionId &id);
|
||||
//
|
||||
} // namespace Qv2ray::core
|
||||
|
||||
using namespace Qv2ray::core;
|
||||
|
@ -308,7 +308,7 @@ namespace Qv2ray::core::connection
|
||||
if (!root.contains("inbounds") || root.value("inbounds").toArray().empty())
|
||||
{
|
||||
INBOUNDS inboundsList;
|
||||
|
||||
QJsonObject sniffingObject{ { "enabled", false } };
|
||||
// HTTP Inbound
|
||||
if (GlobalConfig.inboundConfig.useHTTP)
|
||||
{
|
||||
@ -317,6 +317,7 @@ namespace Qv2ray::core::connection
|
||||
httpInBoundObject.insert("port", GlobalConfig.inboundConfig.http_port);
|
||||
httpInBoundObject.insert("protocol", "http");
|
||||
httpInBoundObject.insert("tag", "http_IN");
|
||||
httpInBoundObject.insert("sniffing", sniffingObject);
|
||||
|
||||
if (GlobalConfig.inboundConfig.http_useAuth)
|
||||
{
|
||||
@ -335,6 +336,7 @@ namespace Qv2ray::core::connection
|
||||
socksInBoundObject.insert("port", GlobalConfig.inboundConfig.socks_port);
|
||||
socksInBoundObject.insert("protocol", "socks");
|
||||
socksInBoundObject.insert("tag", "socks_IN");
|
||||
socksInBoundObject.insert("sniffing", sniffingObject);
|
||||
auto socksInSettings = GenerateSocksIN(GlobalConfig.inboundConfig.socks_useAuth ? "password" : "noauth",
|
||||
QList<AccountObject>() << GlobalConfig.inboundConfig.socksAccount,
|
||||
GlobalConfig.inboundConfig.socksUDP, GlobalConfig.inboundConfig.socksLocalIP);
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include "Generation.hpp"
|
||||
#include "common/QvHelpers.hpp"
|
||||
#include "components/plugins/QvPluginHost.hpp"
|
||||
#include "core/CoreUtils.hpp"
|
||||
#include "core/handler/ConfigHandler.hpp"
|
||||
|
||||
@ -11,29 +12,61 @@ namespace Qv2ray::core::connection
|
||||
{
|
||||
QMultiHash<QString, CONFIGROOT> ConvertConfigFromString(const QString &link, QString *prefix, QString *errMessage, QString *newGroupName)
|
||||
{
|
||||
QMultiHash<QString, CONFIGROOT> config;
|
||||
QMultiHash<QString, CONFIGROOT> connectionConf;
|
||||
if (link.startsWith("vmess://"))
|
||||
{
|
||||
auto conf = ConvertConfigFromVMessString(link, prefix, errMessage);
|
||||
config.insert(*prefix, conf);
|
||||
//
|
||||
if (GlobalConfig.advancedConfig.setAllowInsecureCiphers || GlobalConfig.advancedConfig.setAllowInsecure)
|
||||
{
|
||||
auto outbound = conf["outbounds"].toArray().first().toObject();
|
||||
auto streamSettings = outbound["streamSettings"].toObject();
|
||||
auto tlsSettings = streamSettings["tlsSettings"].toObject();
|
||||
tlsSettings["allowInsecure"] = GlobalConfig.advancedConfig.setAllowInsecure;
|
||||
tlsSettings["allowInsecureCiphers"] = GlobalConfig.advancedConfig.setAllowInsecureCiphers;
|
||||
streamSettings["tlsSettings"] = tlsSettings;
|
||||
outbound["streamSettings"] = streamSettings;
|
||||
//
|
||||
auto outbounds = conf["outbounds"].toArray();
|
||||
outbounds[0] = outbound;
|
||||
conf["outbounds"] = outbounds;
|
||||
}
|
||||
//
|
||||
connectionConf.insert(*prefix, conf);
|
||||
}
|
||||
else if (link.startsWith("ss://"))
|
||||
{
|
||||
auto conf = ConvertConfigFromSSString(link, prefix, errMessage);
|
||||
config.insert(*prefix, conf);
|
||||
connectionConf.insert(*prefix, conf);
|
||||
}
|
||||
else if (link.startsWith("ssd://"))
|
||||
{
|
||||
QStringList errMessageList;
|
||||
config = ConvertConfigFromSSDString(link, newGroupName, &errMessageList);
|
||||
connectionConf = ConvertConfigFromSSDString(link, newGroupName, &errMessageList);
|
||||
*errMessage = errMessageList.join(NEWLINE);
|
||||
}
|
||||
else
|
||||
{
|
||||
*errMessage = QObject::tr("Unsupported share link format.");
|
||||
bool ok = false;
|
||||
auto configs = PluginHost->TryDeserializeShareLink(link, prefix, errMessage, newGroupName, &ok);
|
||||
for (const auto &key : configs.keys())
|
||||
{
|
||||
auto vals = configs.values(key);
|
||||
for (const auto &val : vals)
|
||||
{
|
||||
CONFIGROOT root;
|
||||
auto outbound = GenerateOutboundEntry(val.first, OUTBOUNDSETTING(val.second), {});
|
||||
root.insert("outbounds", QJsonArray{ outbound });
|
||||
connectionConf.insert(key, root);
|
||||
}
|
||||
}
|
||||
if (!ok)
|
||||
{
|
||||
*errMessage = QObject::tr("Unsupported share link format.");
|
||||
}
|
||||
}
|
||||
|
||||
return config;
|
||||
return connectionConf;
|
||||
}
|
||||
|
||||
const QString ConvertConfigToString(const ConnectionId &id, bool isSip002)
|
||||
@ -45,51 +78,51 @@ namespace Qv2ray::core::connection
|
||||
return QV2RAY_SERIALIZATION_COMPLEX_CONFIG_PLACEHOLDER;
|
||||
}
|
||||
auto server = ConnectionManager->GetConnectionRoot(id);
|
||||
return ConvertConfigToString(alias, server, isSip002);
|
||||
return ConvertConfigToString(alias, GetDisplayName(GetConnectionGroupId(id)), server, isSip002);
|
||||
}
|
||||
|
||||
const QString ConvertConfigToString(const QString &alias, const CONFIGROOT &server, bool isSip002)
|
||||
const QString ConvertConfigToString(const QString &alias, const QString &groupName, const CONFIGROOT &server, bool isSip002)
|
||||
{
|
||||
OUTBOUND outbound = OUTBOUND(server["outbounds"].toArray().first().toObject());
|
||||
auto type = outbound["protocol"].toString();
|
||||
const auto outbound = OUTBOUND(server["outbounds"].toArray().first().toObject());
|
||||
const auto type = outbound["protocol"].toString();
|
||||
const auto &settings = outbound["settings"].toObject();
|
||||
QString sharelink = "";
|
||||
|
||||
if (type == "vmess")
|
||||
{
|
||||
auto vmessServer =
|
||||
StructFromJsonString<VMessServerObject>(JsonToString(outbound["settings"].toObject()["vnext"].toArray().first().toObject()));
|
||||
auto vmessServer = StructFromJsonString<VMessServerObject>(JsonToString(settings["vnext"].toArray().first().toObject()));
|
||||
auto transport = StructFromJsonString<StreamSettingsObject>(JsonToString(outbound["streamSettings"].toObject()));
|
||||
sharelink = vmess::ConvertConfigToVMessString(transport, vmessServer, alias);
|
||||
}
|
||||
else if (type == "shadowsocks")
|
||||
{
|
||||
auto ssServer = StructFromJsonString<ShadowSocksServerObject>(
|
||||
JsonToString(outbound["settings"].toObject()["servers"].toArray().first().toObject()));
|
||||
auto ssServer = StructFromJsonString<ShadowSocksServerObject>(JsonToString(settings["servers"].toArray().first().toObject()));
|
||||
sharelink = ss::ConvertConfigToSSString(ssServer, alias, isSip002);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!type.isEmpty())
|
||||
if (type.isEmpty())
|
||||
{
|
||||
// DEBUG(MODULE_CONNECTION, "WARNING: Unsupported outbound type: " + type)
|
||||
DEBUG(MODULE_CONNECTION, "WARNING: Empty outbound type.")
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG(MODULE_CONNECTION, "WARNING: Empty outbound type.")
|
||||
bool ok = false;
|
||||
sharelink = PluginHost->TrySerializeShareLink(type, settings, alias, groupName, &ok);
|
||||
Q_UNUSED(ok)
|
||||
}
|
||||
}
|
||||
|
||||
return sharelink;
|
||||
}
|
||||
|
||||
QString DecodeSubscriptionString(QByteArray arr)
|
||||
QString DecodeSubscriptionString(const QByteArray &arr)
|
||||
{
|
||||
// String may start with: vmess:// and ss://
|
||||
// We only process vmess:// here
|
||||
// Some subscription providers may use plain vmess:// saperated by
|
||||
// lines But others may use base64 of above.
|
||||
auto result = QString::fromUtf8(arr).trimmed();
|
||||
return result.startsWith("vmess://") ? result : Base64Decode(result);
|
||||
return result.contains("://") ? result : Base64Decode(result);
|
||||
}
|
||||
} // namespace Serialization
|
||||
} // namespace Qv2ray::core::connection
|
||||
|
@ -15,11 +15,11 @@ namespace Qv2ray::core::connection
|
||||
inline auto QV2RAY_SSD_DEFAULT_NAME_PATTERN = QObject::tr("%1 - %2 (rate %3)");
|
||||
//
|
||||
// General
|
||||
QString DecodeSubscriptionString(QByteArray arr);
|
||||
QString DecodeSubscriptionString(const QByteArray &arr);
|
||||
QMultiHash<QString, CONFIGROOT> ConvertConfigFromString(const QString &link, QString *aliasPrefix, QString *errMessage,
|
||||
QString *newGroupName = nullptr);
|
||||
const QString ConvertConfigToString(const ConnectionId &id, bool isSip002 = false);
|
||||
const QString ConvertConfigToString(const QString &alias, const CONFIGROOT &server, bool isSip002);
|
||||
const QString ConvertConfigToString(const QString &alias, const QString &groupName, const CONFIGROOT &server, bool isSip002);
|
||||
//
|
||||
// VMess URI Protocol
|
||||
namespace vmess
|
||||
|
@ -2,7 +2,6 @@
|
||||
#include "Serialization.hpp"
|
||||
#include "common/QvHelpers.hpp"
|
||||
#include "core/CoreUtils.hpp"
|
||||
#include "core/handler/ConfigHandler.hpp"
|
||||
|
||||
namespace Qv2ray::core::connection
|
||||
{
|
||||
|
@ -83,7 +83,7 @@ namespace Qv2ray::core::connection::Serialization
|
||||
}
|
||||
|
||||
// decode base64
|
||||
const auto ssdURIBody = QStringRef(&uri, 6, uri.length() - 7);
|
||||
const auto ssdURIBody = QStringRef(&uri, 6, uri.length() - 6);
|
||||
const auto decodedJSON = QByteArray::fromBase64(ssdURIBody.toUtf8());
|
||||
|
||||
if (decodedJSON.length() == 0)
|
||||
@ -161,13 +161,12 @@ namespace Qv2ray::core::connection::Serialization
|
||||
{
|
||||
ssObject.port = port;
|
||||
}
|
||||
else if (auto currPort = serverObject["port"].toInt(-1); port >= 0 && port <= 65535)
|
||||
else if (auto currPort = serverObject["port"].toInt(-1); (currPort >= 0 && currPort <= 65535))
|
||||
{
|
||||
ssObject.port = currPort;
|
||||
}
|
||||
else
|
||||
{
|
||||
*logList << QObject::tr("Invalid port encountered. using fallback value.");
|
||||
ssObject.port = port;
|
||||
}
|
||||
|
||||
@ -186,7 +185,6 @@ namespace Qv2ray::core::connection::Serialization
|
||||
}
|
||||
else
|
||||
{
|
||||
*logList << QObject::tr("Invalid name encountered. using fallback value.");
|
||||
nodeName = QString("%1:%2").arg(ssObject.address).arg(ssObject.port);
|
||||
}
|
||||
|
||||
@ -201,7 +199,7 @@ namespace Qv2ray::core::connection::Serialization
|
||||
}
|
||||
else if (!serverObject["ratio"].isUndefined())
|
||||
{
|
||||
*logList << QObject::tr("Invalid ratio encountered. using fallback value.");
|
||||
//*logList << QObject::tr("Invalid ratio encountered. using fallback value.");
|
||||
}
|
||||
|
||||
// format the total name of the node.
|
||||
@ -209,10 +207,8 @@ namespace Qv2ray::core::connection::Serialization
|
||||
// appending to the total list
|
||||
CONFIGROOT root;
|
||||
OUTBOUNDS outbounds;
|
||||
outbounds.append(
|
||||
GenerateOutboundEntry("shadowsocks", GenerateShadowSocksOUT(QList<ShadowSocksServerObject>{ ssObject }), QJsonObject()));
|
||||
outbounds.append(GenerateOutboundEntry("shadowsocks", GenerateShadowSocksOUT({ ssObject }), {}));
|
||||
JADD(outbounds)
|
||||
|
||||
serverList.insertMulti(totalName, root);
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "ConfigHandler.hpp"
|
||||
|
||||
#include "common/QvHelpers.hpp"
|
||||
#include "components/plugins/QvPluginHost.hpp"
|
||||
#include "core/connection/Serialization.hpp"
|
||||
#include "core/settings/SettingsBackend.hpp"
|
||||
|
||||
@ -12,7 +13,7 @@ namespace Qv2ray::core::handlers
|
||||
DEBUG(MODULE_CORE_HANDLER, "ConnectionHandler Constructor.")
|
||||
|
||||
// Do we need to check how many of them are loaded?
|
||||
// Do not use: for (const auto &key : connections)
|
||||
// Do not use: for (const auto &key : connections), why?
|
||||
for (auto i = 0; i < GlobalConfig.connections.count(); i++)
|
||||
{
|
||||
auto const &id = ConnectionId(GlobalConfig.connections.keys().at(i));
|
||||
@ -75,20 +76,20 @@ namespace Qv2ray::core::handlers
|
||||
groups[DefaultGroupId].displayName = tr("Default Group");
|
||||
groups[DefaultGroupId].isSubscription = false;
|
||||
//
|
||||
vCoreInstance = new V2rayKernelInstance();
|
||||
connect(vCoreInstance, &V2rayKernelInstance::OnProcessErrored, this, &QvConfigHandler::OnVCoreCrashed);
|
||||
connect(vCoreInstance, &V2rayKernelInstance::OnNewStatsDataArrived, this, &QvConfigHandler::OnStatsDataArrived);
|
||||
// Directly connected to a signal.
|
||||
connect(vCoreInstance, &V2rayKernelInstance::OnProcessOutputReadyRead, this, &QvConfigHandler::OnVCoreLogAvailable);
|
||||
kernelHandler = new KernelInstanceHandler(this);
|
||||
connect(kernelHandler, &KernelInstanceHandler::OnCrashed, this, &QvConfigHandler::OnKernelCrashed_p);
|
||||
connect(kernelHandler, &KernelInstanceHandler::OnStatsDataAvailable, this, &QvConfigHandler::OnStatsDataArrived_p);
|
||||
connect(kernelHandler, &KernelInstanceHandler::OnKernelLogAvailable, this, &QvConfigHandler::OnKernelLogAvailable);
|
||||
connect(kernelHandler, &KernelInstanceHandler::OnConnected, this, &QvConfigHandler::OnConnected);
|
||||
connect(kernelHandler, &KernelInstanceHandler::OnDisconnected, this, &QvConfigHandler::OnDisconnected);
|
||||
//
|
||||
tcpingHelper = new QvTCPingHelper(5, this);
|
||||
httpHelper = new QvHttpRequestHelper(this);
|
||||
connect(tcpingHelper, &QvTCPingHelper::OnLatencyTestCompleted, this, &QvConfigHandler::OnLatencyDataArrived);
|
||||
connect(tcpingHelper, &QvTCPingHelper::OnLatencyTestCompleted, this, &QvConfigHandler::OnLatencyDataArrived_p);
|
||||
//
|
||||
// Save per 2 minutes.
|
||||
saveTimerId = startTimer(2 * 60 * 1000);
|
||||
// Save per 1 minutes.
|
||||
saveTimerId = startTimer(1 * 60 * 1000);
|
||||
// Do not ping all...
|
||||
// pingAllTimerId = startTimer(5 * 60 * 1000);
|
||||
pingConnectionTimerId = startTimer(60 * 1000);
|
||||
}
|
||||
|
||||
@ -138,9 +139,10 @@ namespace Qv2ray::core::handlers
|
||||
}
|
||||
else if (event->timerId() == pingConnectionTimerId)
|
||||
{
|
||||
if (currentConnectionId != NullConnectionId)
|
||||
auto id = kernelHandler->CurrentConnection();
|
||||
if (id != NullConnectionId && GlobalConfig.advancedConfig.testLatencyPeriodcally)
|
||||
{
|
||||
StartLatencyTest(currentConnectionId);
|
||||
StartLatencyTest(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -207,20 +209,28 @@ namespace Qv2ray::core::handlers
|
||||
|
||||
return NullGroupId;
|
||||
}
|
||||
|
||||
const optional<QString> QvConfigHandler::ClearConnectionUsage(const ConnectionId &id)
|
||||
void QvConfigHandler::ClearGroupUsage(const GroupId &id)
|
||||
{
|
||||
CheckConnectionExistance(id);
|
||||
for (const auto &conn : groups[id].connections)
|
||||
{
|
||||
ClearConnectionUsage(conn);
|
||||
}
|
||||
}
|
||||
void QvConfigHandler::ClearConnectionUsage(const ConnectionId &id)
|
||||
{
|
||||
CheckConnectionExistanceEx(id, nothing);
|
||||
connections[id].upLinkData = 0;
|
||||
connections[id].downLinkData = 0;
|
||||
emit OnStatsAvailable(id, 0, 0, 0, 0);
|
||||
return {};
|
||||
PluginHost->Send_ConnectionStatsEvent({ GetDisplayName(id), 0, 0, 0, 0 });
|
||||
return;
|
||||
}
|
||||
|
||||
const optional<QString> QvConfigHandler::RenameConnection(const ConnectionId &id, const QString &newName)
|
||||
{
|
||||
CheckConnectionExistance(id);
|
||||
OnConnectionRenamed(id, connections[id].displayName, newName);
|
||||
PluginHost->Send_ConnectionEvent({ newName, connections[id].displayName, Events::ConnectionEntry::ConnectionEvent_Renamed });
|
||||
connections[id].displayName = newName;
|
||||
CHSaveConfigData_p();
|
||||
return {};
|
||||
@ -232,6 +242,7 @@ namespace Qv2ray::core::handlers
|
||||
QFile connectionFile((groups[groupId].isSubscription ? QV2RAY_SUBSCRIPTION_DIR : QV2RAY_CONNECTIONS_DIR) + groupId.toString() + "/" +
|
||||
id.toString() + QV2RAY_CONFIG_FILE_EXTENSION);
|
||||
//
|
||||
PluginHost->Send_ConnectionEvent({ connections[id].displayName, "", Events::ConnectionEntry::ConnectionEvent_Deleted });
|
||||
connections.remove(id);
|
||||
groups[groupId].connections.removeAll(id);
|
||||
//
|
||||
@ -279,6 +290,8 @@ namespace Qv2ray::core::handlers
|
||||
groups[newGroupId].connections.append(id);
|
||||
connections[id].groupId = newGroupId;
|
||||
//
|
||||
PluginHost->Send_ConnectionEvent({ connections[id].displayName, "", Events::ConnectionEntry::ConnectionEvent_Updated });
|
||||
//
|
||||
emit OnConnectionGroupChanged(id, oldgid, newGroupId);
|
||||
//
|
||||
return {};
|
||||
@ -308,6 +321,8 @@ namespace Qv2ray::core::handlers
|
||||
QDir(QV2RAY_CONNECTIONS_DIR + id.toString()).removeRecursively();
|
||||
}
|
||||
//
|
||||
PluginHost->Send_ConnectionEvent({ groups[id].displayName, "", Events::ConnectionEntry::ConnectionEvent_Deleted });
|
||||
//
|
||||
groups.remove(id);
|
||||
CHSaveConfigData_p();
|
||||
emit OnGroupDeleted(id, list);
|
||||
@ -321,55 +336,41 @@ namespace Qv2ray::core::handlers
|
||||
const optional<QString> QvConfigHandler::StartConnection(const ConnectionId &id)
|
||||
{
|
||||
CheckConnectionExistance(id);
|
||||
|
||||
if (currentConnectionId != NullConnectionId)
|
||||
{
|
||||
StopConnection();
|
||||
}
|
||||
|
||||
connections[id].lastConnected = system_clock::to_time_t(system_clock::now());
|
||||
CONFIGROOT root = GetConnectionRoot(id);
|
||||
return CHStartConnection_p(id, root);
|
||||
return kernelHandler->StartConnection(id, root);
|
||||
}
|
||||
|
||||
void QvConfigHandler::RestartConnection() // const ConnectionId &id
|
||||
{
|
||||
auto conn = currentConnectionId;
|
||||
if (conn != NullConnectionId)
|
||||
{
|
||||
StopConnection();
|
||||
StartConnection(conn);
|
||||
}
|
||||
kernelHandler->RestartConnection();
|
||||
}
|
||||
|
||||
void QvConfigHandler::StopConnection() // const ConnectionId &id
|
||||
{
|
||||
// Currently just simply stop it.
|
||||
//_UNUSED(id)
|
||||
// if (currentConnectionId == id) {
|
||||
//}
|
||||
CHStopConnection_p();
|
||||
kernelHandler->StopConnection();
|
||||
CHSaveConfigData_p();
|
||||
}
|
||||
|
||||
bool QvConfigHandler::IsConnected(const ConnectionId &id) const
|
||||
{
|
||||
CheckConnectionExistanceEx(id, false);
|
||||
return currentConnectionId == id;
|
||||
return kernelHandler->isConnected(id);
|
||||
}
|
||||
|
||||
void QvConfigHandler::OnKernelCrashed_p(const ConnectionId &id, const QString &errMessage)
|
||||
{
|
||||
LOG(MODULE_CORE_HANDLER, "Kernel crashed: " + errMessage)
|
||||
emit OnDisconnected(id);
|
||||
PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), {}, Events::Connectivity::QvConnecticity_Disconnected });
|
||||
emit OnKernelCrashed(id, errMessage);
|
||||
}
|
||||
|
||||
QvConfigHandler::~QvConfigHandler()
|
||||
{
|
||||
LOG(MODULE_CORE_HANDLER, "Triggering save settings from destructor")
|
||||
CHSaveConfigData_p();
|
||||
|
||||
if (vCoreInstance->KernelStarted)
|
||||
{
|
||||
vCoreInstance->StopConnection();
|
||||
LOG(MODULE_CORE_HANDLER, "Stopped connection from destructor.")
|
||||
}
|
||||
|
||||
delete vCoreInstance;
|
||||
delete kernelHandler;
|
||||
delete httpHelper;
|
||||
CHSaveConfigData_p();
|
||||
}
|
||||
|
||||
const CONFIGROOT QvConfigHandler::GetConnectionRoot(const ConnectionId &id) const
|
||||
@ -378,7 +379,7 @@ namespace Qv2ray::core::handlers
|
||||
return connectionRootCache.value(id);
|
||||
}
|
||||
|
||||
void QvConfigHandler::OnLatencyDataArrived(const QvTCPingResultObject &result)
|
||||
void QvConfigHandler::OnLatencyDataArrived_p(const QvTCPingResultObject &result)
|
||||
{
|
||||
CheckConnectionExistanceEx(result.connectionId, nothing);
|
||||
connections[result.connectionId].latency = result.avg;
|
||||
@ -399,7 +400,8 @@ namespace Qv2ray::core::handlers
|
||||
connectionRootCache[id] = root;
|
||||
//
|
||||
emit OnConnectionModified(id);
|
||||
if (!skipRestart && id == currentConnectionId)
|
||||
PluginHost->Send_ConnectionEvent({ connections[id].displayName, "", Events::ConnectionEntry::ConnectionEvent_Updated });
|
||||
if (!skipRestart && kernelHandler->isConnected(id))
|
||||
{
|
||||
emit RestartConnection();
|
||||
}
|
||||
@ -412,6 +414,7 @@ namespace Qv2ray::core::handlers
|
||||
groups[id].displayName = displayName;
|
||||
groups[id].isSubscription = isSubscription;
|
||||
groups[id].importDate = system_clock::to_time_t(system_clock::now());
|
||||
PluginHost->Send_ConnectionEvent({ displayName, "", Events::ConnectionEntry::ConnectionEvent_Created });
|
||||
emit OnGroupCreated(id, displayName);
|
||||
CHSaveConfigData_p();
|
||||
return id;
|
||||
@ -425,6 +428,7 @@ namespace Qv2ray::core::handlers
|
||||
return tr("Group does not exist");
|
||||
}
|
||||
OnGroupRenamed(id, groups[id].displayName, newName);
|
||||
PluginHost->Send_ConnectionEvent({ newName, groups[id].displayName, Events::ConnectionEntry::ConnectionEvent_Renamed });
|
||||
groups[id].displayName = newName;
|
||||
return {};
|
||||
}
|
||||
@ -468,7 +472,7 @@ namespace Qv2ray::core::handlers
|
||||
return false;
|
||||
}
|
||||
isHttpRequestInProgress = true;
|
||||
auto data = httpHelper->syncget(groups[id].address, useSystemProxy);
|
||||
auto data = httpHelper->Get(groups[id].address, useSystemProxy);
|
||||
isHttpRequestInProgress = false;
|
||||
return CHUpdateSubscription_p(id, data);
|
||||
}
|
||||
@ -583,7 +587,17 @@ namespace Qv2ray::core::handlers
|
||||
return hasErrorOccured;
|
||||
}
|
||||
|
||||
const ConnectionId QvConfigHandler::CreateConnection(const QString &displayName, const GroupId &groupId, const CONFIGROOT &root)
|
||||
void QvConfigHandler::OnStatsDataArrived_p(const ConnectionId &id, const quint64 uploadSpeed, const quint64 downloadSpeed)
|
||||
{
|
||||
connections[id].upLinkData += uploadSpeed;
|
||||
connections[id].downLinkData += downloadSpeed;
|
||||
emit OnStatsAvailable(id, uploadSpeed, downloadSpeed, connections[id].upLinkData, connections[id].downLinkData);
|
||||
PluginHost->Send_ConnectionStatsEvent(
|
||||
{ GetDisplayName(id), uploadSpeed, downloadSpeed, connections[id].upLinkData, connections[id].downLinkData });
|
||||
}
|
||||
|
||||
const ConnectionId QvConfigHandler::CreateConnection(const QString &displayName, const GroupId &groupId, const CONFIGROOT &root,
|
||||
bool skipSaveConfig)
|
||||
{
|
||||
LOG(MODULE_CORE_HANDLER, "Creating new connection: " + displayName)
|
||||
ConnectionId newId(GenerateUuid());
|
||||
@ -592,8 +606,12 @@ namespace Qv2ray::core::handlers
|
||||
connections[newId].importDate = system_clock::to_time_t(system_clock::now());
|
||||
connections[newId].displayName = displayName;
|
||||
emit OnConnectionCreated(newId, displayName);
|
||||
PluginHost->Send_ConnectionEvent({ displayName, "", Events::ConnectionEntry::ConnectionEvent_Created });
|
||||
UpdateConnection(newId, root);
|
||||
CHSaveConfigData_p();
|
||||
if (!skipSaveConfig)
|
||||
{
|
||||
CHSaveConfigData_p();
|
||||
}
|
||||
return newId;
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
#include "core/CoreSafeTypes.hpp"
|
||||
#include "core/CoreUtils.hpp"
|
||||
#include "core/connection/ConnectionIO.hpp"
|
||||
#include "core/kernel/KernelInteractions.hpp"
|
||||
#include "core/handler/KernelInstanceHandler.hpp"
|
||||
|
||||
#define CheckIdExistance(type, id, val) \
|
||||
if (!type.contains(id)) \
|
||||
@ -21,7 +21,6 @@
|
||||
#define CheckConnectionExistance(id) CheckConnectionExistanceEx(id, tr("Connection does not exist"))
|
||||
namespace Qv2ray::core::handlers
|
||||
{
|
||||
//
|
||||
class QvConfigHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -30,11 +29,6 @@ namespace Qv2ray::core::handlers
|
||||
~QvConfigHandler();
|
||||
|
||||
public slots:
|
||||
//
|
||||
inline const ConnectionId CurrentConnection() const
|
||||
{
|
||||
return currentConnectionId;
|
||||
}
|
||||
inline const QList<ConnectionId> Connections() const
|
||||
{
|
||||
return connections.keys();
|
||||
@ -80,11 +74,13 @@ namespace Qv2ray::core::handlers
|
||||
//
|
||||
// Connection Operations.
|
||||
bool UpdateConnection(const ConnectionId &id, const CONFIGROOT &root, bool skipRestart = false);
|
||||
const optional<QString> ClearConnectionUsage(const ConnectionId &id);
|
||||
void ClearGroupUsage(const GroupId &id);
|
||||
void ClearConnectionUsage(const ConnectionId &id);
|
||||
const optional<QString> DeleteConnection(const ConnectionId &id);
|
||||
const optional<QString> RenameConnection(const ConnectionId &id, const QString &newName);
|
||||
const optional<QString> MoveConnectionGroup(const ConnectionId &id, const GroupId &newGroupId);
|
||||
const ConnectionId CreateConnection(const QString &displayName, const GroupId &groupId, const CONFIGROOT &root);
|
||||
const ConnectionId CreateConnection(const QString &displayName, const GroupId &groupId, const CONFIGROOT &root,
|
||||
bool skipSaveConfig = false);
|
||||
//
|
||||
// Get Conncetion Property
|
||||
const CONFIGROOT GetConnectionRoot(const ConnectionId &id) const;
|
||||
@ -107,10 +103,7 @@ namespace Qv2ray::core::handlers
|
||||
const tuple<QString, int64_t, float> GetSubscriptionData(const GroupId &id) const;
|
||||
|
||||
signals:
|
||||
void OnCrashed();
|
||||
void OnConnected(const ConnectionId &id);
|
||||
void OnDisconnected(const ConnectionId &id);
|
||||
void OnVCoreLogAvailable(const ConnectionId &id, const QString &log);
|
||||
void OnKernelLogAvailable(const ConnectionId &id, const QString &log);
|
||||
void OnStatsAvailable(const ConnectionId &id, const quint64 upS, const quint64 downS, const quint64 upD, const quint64 downD);
|
||||
//
|
||||
void OnConnectionCreated(const ConnectionId &id, const QString &displayName);
|
||||
@ -127,20 +120,20 @@ namespace Qv2ray::core::handlers
|
||||
void OnGroupDeleted(const GroupId &id, const QList<ConnectionId> &connections);
|
||||
//
|
||||
void OnSubscriptionUpdateFinished(const GroupId &id);
|
||||
|
||||
void OnConnected(const ConnectionId &id);
|
||||
void OnDisconnected(const ConnectionId &id);
|
||||
void OnKernelCrashed(const ConnectionId &id, const QString &errMessage);
|
||||
//
|
||||
private slots:
|
||||
void OnStatsDataArrived(const ConnectionId &id, const quint64 uploadSpeed, const quint64 downloadSpeed);
|
||||
void OnVCoreCrashed(const ConnectionId &id);
|
||||
void OnLatencyDataArrived(const QvTCPingResultObject &data);
|
||||
void OnKernelCrashed_p(const ConnectionId &id, const QString &errMessage);
|
||||
void OnLatencyDataArrived_p(const QvTCPingResultObject &data);
|
||||
void OnStatsDataArrived_p(const ConnectionId &id, const quint64 uploadSpeed, const quint64 downloadSpeed);
|
||||
|
||||
protected:
|
||||
void timerEvent(QTimerEvent *event) override;
|
||||
|
||||
private:
|
||||
void CHSaveConfigData_p();
|
||||
//
|
||||
optional<QString> CHStartConnection_p(const ConnectionId &id, const CONFIGROOT &root);
|
||||
void CHStopConnection_p();
|
||||
bool CHUpdateSubscription_p(const GroupId &id, const QByteArray &subscriptionData);
|
||||
|
||||
private:
|
||||
@ -155,13 +148,7 @@ namespace Qv2ray::core::handlers
|
||||
QvHttpRequestHelper *httpHelper;
|
||||
bool isHttpRequestInProgress = false;
|
||||
QvTCPingHelper *tcpingHelper;
|
||||
// We only support one cuncurrent connection currently.
|
||||
#ifdef QV2RAY_MULTIPlE_ONNECTION
|
||||
QHash<ConnectionId, *V2rayKernelInstance> kernelInstances;
|
||||
#else
|
||||
ConnectionId currentConnectionId = NullConnectionId;
|
||||
V2rayKernelInstance *vCoreInstance = nullptr;
|
||||
#endif
|
||||
KernelInstanceHandler *kernelHandler;
|
||||
};
|
||||
|
||||
inline ::Qv2ray::core::handlers::QvConfigHandler *ConnectionManager = nullptr;
|
||||
|
277
src/core/handler/KernelInstanceHandler.cpp
Normal file
@ -0,0 +1,277 @@
|
||||
#include "KernelInstanceHandler.hpp"
|
||||
|
||||
#include "core/CoreUtils.hpp"
|
||||
#include "core/connection/Generation.hpp"
|
||||
namespace Qv2ray::core::handlers
|
||||
{
|
||||
#define isConnected (vCoreInstance->KernelStarted || !activeKernels.isEmpty())
|
||||
KernelInstanceHandler::KernelInstanceHandler(QObject *parent) : QObject(parent)
|
||||
{
|
||||
KernelInstance = this;
|
||||
vCoreInstance = new V2rayKernelInstance(this);
|
||||
connect(vCoreInstance, &V2rayKernelInstance::OnNewStatsDataArrived, this, &KernelInstanceHandler::OnStatsDataArrived_p);
|
||||
connect(vCoreInstance, &V2rayKernelInstance::OnProcessOutputReadyRead, this, &KernelInstanceHandler::OnKernelLogAvailable_p);
|
||||
connect(vCoreInstance, &V2rayKernelInstance::OnProcessErrored, this, &KernelInstanceHandler::OnKernelCrashed_p);
|
||||
//
|
||||
kernels = PluginHost->GetPluginKernels();
|
||||
for (const auto &kernel : kernels)
|
||||
{
|
||||
connect(kernel.get(), &QvPluginKernel::OnKernelCrashed, this, &KernelInstanceHandler::OnKernelCrashed_p);
|
||||
connect(kernel.get(), &QvPluginKernel::OnKernelLogAvaliable, this, &KernelInstanceHandler::OnKernelLogAvailable_p);
|
||||
}
|
||||
}
|
||||
|
||||
KernelInstanceHandler::~KernelInstanceHandler()
|
||||
{
|
||||
}
|
||||
|
||||
std::optional<QString> KernelInstanceHandler::StartConnection(const ConnectionId &id, const CONFIGROOT &root)
|
||||
{
|
||||
if (isConnected)
|
||||
{
|
||||
StopConnection();
|
||||
}
|
||||
activeKernels.clear();
|
||||
this->root = root;
|
||||
bool isComplex = IsComplexConfig(root);
|
||||
auto fullConfig = GenerateRuntimeConfig(root);
|
||||
inboundPorts = GetConfigInboundPorts(fullConfig);
|
||||
PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), inboundPorts, Events::Connectivity::QvConnecticity_Connecting });
|
||||
QList<std::tuple<QString, int, QString>> inboundInfo;
|
||||
for (const auto &inbound_v : fullConfig["inbounds"].toArray())
|
||||
{
|
||||
const auto &inbound = inbound_v.toObject();
|
||||
inboundInfo.push_back({ inbound["protocol"].toString(), inbound["port"].toInt(), inbound["tag"].toString() });
|
||||
}
|
||||
//
|
||||
if (GlobalConfig.pluginConfig.v2rayIntegration)
|
||||
{
|
||||
if (isComplex)
|
||||
{
|
||||
LOG(MODULE_CONNECTION, "WARNING: Complex connection config support of this feature has not been tested.")
|
||||
}
|
||||
QList<std::tuple<QString, QString, QString>> pluginProcessedOutboundList;
|
||||
//
|
||||
// Process outbounds.
|
||||
{
|
||||
OUTBOUNDS new_outbounds;
|
||||
auto pluginPort = GlobalConfig.pluginConfig.portAllocationStart;
|
||||
//
|
||||
/// Key = Original Outbound Tag, Value = QStringList containing new outbound lists.
|
||||
for (const auto &outbound_v : fullConfig["outbounds"].toArray())
|
||||
{
|
||||
const auto &outbound = outbound_v.toObject();
|
||||
const auto &outProtocol = outbound["protocol"].toString();
|
||||
//
|
||||
if (!kernels.contains(outProtocol))
|
||||
{
|
||||
// Normal outbound, or the one without a plugin supported.
|
||||
new_outbounds.push_back(outbound);
|
||||
continue;
|
||||
}
|
||||
LOG(MODULE_CONNECTION, "Get kernel plugin: " + outProtocol)
|
||||
auto kernel = kernels[outProtocol].get();
|
||||
disconnect(kernel, &QvPluginKernel::OnKernelStatsAvailable, this, &KernelInstanceHandler::OnStatsDataArrived_p);
|
||||
activeKernels.insert(outProtocol, kernel);
|
||||
//
|
||||
QMap<QString, int> pluginInboundPort;
|
||||
const auto &originalOutboundTag = outbound["tag"].toString();
|
||||
for (const auto &[inProtocol, inPort, inTag] : inboundInfo)
|
||||
{
|
||||
if (!QStringList{ "http", "socks" }.contains(inProtocol))
|
||||
continue;
|
||||
pluginInboundPort.insert(inProtocol, pluginPort);
|
||||
LOG(MODULE_VCORE, "Plugin Integration: " + QSTRN(pluginPort) + " = " + inProtocol + "(" + inTag + ") --> " + outProtocol)
|
||||
//
|
||||
const auto &freedomTag = "plugin_" + inTag + "_" + inProtocol + "-" + QSTRN(inPort) + "_" + QSTRN(pluginPort);
|
||||
const auto &pluginOutSettings = GenerateHTTPSOCKSOut("127.0.0.1", pluginPort, false, "", "");
|
||||
const auto &direct = GenerateOutboundEntry(inProtocol, pluginOutSettings, {}, {}, "0.0.0.0", freedomTag);
|
||||
//
|
||||
// Add the integration outbound to the list.
|
||||
new_outbounds.push_back(direct);
|
||||
pluginProcessedOutboundList.append({ originalOutboundTag, inTag, freedomTag });
|
||||
pluginPort++;
|
||||
}
|
||||
kernel->SetConnectionSettings(GlobalConfig.inboundConfig.listenip, pluginInboundPort, outbound["settings"].toObject());
|
||||
}
|
||||
fullConfig["outbounds"] = new_outbounds;
|
||||
}
|
||||
//
|
||||
// Process routing entries
|
||||
{
|
||||
QJsonArray newRules;
|
||||
auto unprocessedOutbound = pluginProcessedOutboundList;
|
||||
const auto rules = fullConfig["routing"].toObject()["rules"].toArray();
|
||||
for (auto i = 0; i < rules.count(); i++)
|
||||
{
|
||||
const auto rule = rules.at(i).toObject();
|
||||
//
|
||||
bool ruleProcessed = false;
|
||||
for (const auto &[originalTag, inboundTag, newOutboundTag] : pluginProcessedOutboundList)
|
||||
{
|
||||
// Check if a rule corresponds to the plugin outbound.
|
||||
if (rule["outboundTag"] == originalTag)
|
||||
{
|
||||
auto newRule = rule;
|
||||
newRule["outboundTag"] = newOutboundTag;
|
||||
newRule["inboundTag"] = QJsonArray{ inboundTag };
|
||||
newRules.push_back(newRule);
|
||||
ruleProcessed = true;
|
||||
unprocessedOutbound.removeOne({ originalTag, inboundTag, newOutboundTag });
|
||||
}
|
||||
}
|
||||
if (!ruleProcessed)
|
||||
{
|
||||
newRules.append(rule);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &[originalTag, inboundTag, newOutboundTag] : unprocessedOutbound)
|
||||
{
|
||||
QJsonObject integrationRule;
|
||||
integrationRule["type"] = "field";
|
||||
integrationRule["outboundTag"] = newOutboundTag;
|
||||
integrationRule["inboundTag"] = QJsonArray{ inboundTag };
|
||||
newRules.push_back(integrationRule);
|
||||
}
|
||||
auto routing = fullConfig["routing"].toObject();
|
||||
routing["rules"] = newRules;
|
||||
fullConfig["routing"] = routing;
|
||||
}
|
||||
// ================================================================================================
|
||||
//
|
||||
currentConnectionId = id;
|
||||
lastConnectionId = id;
|
||||
bool success = true;
|
||||
for (auto &kernel : activeKernels.keys())
|
||||
{
|
||||
bool status = activeKernels[kernel]->StartKernel();
|
||||
success = success && status;
|
||||
if (!status)
|
||||
{
|
||||
LOG(MODULE_CONNECTION, "Plugin Kernel: " + kernel + " failed to start.")
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!success)
|
||||
{
|
||||
StopConnection();
|
||||
return tr("A plugin kernel failed to start. Please check the outbound settings.");
|
||||
}
|
||||
//
|
||||
auto result = vCoreInstance->StartConnection(fullConfig);
|
||||
//
|
||||
if (!result.has_value())
|
||||
{
|
||||
emit OnConnected(currentConnectionId);
|
||||
PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), inboundPorts, Events::Connectivity::QvConnecticity_Connected });
|
||||
}
|
||||
else
|
||||
{
|
||||
PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), {}, Events::Connectivity::QvConnecticity_Disconnected });
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto firstOutbound = fullConfig["outbounds"].toArray().first().toObject();
|
||||
const auto protocol = firstOutbound["protocol"].toString();
|
||||
if (kernels.contains(protocol))
|
||||
{
|
||||
auto kernel = kernels[firstOutbound["protocol"].toString()].get();
|
||||
activeKernels[protocol] = kernel;
|
||||
QMap<QString, int> pluginInboundPort;
|
||||
for (const auto &[_protocol, _port, _tag] : inboundInfo)
|
||||
{
|
||||
pluginInboundPort[_protocol] = _port;
|
||||
}
|
||||
connect(kernel, &QvPluginKernel::OnKernelStatsAvailable, this, &KernelInstanceHandler::OnStatsDataArrived_p);
|
||||
currentConnectionId = id;
|
||||
lastConnectionId = id;
|
||||
kernel->SetConnectionSettings(GlobalConfig.inboundConfig.listenip, pluginInboundPort, firstOutbound["settings"].toObject());
|
||||
bool result = kernel->StartKernel();
|
||||
if (result)
|
||||
{
|
||||
emit OnConnected(currentConnectionId);
|
||||
return {};
|
||||
}
|
||||
else
|
||||
{
|
||||
return tr("A plugin kernel failed to start. Please check the outbound settings.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
currentConnectionId = id;
|
||||
lastConnectionId = id;
|
||||
auto result = vCoreInstance->StartConnection(fullConfig);
|
||||
if (result.has_value())
|
||||
{
|
||||
PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), {}, Events::Connectivity::QvConnecticity_Disconnected });
|
||||
}
|
||||
else
|
||||
{
|
||||
emit OnConnected(currentConnectionId);
|
||||
PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), inboundPorts, Events::Connectivity::QvConnecticity_Connected });
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KernelInstanceHandler::RestartConnection()
|
||||
{
|
||||
StopConnection();
|
||||
StartConnection(lastConnectionId, root);
|
||||
}
|
||||
|
||||
void KernelInstanceHandler::OnKernelCrashed_p(const QString &msg)
|
||||
{
|
||||
StopConnection();
|
||||
emit OnCrashed(currentConnectionId, msg);
|
||||
emit OnDisconnected(currentConnectionId);
|
||||
lastConnectionId = currentConnectionId;
|
||||
currentConnectionId = NullConnectionId;
|
||||
}
|
||||
|
||||
void KernelInstanceHandler::OnKernelLogAvailable_p(const QString &log)
|
||||
{
|
||||
emit OnKernelLogAvailable(currentConnectionId, log);
|
||||
}
|
||||
|
||||
void KernelInstanceHandler::StopConnection()
|
||||
{
|
||||
if (isConnected)
|
||||
{
|
||||
PluginHost->Send_ConnectivityEvent({ GetDisplayName(currentConnectionId), {}, Events::Connectivity::QvConnecticity_Disconnecting });
|
||||
if (vCoreInstance->KernelStarted)
|
||||
{
|
||||
vCoreInstance->StopConnection();
|
||||
}
|
||||
//
|
||||
for (const auto &kernel : activeKernels.keys())
|
||||
{
|
||||
LOG(MODULE_CONNECTION, "Stopping plugin kernel: " + kernel)
|
||||
activeKernels[kernel]->StopKernel();
|
||||
}
|
||||
// Copy
|
||||
ConnectionId id = currentConnectionId;
|
||||
currentConnectionId = NullConnectionId;
|
||||
emit OnDisconnected(id);
|
||||
PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), {}, Events::Connectivity::QvConnecticity_Disconnected });
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(MODULE_CORE_HANDLER, "Cannot disconnect when there's nothing connected.")
|
||||
}
|
||||
}
|
||||
|
||||
void KernelInstanceHandler::OnStatsDataArrived_p(const quint64 uploadSpeed, const quint64 downloadSpeed)
|
||||
{
|
||||
if (isConnected)
|
||||
{
|
||||
emit OnStatsDataAvailable(currentConnectionId, uploadSpeed, downloadSpeed);
|
||||
}
|
||||
}
|
||||
} // namespace Qv2ray::core::handlers
|
56
src/core/handler/KernelInstanceHandler.hpp
Normal file
@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
#include "components/plugins/QvPluginHost.hpp"
|
||||
#include "core/CoreSafeTypes.hpp"
|
||||
#include "core/kernel/V2rayKernelInteractions.hpp"
|
||||
|
||||
#include <QObject>
|
||||
#include <optional>
|
||||
|
||||
namespace Qv2ray::core::handlers
|
||||
{
|
||||
class KernelInstanceHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit KernelInstanceHandler(QObject *parent = nullptr);
|
||||
~KernelInstanceHandler();
|
||||
|
||||
std::optional<QString> StartConnection(const ConnectionId &id, const CONFIGROOT &root);
|
||||
void RestartConnection();
|
||||
void StopConnection();
|
||||
const ConnectionId CurrentConnection() const
|
||||
{
|
||||
return currentConnectionId;
|
||||
}
|
||||
bool isConnected(const ConnectionId &id) const
|
||||
{
|
||||
return id == currentConnectionId;
|
||||
}
|
||||
const QMap<QString, int> InboundPorts() const
|
||||
{
|
||||
return inboundPorts;
|
||||
}
|
||||
|
||||
signals:
|
||||
void OnConnected(const ConnectionId &id);
|
||||
void OnDisconnected(const ConnectionId &id);
|
||||
void OnCrashed(const ConnectionId &id, const QString &errMessage);
|
||||
void OnStatsDataAvailable(const ConnectionId &id, const quint64 uploadSpeed, const quint64 downloadSpeed);
|
||||
void OnKernelLogAvailable(const ConnectionId &id, const QString &log);
|
||||
|
||||
private slots:
|
||||
void OnKernelCrashed_p(const QString &msg);
|
||||
void OnKernelLogAvailable_p(const QString &log);
|
||||
void OnStatsDataArrived_p(const quint64 uploadSpeed, const quint64 downloadSpeed);
|
||||
|
||||
private:
|
||||
QMap<QString, std::shared_ptr<QvPluginKernel>> kernels;
|
||||
QMap<QString, QvPluginKernel *> activeKernels;
|
||||
QMap<QString, int> inboundPorts;
|
||||
CONFIGROOT root;
|
||||
V2rayKernelInstance *vCoreInstance = nullptr;
|
||||
ConnectionId currentConnectionId = NullConnectionId;
|
||||
ConnectionId lastConnectionId = NullConnectionId;
|
||||
};
|
||||
inline const KernelInstanceHandler *KernelInstance;
|
||||
} // namespace Qv2ray::core::handlers
|
@ -1,49 +0,0 @@
|
||||
#include "ConfigHandler.hpp"
|
||||
#include "core/connection/Generation.hpp"
|
||||
|
||||
optional<QString> QvConfigHandler::CHStartConnection_p(const ConnectionId &id, const CONFIGROOT &root)
|
||||
{
|
||||
connections[id].lastConnected = system_clock::to_time_t(system_clock::now());
|
||||
//
|
||||
auto fullConfig = GenerateRuntimeConfig(root);
|
||||
auto result = vCoreInstance->StartConnection(id, fullConfig);
|
||||
|
||||
if (!result.has_value())
|
||||
{
|
||||
currentConnectionId = id;
|
||||
emit OnConnected(currentConnectionId);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void QvConfigHandler::CHStopConnection_p()
|
||||
{
|
||||
if (vCoreInstance->KernelStarted)
|
||||
{
|
||||
vCoreInstance->StopConnection();
|
||||
// Copy
|
||||
ConnectionId id = currentConnectionId;
|
||||
currentConnectionId = NullConnectionId;
|
||||
emit OnDisconnected(id);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(MODULE_CORE_HANDLER, "VCore is not started, not disconnecting")
|
||||
}
|
||||
}
|
||||
|
||||
void QvConfigHandler::OnStatsDataArrived(const ConnectionId &id, const quint64 uploadSpeed, const quint64 downloadSpeed)
|
||||
{
|
||||
connections[id].upLinkData += uploadSpeed;
|
||||
connections[id].downLinkData += downloadSpeed;
|
||||
emit OnStatsAvailable(id, uploadSpeed, downloadSpeed, connections[id].upLinkData, connections[id].downLinkData);
|
||||
}
|
||||
|
||||
void QvConfigHandler::OnVCoreCrashed(const ConnectionId &id)
|
||||
{
|
||||
LOG(MODULE_CORE_HANDLER, "V2ray core crashed!")
|
||||
currentConnectionId = NullConnectionId;
|
||||
emit OnDisconnected(id);
|
||||
emit OnCrashed();
|
||||
}
|
@ -91,7 +91,7 @@ namespace Qv2ray::core::kernel
|
||||
qint64 value_up = 0;
|
||||
qint64 value_down = 0;
|
||||
|
||||
for (auto tag : inboundTags)
|
||||
for (const auto &tag : inboundTags)
|
||||
{
|
||||
value_up += CallStatsAPIByName("inbound>>>" + tag + ">>>traffic>>>uplink");
|
||||
value_down += CallStatsAPIByName("inbound>>>" + tag + ">>>traffic>>>downlink");
|
||||
@ -105,7 +105,6 @@ namespace Qv2ray::core::kernel
|
||||
|
||||
if (running)
|
||||
{
|
||||
apiFailedCounter = 0;
|
||||
emit OnDataReady(value_up, value_down);
|
||||
}
|
||||
|
||||
@ -169,6 +168,10 @@ namespace Qv2ray::core::kernel
|
||||
LOG(MODULE_VCORE, "API call returns: " + QSTRN(status.error_code()) + " (" + QString::fromStdString(status.error_message()) + ")")
|
||||
apiFailedCounter++;
|
||||
}
|
||||
else
|
||||
{
|
||||
apiFailedCounter = 0;
|
||||
}
|
||||
|
||||
qint64 data = response.stat().value();
|
||||
#else
|
||||
|
1
src/core/kernel/PluginKernelInteractions.cpp
Normal file
@ -0,0 +1 @@
|
||||
#include "PluginKernelInteractions.hpp"
|
6
src/core/kernel/PluginKernelInteractions.hpp
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
#include "components/plugins/QvPluginHost.hpp"
|
||||
namespace Qv2ray::core::kernel
|
||||
{
|
||||
|
||||
}
|
@ -4,13 +4,14 @@
|
||||
|
||||
namespace Qv2ray::core::kernel::abi
|
||||
{
|
||||
[[nodiscard]] QvKernelABICompatibility checkCompatibility(QvKernelABIType hostType, QvKernelABIType targetType)
|
||||
QvKernelABICompatibility checkCompatibility(QvKernelABIType hostType, QvKernelABIType targetType)
|
||||
{
|
||||
switch (hostType)
|
||||
{
|
||||
case ABI_WIN32: [[fallthrough]];
|
||||
case ABI_MACH_O: [[fallthrough]];
|
||||
case ABI_ELF_AARCH64: [[fallthrough]];
|
||||
case ABI_WIN32:
|
||||
case ABI_MACH_O:
|
||||
case ABI_ELF_AARCH64:
|
||||
case ABI_ELF_ARM:
|
||||
case ABI_ELF_X86: return targetType == hostType ? ABI_PERFECT : ABI_NOPE;
|
||||
case ABI_ELF_X86_64: return targetType == hostType ? ABI_PERFECT : targetType == ABI_ELF_X86 ? ABI_MAYBE : ABI_NOPE;
|
||||
case ABI_ELF_OTHER: return targetType == hostType ? ABI_PERFECT : ABI_MAYBE;
|
||||
@ -18,7 +19,7 @@ namespace Qv2ray::core::kernel::abi
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] std::pair<std::optional<QvKernelABIType>, std::optional<QString>> deduceKernelABI(const QString &pathCoreExecutable)
|
||||
std::pair<std::optional<QvKernelABIType>, std::optional<QString>> deduceKernelABI(const QString &pathCoreExecutable)
|
||||
{
|
||||
QFile file(pathCoreExecutable);
|
||||
if (!file.exists())
|
||||
@ -43,6 +44,8 @@ namespace Qv2ray::core::kernel::abi
|
||||
return { QvKernelABIType::ABI_ELF_X86, std::nullopt };
|
||||
else if (elfInstruction == 0xB700u)
|
||||
return { QvKernelABIType::ABI_ELF_AARCH64, std::nullopt };
|
||||
else if (elfInstruction == 0x2800u)
|
||||
return { QvKernelABIType::ABI_ELF_ARM, std::nullopt };
|
||||
else
|
||||
return { QvKernelABIType::ABI_ELF_OTHER, std::nullopt };
|
||||
}
|
||||
@ -54,7 +57,7 @@ namespace Qv2ray::core::kernel::abi
|
||||
return { std::nullopt, QObject::tr("cannot deduce the type of core executable file %1").arg(pathCoreExecutable) };
|
||||
}
|
||||
|
||||
[[nodiscard]] QString abiToString(QvKernelABIType abi)
|
||||
QString abiToString(QvKernelABIType abi)
|
||||
{
|
||||
switch (abi)
|
||||
{
|
||||
@ -63,6 +66,7 @@ namespace Qv2ray::core::kernel::abi
|
||||
case ABI_ELF_X86: return QObject::tr("ELF x86 executable");
|
||||
case ABI_ELF_X86_64: return QObject::tr("ELF amd64 executable");
|
||||
case ABI_ELF_AARCH64: return QObject::tr("ELF arm64 executable");
|
||||
case ABI_ELF_ARM: return QObject::tr("ELF arm executable");
|
||||
case ABI_ELF_OTHER: return QObject::tr("other ELF executable");
|
||||
default: return QObject::tr("unknown abi");
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ namespace Qv2ray::core::kernel
|
||||
ABI_ELF_X86,
|
||||
ABI_ELF_X86_64,
|
||||
ABI_ELF_AARCH64,
|
||||
ABI_ELF_ARM,
|
||||
ABI_ELF_OTHER,
|
||||
};
|
||||
|
||||
@ -38,10 +39,14 @@ namespace Qv2ray::core::kernel
|
||||
QvKernelABIType::ABI_WIN32;
|
||||
#elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_ARM_64)
|
||||
QvKernelABIType::ABI_ELF_AARCH64;
|
||||
#elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_ARM_V7)
|
||||
QvKernelABIType::ABI_ELF_ARM;
|
||||
#else
|
||||
#error "unknown architecture"
|
||||
#endif
|
||||
|
||||
[[nodiscard]] std::pair<std::optional<QvKernelABIType>, std::optional<QString>> deduceKernelABI(const QString &pathCoreExecutable);
|
||||
[[nodiscard]] QvKernelABICompatibility checkCompatibility(QvKernelABIType hostType, QvKernelABIType targetType);
|
||||
[[nodiscard]] QString abiToString(QvKernelABIType abi);
|
||||
std::pair<std::optional<QvKernelABIType>, std::optional<QString>> deduceKernelABI(const QString &pathCoreExecutable);
|
||||
QvKernelABICompatibility checkCompatibility(QvKernelABIType hostType, QvKernelABIType targetType);
|
||||
QString abiToString(QvKernelABIType abi);
|
||||
} // namespace abi
|
||||
} // namespace Qv2ray::core::kernel
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "KernelInteractions.hpp"
|
||||
#include "V2rayKernelInteractions.hpp"
|
||||
|
||||
#include "APIBackend.hpp"
|
||||
#include "common/QvHelpers.hpp"
|
||||
@ -31,33 +31,42 @@ namespace Qv2ray::core::kernel
|
||||
}
|
||||
|
||||
coreFile.close();
|
||||
|
||||
// Get Core ABI.
|
||||
auto [abi, err] = kernel::abi::deduceKernelABI(vCorePath);
|
||||
if (err)
|
||||
{
|
||||
LOG(MODULE_VCORE, "Core ABI deduction failed: " + err.value())
|
||||
*message = err.value();
|
||||
LOG(MODULE_VCORE, "Core ABI deduction failed: " + ACCESS_OPTIONAL_VALUE(err))
|
||||
*message = ACCESS_OPTIONAL_VALUE(err);
|
||||
return false;
|
||||
}
|
||||
LOG(MODULE_VCORE, "Core ABI: " + kernel::abi::abiToString(abi.value()))
|
||||
LOG(MODULE_VCORE, "Core ABI: " + kernel::abi::abiToString(ACCESS_OPTIONAL_VALUE(abi)))
|
||||
|
||||
// Get Compiled ABI
|
||||
auto compiledABI = kernel::abi::COMPILED_ABI_TYPE;
|
||||
LOG(MODULE_VCORE, "Host ABI: " + kernel::abi::abiToString(compiledABI))
|
||||
|
||||
// Check ABI Compatibility.
|
||||
switch (kernel::abi::checkCompatibility(compiledABI, abi.value()))
|
||||
switch (kernel::abi::checkCompatibility(compiledABI, ACCESS_OPTIONAL_VALUE(abi)))
|
||||
{
|
||||
case kernel::abi::ABI_NOPE:
|
||||
{
|
||||
LOG(MODULE_VCORE, "Host is incompatible with core")
|
||||
*message = tr("V2Ray core is incompatible with your platform.\r\n" //
|
||||
"Expected core ABI is %1, but got actual %2.\r\n" //
|
||||
"Maybe you have downloaded the wrong core?")
|
||||
.arg(kernel::abi::abiToString(compiledABI), kernel::abi::abiToString(abi.value()));
|
||||
.arg(kernel::abi::abiToString(compiledABI), kernel::abi::abiToString(ACCESS_OPTIONAL_VALUE(abi)));
|
||||
return false;
|
||||
case kernel::abi::ABI_MAYBE: LOG(MODULE_VCORE, "WARNING: Host maybe incompatible with core"); [[fallthrough]];
|
||||
case kernel::abi::ABI_PERFECT: LOG(MODULE_VCORE, "Host is compatible with core");
|
||||
}
|
||||
case kernel::abi::ABI_MAYBE:
|
||||
{
|
||||
LOG(MODULE_VCORE, "WARNING: Host maybe incompatible with core");
|
||||
break;
|
||||
}
|
||||
case kernel::abi::ABI_PERFECT:
|
||||
{
|
||||
LOG(MODULE_VCORE, "Host is compatible with core");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
@ -156,17 +165,18 @@ namespace Qv2ray::core::kernel
|
||||
}
|
||||
else
|
||||
{
|
||||
QvMessageBoxWarn(nullptr, tr("Cannot start V2ray"),
|
||||
tr("V2ray core settings is incorrect.") + NEWLINE + NEWLINE + tr("The error is: ") + NEWLINE + v2rayCheckResult);
|
||||
QvMessageBoxWarn(nullptr, tr("Cannot start V2ray"), //
|
||||
tr("V2ray core settings is incorrect.") + NEWLINE + NEWLINE + //
|
||||
tr("The error is: ") + NEWLINE + v2rayCheckResult);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
V2rayKernelInstance::V2rayKernelInstance()
|
||||
V2rayKernelInstance::V2rayKernelInstance(QObject *parent) : QObject(parent)
|
||||
{
|
||||
vProcess = new QProcess();
|
||||
connect(vProcess, &QProcess::readyReadStandardOutput, this,
|
||||
[&]() { emit OnProcessOutputReadyRead(id, vProcess->readAllStandardOutput().trimmed()); });
|
||||
[&]() { emit OnProcessOutputReadyRead(vProcess->readAllStandardOutput().trimmed()); });
|
||||
connect(vProcess, &QProcess::stateChanged, [&](QProcess::ProcessState state) {
|
||||
DEBUG(MODULE_VCORE, "V2ray kernel process status changed: " + QVariant::fromValue(state).toString())
|
||||
|
||||
@ -175,7 +185,7 @@ namespace Qv2ray::core::kernel
|
||||
{
|
||||
LOG(MODULE_VCORE, "V2ray kernel crashed.")
|
||||
StopConnection();
|
||||
emit OnProcessErrored(id);
|
||||
emit OnProcessErrored("V2ray kernel crashed.");
|
||||
}
|
||||
});
|
||||
apiWorker = new APIWorker();
|
||||
@ -183,7 +193,7 @@ namespace Qv2ray::core::kernel
|
||||
KernelStarted = false;
|
||||
}
|
||||
|
||||
optional<QString> V2rayKernelInstance::StartConnection(const ConnectionId &id, const CONFIGROOT &root)
|
||||
optional<QString> V2rayKernelInstance::StartConnection(const CONFIGROOT &root)
|
||||
{
|
||||
if (KernelStarted)
|
||||
{
|
||||
@ -206,8 +216,6 @@ namespace Qv2ray::core::kernel
|
||||
vProcess->waitForStarted();
|
||||
DEBUG(MODULE_VCORE, "V2ray core started.")
|
||||
KernelStarted = true;
|
||||
// Set Connection ID
|
||||
this->id = id;
|
||||
QStringList inboundTags;
|
||||
|
||||
for (auto item : root["inbounds"].toArray())
|
||||
@ -285,6 +293,6 @@ namespace Qv2ray::core::kernel
|
||||
|
||||
void V2rayKernelInstance::onAPIDataReady(const quint64 speedUp, const quint64 speedDown)
|
||||
{
|
||||
emit OnNewStatsDataArrived(id, speedUp, speedDown);
|
||||
emit OnNewStatsDataArrived(speedUp, speedDown);
|
||||
}
|
||||
} // namespace Qv2ray::core::kernel
|
@ -12,7 +12,7 @@ namespace Qv2ray::core::kernel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit V2rayKernelInstance();
|
||||
explicit V2rayKernelInstance(QObject *parent = nullptr);
|
||||
~V2rayKernelInstance() override;
|
||||
//
|
||||
// Speed
|
||||
@ -21,7 +21,7 @@ namespace Qv2ray::core::kernel
|
||||
qulonglong getAllSpeedUp();
|
||||
qulonglong getAllSpeedDown();
|
||||
//
|
||||
optional<QString> StartConnection(const ConnectionId &id, const CONFIGROOT &root);
|
||||
optional<QString> StartConnection(const CONFIGROOT &root);
|
||||
void StopConnection();
|
||||
bool KernelStarted = false;
|
||||
//
|
||||
@ -29,19 +29,17 @@ namespace Qv2ray::core::kernel
|
||||
static bool ValidateKernel(const QString &vCorePath, const QString &vAssetsPath, QString *message);
|
||||
|
||||
signals:
|
||||
void OnProcessErrored(const ConnectionId &id);
|
||||
void OnProcessOutputReadyRead(const ConnectionId &id, const QString &output);
|
||||
void OnNewStatsDataArrived(const ConnectionId &id, const quint64 speedUp, const quint64 speedDown);
|
||||
void OnProcessErrored(const QString &errMessage);
|
||||
void OnProcessOutputReadyRead(const QString &output);
|
||||
void OnNewStatsDataArrived(const quint64 speedUp, const quint64 speedDown);
|
||||
|
||||
public slots:
|
||||
private slots:
|
||||
void onAPIDataReady(const quint64 speedUp, const quint64 speedDown);
|
||||
|
||||
private:
|
||||
APIWorker *apiWorker;
|
||||
QProcess *vProcess;
|
||||
bool apiEnabled;
|
||||
//
|
||||
ConnectionId id = NullConnectionId;
|
||||
};
|
||||
} // namespace Qv2ray::core::kernel
|
||||
|
52
src/main.cpp
@ -4,6 +4,7 @@
|
||||
#include "common/QvTranslator.hpp"
|
||||
#include "core/handler/ConfigHandler.hpp"
|
||||
#include "core/settings/SettingsBackend.hpp"
|
||||
#include "src/components/plugins/QvPluginHost.hpp"
|
||||
#include "ui/w_MainWindow.hpp"
|
||||
|
||||
#include <QApplication>
|
||||
@ -139,7 +140,7 @@ bool initialiseQv2ray()
|
||||
Qv2rayConfig conf;
|
||||
conf.kernelConfig.KernelPath(QString(QV2RAY_DEFAULT_VCORE_PATH));
|
||||
conf.kernelConfig.AssetsPath(QString(QV2RAY_DEFAULT_VASSETS_PATH));
|
||||
conf.logLevel = 2;
|
||||
conf.logLevel = 3;
|
||||
conf.uiConfig.language = QLocale::system().name();
|
||||
//
|
||||
// Save initial config.
|
||||
@ -229,7 +230,7 @@ int main(int argc, char *argv[])
|
||||
#endif
|
||||
if (StartupOption.noScaleFactors)
|
||||
{
|
||||
LOG(MODULE_INIT, "Force set QT_SCALE_FACTOR to 0.")
|
||||
LOG(MODULE_INIT, "Force set QT_SCALE_FACTOR to 1.")
|
||||
LOG(MODULE_UI, "Original QT_SCALE_FACTOR was: " + qEnvironmentVariable("QT_SCALE_FACTOR"))
|
||||
qputenv("QT_SCALE_FACTOR", "1");
|
||||
}
|
||||
@ -237,6 +238,9 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
LOG(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
|
||||
}
|
||||
SingleApplication _qApp(argc, argv, false,
|
||||
SingleApplication::User | SingleApplication::ExcludeAppPath | SingleApplication::ExcludeAppVersion);
|
||||
@ -250,25 +254,25 @@ int main(int argc, char *argv[])
|
||||
bool _result_ = Qv2rayTranslator->InstallTranslation(_lang);
|
||||
LOG(MODULE_UI, "Installing a tranlator from OS: " + _lang + " -- " + (_result_ ? "OK" : "Failed"))
|
||||
//
|
||||
LOG("LICENCE", NEWLINE //
|
||||
"This program comes with ABSOLUTELY NO WARRANTY." NEWLINE //
|
||||
"This is free software, and you are welcome to redistribute it" NEWLINE //
|
||||
"under certain conditions." NEWLINE //
|
||||
NEWLINE //
|
||||
"Copyright (c) 2019-2020 Qv2ray Development Group." NEWLINE //
|
||||
NEWLINE //
|
||||
"Libraries that have been used in Qv2ray are listed below (Sorted by date added):" NEWLINE //
|
||||
"Copyright (c) 2020 xyz347 (@xyz347): X2Struct (Apache)" NEWLINE //
|
||||
"Copyright (c) 2011 SCHUTZ Sacha (@dridk): QJsonModel (MIT)" NEWLINE //
|
||||
"Copyright (c) 2020 Nikolaos Ftylitakis (@ftylitak): QZXing (Apache2)" NEWLINE //
|
||||
"Copyright (c) 2016 Singein (@Singein): ScreenShot (MIT)" NEWLINE //
|
||||
"Copyright (c) 2020 Itay Grudev (@itay-grudev): SingleApplication (MIT)" NEWLINE //
|
||||
"Copyright (c) 2020 paceholder (@paceholder): nodeeditor (Qv2ray group modified version) (BSD-3-Clause)" NEWLINE //
|
||||
"Copyright (c) 2019 TheWanderingCoel (@TheWanderingCoel): ShadowClash (launchatlogin) (GPLv3)" NEWLINE //
|
||||
"Copyright (c) 2020 Ram Pani (@DuckSoft): QvRPCBridge (WTFPL)" NEWLINE //
|
||||
"Copyright (c) 2019 ShadowSocks (@shadowsocks): libQtShadowsocks (LGPLv3)" NEWLINE //
|
||||
"Copyright (c) 2015-2020 qBittorrent (Anton Lashkov) (@qBittorrent): speedplotview (GPLv2)" NEWLINE //
|
||||
"Copyright (c) 2020 yhirose (@yhirose): cpp-httplib (MIT)" NEWLINE NEWLINE) //
|
||||
LOG("LICENCE", NEWLINE //
|
||||
"This program comes with ABSOLUTELY NO WARRANTY." NEWLINE //
|
||||
"This is free software, and you are welcome to redistribute it" NEWLINE //
|
||||
"under certain conditions." NEWLINE //
|
||||
NEWLINE //
|
||||
"Copyright (c) 2019-2020 Qv2ray Development Group." NEWLINE //
|
||||
NEWLINE //
|
||||
"Libraries that have been used in Qv2ray are listed below (Sorted by date added):" NEWLINE //
|
||||
"Copyright (c) 2020 xyz347 (@xyz347): X2Struct (Apache)" NEWLINE //
|
||||
"Copyright (c) 2011 SCHUTZ Sacha (@dridk): QJsonModel (MIT)" NEWLINE //
|
||||
"Copyright (c) 2020 Nikolaos Ftylitakis (@ftylitak): QZXing (Apache2)" NEWLINE //
|
||||
"Copyright (c) 2016 Singein (@Singein): ScreenShot (MIT)" NEWLINE //
|
||||
"Copyright (c) 2020 Itay Grudev (@itay-grudev): SingleApplication (MIT)" NEWLINE //
|
||||
"Copyright (c) 2020 paceholder (@paceholder): nodeeditor (Qv2ray group modified version) (BSD-3-Clause)" NEWLINE //
|
||||
"Copyright (c) 2019 TheWanderingCoel (@TheWanderingCoel): ShadowClash (launchatlogin) (GPLv3)" NEWLINE //
|
||||
"Copyright (c) 2020 Ram Pani (@DuckSoft): QvRPCBridge (WTFPL)" NEWLINE //
|
||||
"Copyright (c) 2019 ShadowSocks (@shadowsocks): libQtShadowsocks (LGPLv3)" NEWLINE //
|
||||
"Copyright (c) 2015-2020 qBittorrent (Anton Lashkov) (@qBittorrent): speedplotview (GPLv2)" NEWLINE //
|
||||
"Copyright (c) 2020 yhirose (@yhirose): cpp-httplib (MIT)" NEWLINE NEWLINE) //
|
||||
//
|
||||
LOG(MODULE_INIT, "Qv2ray Start Time: " + QSTRN(QTime::currentTime().msecsSinceStartOfDay()))
|
||||
//
|
||||
@ -404,11 +408,8 @@ int main(int argc, char *argv[])
|
||||
#endif
|
||||
//_qApp.setAttribute(Qt::AA_DontUseNativeMenuBar);
|
||||
// Initialise Connection Handler
|
||||
PluginHost = new QvPluginHost();
|
||||
ConnectionManager = new QvConfigHandler();
|
||||
// Handler for session logout, shutdown, etc.
|
||||
// Will not block.
|
||||
QGuiApplication::setFallbackSessionManagementEnabled(false);
|
||||
QObject::connect(&_qApp, &QGuiApplication::commitDataRequest, [] { LOG(MODULE_INIT, "Quit triggered by session manager.") });
|
||||
// Show MainWindow
|
||||
MainWindow w;
|
||||
QObject::connect(&_qApp, &SingleApplication::instanceStarted, [&]() {
|
||||
@ -423,6 +424,7 @@ int main(int argc, char *argv[])
|
||||
#endif
|
||||
auto rcode = _qApp.exec();
|
||||
delete ConnectionManager;
|
||||
delete PluginHost;
|
||||
LOG(MODULE_INIT, "Quitting normally")
|
||||
return rcode;
|
||||
#ifndef QT_DEBUG
|
||||
|
@ -9,78 +9,53 @@
|
||||
#include <QIntValidator>
|
||||
#include <iostream>
|
||||
|
||||
OutboundEditor::OutboundEditor(QWidget *parent) : QDialog(parent), Tag(""), Mux(), vmess(), shadowsocks()
|
||||
OutboundEditor::OutboundEditor(QWidget *parent) : QDialog(parent), tag(OUTBOUND_TAG_PROXY)
|
||||
{
|
||||
QvMessageBusConnect(OutboundEditor);
|
||||
setupUi(this);
|
||||
//
|
||||
ssWidget = new StreamSettingsWidget(this);
|
||||
transportFrame->addWidget(ssWidget);
|
||||
outboundType = "vmess";
|
||||
//
|
||||
shadowsocks = ShadowSocksServerObject();
|
||||
socks = SocksServerObject();
|
||||
vmess = VMessServerObject();
|
||||
socks.users.push_back(SocksServerObject::UserObject());
|
||||
vmess.users.push_back(VMessServerObject::UserObject());
|
||||
streamSettingsWidget = new StreamSettingsWidget(this);
|
||||
streamSettingsWidget->SetStreamObject({});
|
||||
transportFrame->addWidget(streamSettingsWidget);
|
||||
//
|
||||
auto stream = StreamSettingsObject();
|
||||
ssWidget->SetStreamObject(stream);
|
||||
socks.users.push_back({});
|
||||
vmess.users.push_back({});
|
||||
//
|
||||
OutboundType = "vmess";
|
||||
Tag = OUTBOUND_TAG_PROXY;
|
||||
useFProxy = false;
|
||||
ReloadGUI();
|
||||
Result = GenerateConnectionJson();
|
||||
auto pluginEditorWidgetsInfo = PluginHost->GetOutboundEditorWidgets();
|
||||
for (const auto &plugin : pluginEditorWidgetsInfo)
|
||||
{
|
||||
for (const auto &_d : plugin->OutboundCapabilities())
|
||||
{
|
||||
outBoundTypeCombo->addItem(_d.displayName, _d.protocol);
|
||||
auto index = outboundTypeStackView->addWidget(plugin);
|
||||
pluginWidgets.insert(index, { _d, plugin });
|
||||
}
|
||||
}
|
||||
//
|
||||
outboundType = "vmess";
|
||||
useForwardProxy = false;
|
||||
}
|
||||
|
||||
QvMessageBusSlotImpl(OutboundEditor)
|
||||
{
|
||||
switch (msg)
|
||||
{
|
||||
MBShowDefaultImpl MBHideDefaultImpl MBRetranslateDefaultImpl
|
||||
case UPDATE_COLORSCHEME:
|
||||
{
|
||||
break;
|
||||
};
|
||||
MBShowDefaultImpl;
|
||||
MBHideDefaultImpl;
|
||||
MBRetranslateDefaultImpl;
|
||||
}
|
||||
}
|
||||
|
||||
OutboundEditor::OutboundEditor(OUTBOUND outboundEntry, QWidget *parent) : OutboundEditor(parent)
|
||||
OutboundEditor::OutboundEditor(const OUTBOUND &outboundEntry, QWidget *parent) : OutboundEditor(parent)
|
||||
{
|
||||
Original = outboundEntry;
|
||||
Tag = outboundEntry["tag"].toString();
|
||||
tagTxt->setText(Tag);
|
||||
OutboundType = outboundEntry["protocol"].toString();
|
||||
Mux = outboundEntry["mux"].toObject();
|
||||
useFProxy = outboundEntry[QV2RAY_USE_FPROXY_KEY].toBool(false);
|
||||
ssWidget->SetStreamObject(StructFromJsonString<StreamSettingsObject>(JsonToString(outboundEntry["streamSettings"].toObject())));
|
||||
|
||||
if (OutboundType == "vmess")
|
||||
{
|
||||
vmess =
|
||||
StructFromJsonString<VMessServerObject>(JsonToString(outboundEntry["settings"].toObject()["vnext"].toArray().first().toObject()));
|
||||
shadowsocks.port = vmess.port;
|
||||
shadowsocks.address = vmess.address;
|
||||
socks.address = vmess.address;
|
||||
socks.port = vmess.port;
|
||||
}
|
||||
else if (OutboundType == "shadowsocks")
|
||||
{
|
||||
shadowsocks = StructFromJsonString<ShadowSocksServerObject>(
|
||||
JsonToString(outboundEntry["settings"].toObject()["servers"].toArray().first().toObject()));
|
||||
vmess.address = shadowsocks.address;
|
||||
vmess.port = shadowsocks.port;
|
||||
socks.address = shadowsocks.address;
|
||||
socks.port = shadowsocks.port;
|
||||
}
|
||||
else if (OutboundType == "socks")
|
||||
{
|
||||
socks =
|
||||
StructFromJsonString<SocksServerObject>(JsonToString(outboundEntry["settings"].toObject()["servers"].toArray().first().toObject()));
|
||||
vmess.address = socks.address;
|
||||
vmess.port = socks.port;
|
||||
shadowsocks.address = socks.address;
|
||||
shadowsocks.port = socks.port;
|
||||
}
|
||||
|
||||
originalConfig = outboundEntry;
|
||||
ReloadGUI();
|
||||
Result = GenerateConnectionJson();
|
||||
}
|
||||
|
||||
OutboundEditor::~OutboundEditor()
|
||||
@ -90,124 +65,187 @@ OutboundEditor::~OutboundEditor()
|
||||
OUTBOUND OutboundEditor::OpenEditor()
|
||||
{
|
||||
int resultCode = this->exec();
|
||||
return resultCode == QDialog::Accepted ? Result : Original;
|
||||
return resultCode == QDialog::Accepted ? resultConfig : originalConfig;
|
||||
}
|
||||
|
||||
QString OutboundEditor::GetFriendlyName()
|
||||
{
|
||||
auto host = ipLineEdit->text().replace(":", "-").replace("/", "_").replace("\\", "_");
|
||||
auto port = portLineEdit->text().replace(":", "-").replace("/", "_").replace("\\", "_");
|
||||
auto type = OutboundType;
|
||||
QString name = Tag.isEmpty() ? host + "-[" + port + "]-" + type : Tag;
|
||||
auto type = outboundType;
|
||||
QString name = tag.isEmpty() ? host + "-[" + port + "]-" + type : tag;
|
||||
return name;
|
||||
}
|
||||
|
||||
OUTBOUND OutboundEditor::GenerateConnectionJson()
|
||||
{
|
||||
OUTBOUNDSETTING settings;
|
||||
auto streaming = GetRootObject(ssWidget->GetStreamSettings());
|
||||
auto streaming = GetRootObject(streamSettingsWidget->GetStreamSettings());
|
||||
|
||||
if (OutboundType == "vmess")
|
||||
if (outboundType == "vmess")
|
||||
{
|
||||
// VMess is only a ServerObject, and we need an array { "vnext": [] }
|
||||
QJsonArray vnext;
|
||||
vmess.address = address;
|
||||
vmess.port = port;
|
||||
vnext.append(GetRootObject(vmess));
|
||||
settings.insert("vnext", vnext);
|
||||
}
|
||||
else if (OutboundType == "shadowsocks")
|
||||
else if (outboundType == "shadowsocks")
|
||||
{
|
||||
streaming = QJsonObject();
|
||||
LOG(MODULE_CONNECTION, "Shadowsocks outbound does not need StreamSettings.")
|
||||
QJsonArray servers;
|
||||
shadowsocks.address = address;
|
||||
shadowsocks.port = port;
|
||||
servers.append(GetRootObject(shadowsocks));
|
||||
settings["servers"] = servers;
|
||||
}
|
||||
else if (OutboundType == "socks")
|
||||
else if (outboundType == "socks")
|
||||
{
|
||||
if (!socks.users.isEmpty() && socks.users.first().user.isEmpty() && socks.users.first().pass.isEmpty())
|
||||
{
|
||||
LOG(MODULE_UI, "Removed empty user form SOCKS settings")
|
||||
socks.users.clear();
|
||||
}
|
||||
socks.address = address;
|
||||
socks.port = port;
|
||||
streaming = QJsonObject();
|
||||
LOG(MODULE_CONNECTION, "Socks outbound does not need StreamSettings.")
|
||||
QJsonArray servers;
|
||||
servers.append(GetRootObject(socks));
|
||||
settings["servers"] = servers;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool processed = false;
|
||||
for (const auto &plugin : pluginWidgets)
|
||||
{
|
||||
if (plugin.first.protocol == outboundType)
|
||||
{
|
||||
plugin.second->SetHostInfo(address, port);
|
||||
settings = OUTBOUNDSETTING(plugin.second->GetContent());
|
||||
processed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!processed)
|
||||
{
|
||||
QvMessageBoxWarn(this, tr("Unknown outbound type."),
|
||||
tr("The specified outbound type is not supported, this may happen due to a plugin failure."));
|
||||
}
|
||||
}
|
||||
|
||||
auto root = GenerateOutboundEntry(OutboundType, settings, streaming, Mux, "0.0.0.0", Tag);
|
||||
root[QV2RAY_USE_FPROXY_KEY] = useFProxy;
|
||||
auto root = GenerateOutboundEntry(outboundType, settings, streaming, muxConfig, "0.0.0.0", tag);
|
||||
root[QV2RAY_USE_FPROXY_KEY] = useForwardProxy;
|
||||
return root;
|
||||
}
|
||||
|
||||
void OutboundEditor::ReloadGUI()
|
||||
{
|
||||
if (OutboundType == "vmess")
|
||||
tag = originalConfig["tag"].toString();
|
||||
tagTxt->setText(tag);
|
||||
outboundType = originalConfig["protocol"].toString("vmess");
|
||||
muxConfig = originalConfig["mux"].toObject();
|
||||
useForwardProxy = originalConfig[QV2RAY_USE_FPROXY_KEY].toBool(false);
|
||||
streamSettingsWidget->SetStreamObject(StructFromJsonString<StreamSettingsObject>(JsonToString(originalConfig["streamSettings"].toObject())));
|
||||
//
|
||||
useFPCB->setChecked(useForwardProxy);
|
||||
muxEnabledCB->setChecked(muxConfig["enabled"].toBool());
|
||||
muxConcurrencyTxt->setValue(muxConfig["concurrency"].toInt());
|
||||
//
|
||||
const auto &settings = originalConfig["settings"].toObject();
|
||||
//
|
||||
if (outboundType == "vmess")
|
||||
{
|
||||
outBoundTypeCombo->setCurrentIndex(0);
|
||||
ipLineEdit->setText(vmess.address);
|
||||
portLineEdit->setText(QSTRN(vmess.port));
|
||||
vmess = StructFromJsonString<VMessServerObject>(JsonToString(settings["vnext"].toArray().first().toObject()));
|
||||
if (vmess.users.empty())
|
||||
{
|
||||
vmess.users.push_back({});
|
||||
}
|
||||
address = vmess.address;
|
||||
port = vmess.port;
|
||||
idLineEdit->setText(vmess.users.front().id);
|
||||
alterLineEdit->setValue(vmess.users.front().alterId);
|
||||
securityCombo->setCurrentText(vmess.users.front().security);
|
||||
}
|
||||
else if (OutboundType == "shadowsocks")
|
||||
else if (outboundType == "shadowsocks")
|
||||
{
|
||||
outBoundTypeCombo->setCurrentIndex(1);
|
||||
shadowsocks = StructFromJsonString<ShadowSocksServerObject>(JsonToString(settings["servers"].toArray().first().toObject()));
|
||||
address = shadowsocks.address;
|
||||
port = shadowsocks.port;
|
||||
// ShadowSocks Configs
|
||||
ipLineEdit->setText(shadowsocks.address);
|
||||
portLineEdit->setText(QSTRN(shadowsocks.port));
|
||||
ss_emailTxt->setText(shadowsocks.email);
|
||||
ss_levelSpin->setValue(shadowsocks.level);
|
||||
ss_otaCheckBox->setChecked(shadowsocks.ota);
|
||||
ss_passwordTxt->setText(shadowsocks.password);
|
||||
ss_encryptionMethod->setCurrentText(shadowsocks.method);
|
||||
}
|
||||
else if (OutboundType == "socks")
|
||||
else if (outboundType == "socks")
|
||||
{
|
||||
outBoundTypeCombo->setCurrentIndex(2);
|
||||
ipLineEdit->setText(socks.address);
|
||||
portLineEdit->setText(QSTRN(socks.port));
|
||||
|
||||
socks = StructFromJsonString<SocksServerObject>(JsonToString(settings["servers"].toArray().first().toObject()));
|
||||
address = socks.address;
|
||||
port = socks.port;
|
||||
if (socks.users.empty())
|
||||
socks.users.push_back(SocksServerObject::UserObject());
|
||||
|
||||
{
|
||||
socks.users.push_back({});
|
||||
}
|
||||
socks_PasswordTxt->setText(socks.users.front().pass);
|
||||
socks_UserNameTxt->setText(socks.users.front().user);
|
||||
}
|
||||
|
||||
useFPCB->setChecked(useFProxy);
|
||||
muxEnabledCB->setChecked(Mux["enabled"].toBool());
|
||||
muxConcurrencyTxt->setValue(Mux["concurrency"].toInt());
|
||||
else
|
||||
{
|
||||
bool processed = false;
|
||||
for (const auto &index : pluginWidgets.keys())
|
||||
{
|
||||
const auto &plugin = pluginWidgets.value(index);
|
||||
if (plugin.first.protocol == outboundType)
|
||||
{
|
||||
plugin.second->SetContent(settings);
|
||||
outBoundTypeCombo->setCurrentIndex(index);
|
||||
auto [_address, _port] = plugin.second->GetHostInfo();
|
||||
address = _address;
|
||||
port = _port;
|
||||
processed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!processed)
|
||||
{
|
||||
LOG(MODULE_UI, "Outbound type: " + outboundType + " is not supported.")
|
||||
QvMessageBoxWarn(this, tr("Unknown outbound."),
|
||||
tr("The specified outbound type is invalid, this may be caused by a plugin failure.") + NEWLINE +
|
||||
tr("Please use the JsonEditor or reload the plugin."));
|
||||
reject();
|
||||
}
|
||||
}
|
||||
//
|
||||
ipLineEdit->setText(address);
|
||||
portLineEdit->setText(QSTRN(port));
|
||||
}
|
||||
|
||||
void OutboundEditor::on_buttonBox_accepted()
|
||||
{
|
||||
Result = GenerateConnectionJson();
|
||||
resultConfig = GenerateConnectionJson();
|
||||
}
|
||||
|
||||
void OutboundEditor::on_ipLineEdit_textEdited(const QString &arg1)
|
||||
{
|
||||
vmess.address = arg1;
|
||||
shadowsocks.address = arg1;
|
||||
socks.address = arg1;
|
||||
address = arg1;
|
||||
}
|
||||
|
||||
void OutboundEditor::on_portLineEdit_textEdited(const QString &arg1)
|
||||
{
|
||||
if (arg1 != "")
|
||||
{
|
||||
vmess.port = arg1.toInt();
|
||||
shadowsocks.port = arg1.toInt();
|
||||
socks.port = arg1.toInt();
|
||||
}
|
||||
port = arg1.toInt();
|
||||
}
|
||||
|
||||
void OutboundEditor::on_idLineEdit_textEdited(const QString &arg1)
|
||||
{
|
||||
if (vmess.users.empty())
|
||||
vmess.users.push_back(VMessServerObject::UserObject());
|
||||
vmess.users.push_back({});
|
||||
|
||||
vmess.users.front().id = arg1;
|
||||
}
|
||||
@ -215,43 +253,51 @@ void OutboundEditor::on_idLineEdit_textEdited(const QString &arg1)
|
||||
void OutboundEditor::on_securityCombo_currentIndexChanged(const QString &arg1)
|
||||
{
|
||||
if (vmess.users.empty())
|
||||
vmess.users.push_back(VMessServerObject::UserObject());
|
||||
vmess.users.push_back({});
|
||||
|
||||
vmess.users.front().security = arg1;
|
||||
}
|
||||
|
||||
void OutboundEditor::on_tagTxt_textEdited(const QString &arg1)
|
||||
{
|
||||
Tag = arg1;
|
||||
tag = arg1;
|
||||
}
|
||||
|
||||
void OutboundEditor::on_muxEnabledCB_stateChanged(int arg1)
|
||||
{
|
||||
Mux["enabled"] = arg1 == Qt::Checked;
|
||||
muxConfig["enabled"] = arg1 == Qt::Checked;
|
||||
}
|
||||
|
||||
void OutboundEditor::on_muxConcurrencyTxt_valueChanged(int arg1)
|
||||
{
|
||||
Mux["concurrency"] = arg1;
|
||||
muxConfig["concurrency"] = arg1;
|
||||
}
|
||||
|
||||
void OutboundEditor::on_alterLineEdit_valueChanged(int arg1)
|
||||
{
|
||||
if (vmess.users.empty())
|
||||
vmess.users.push_back(VMessServerObject::UserObject());
|
||||
vmess.users.push_back({});
|
||||
|
||||
vmess.users.front().alterId = arg1;
|
||||
}
|
||||
|
||||
void OutboundEditor::on_useFPCB_stateChanged(int arg1)
|
||||
{
|
||||
useFProxy = arg1 == Qt::Checked;
|
||||
useForwardProxy = arg1 == Qt::Checked;
|
||||
}
|
||||
|
||||
void OutboundEditor::on_outBoundTypeCombo_currentIndexChanged(int index)
|
||||
{
|
||||
// 0, 1, 2 as built-in vmess, ss, socks
|
||||
outboundTypeStackView->setCurrentIndex(index);
|
||||
OutboundType = outBoundTypeCombo->currentText().toLower();
|
||||
if (index < 3)
|
||||
{
|
||||
outboundType = outBoundTypeCombo->currentText().toLower();
|
||||
}
|
||||
else
|
||||
{
|
||||
outboundType = pluginWidgets.value(index).first.protocol;
|
||||
}
|
||||
}
|
||||
|
||||
void OutboundEditor::on_ss_emailTxt_textEdited(const QString &arg1)
|
||||
@ -282,13 +328,13 @@ void OutboundEditor::on_ss_otaCheckBox_stateChanged(int arg1)
|
||||
void OutboundEditor::on_socks_UserNameTxt_textEdited(const QString &arg1)
|
||||
{
|
||||
if (socks.users.isEmpty())
|
||||
socks.users.push_back(SocksServerObject::UserObject());
|
||||
socks.users.push_back({});
|
||||
socks.users.front().user = arg1;
|
||||
}
|
||||
|
||||
void OutboundEditor::on_socks_PasswordTxt_textEdited(const QString &arg1)
|
||||
{
|
||||
if (socks.users.isEmpty())
|
||||
socks.users.push_back(SocksServerObject::UserObject());
|
||||
socks.users.push_back({});
|
||||
socks.users.front().pass = arg1;
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include "base/Qv2rayBase.hpp"
|
||||
#include "components/plugins/QvPluginHost.hpp"
|
||||
#include "ui/messaging/QvMessageBus.hpp"
|
||||
#include "ui/widgets/StreamSettingsWidget.hpp"
|
||||
#include "ui_w_OutboundEditor.h"
|
||||
@ -13,68 +14,55 @@ class OutboundEditor
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit OutboundEditor(QWidget *parent = nullptr);
|
||||
explicit OutboundEditor(OUTBOUND outboundEntry, QWidget *parent = nullptr);
|
||||
explicit OutboundEditor(const OUTBOUND &outboundEntry, QWidget *parent = nullptr);
|
||||
~OutboundEditor();
|
||||
OUTBOUND OpenEditor();
|
||||
QString GetFriendlyName();
|
||||
|
||||
private:
|
||||
explicit OutboundEditor(QWidget *parent = nullptr);
|
||||
QvMessageBusSlotDecl;
|
||||
signals:
|
||||
void s_reload_config(bool need_restart);
|
||||
private slots:
|
||||
void on_buttonBox_accepted();
|
||||
|
||||
void on_ipLineEdit_textEdited(const QString &arg1);
|
||||
|
||||
void on_portLineEdit_textEdited(const QString &arg1);
|
||||
|
||||
void on_idLineEdit_textEdited(const QString &arg1);
|
||||
|
||||
void on_tagTxt_textEdited(const QString &arg1);
|
||||
|
||||
void on_muxEnabledCB_stateChanged(int arg1);
|
||||
|
||||
void on_muxConcurrencyTxt_valueChanged(int arg1);
|
||||
|
||||
void on_alterLineEdit_valueChanged(int arg1);
|
||||
|
||||
void on_useFPCB_stateChanged(int arg1);
|
||||
|
||||
void on_outBoundTypeCombo_currentIndexChanged(int index);
|
||||
|
||||
void on_ss_emailTxt_textEdited(const QString &arg1);
|
||||
|
||||
void on_ss_passwordTxt_textEdited(const QString &arg1);
|
||||
|
||||
void on_ss_encryptionMethod_currentIndexChanged(const QString &arg1);
|
||||
|
||||
void on_ss_levelSpin_valueChanged(int arg1);
|
||||
|
||||
void on_ss_otaCheckBox_stateChanged(int arg1);
|
||||
|
||||
void on_socks_UserNameTxt_textEdited(const QString &arg1);
|
||||
|
||||
void on_socks_PasswordTxt_textEdited(const QString &arg1);
|
||||
|
||||
void on_securityCombo_currentIndexChanged(const QString &arg1);
|
||||
|
||||
private:
|
||||
QString Tag;
|
||||
QString tag;
|
||||
void ReloadGUI();
|
||||
bool useFProxy;
|
||||
bool useForwardProxy;
|
||||
OUTBOUND GenerateConnectionJson();
|
||||
OUTBOUND Original;
|
||||
OUTBOUND Result;
|
||||
QJsonObject Mux;
|
||||
OUTBOUND originalConfig;
|
||||
OUTBOUND resultConfig;
|
||||
QJsonObject muxConfig;
|
||||
//
|
||||
// Connection Configs
|
||||
QString OutboundType;
|
||||
QString outboundType;
|
||||
QString address;
|
||||
int port;
|
||||
//
|
||||
VMessServerObject vmess;
|
||||
ShadowSocksServerObject shadowsocks;
|
||||
SocksServerObject socks;
|
||||
//
|
||||
StreamSettingsWidget *ssWidget;
|
||||
StreamSettingsWidget *streamSettingsWidget;
|
||||
//
|
||||
QMap<int, QPair<QvPluginOutboundProtocolObject, QvPluginEditor *>> pluginWidgets;
|
||||
};
|
||||
|
@ -5,7 +5,7 @@
|
||||
#include "core/connection/ConnectionIO.hpp"
|
||||
#include "core/connection/Serialization.hpp"
|
||||
#include "core/handler/ConfigHandler.hpp"
|
||||
#include "core/kernel/KernelInteractions.hpp"
|
||||
#include "core/kernel/V2rayKernelInteractions.hpp"
|
||||
#include "ui/editors/w_JsonEditor.hpp"
|
||||
#include "ui/editors/w_OutboundEditor.hpp"
|
||||
#include "ui/editors/w_RoutesEditor.hpp"
|
||||
@ -37,7 +37,10 @@ QvMessageBusSlotImpl(ImportConfigWindow)
|
||||
{
|
||||
switch (msg)
|
||||
{
|
||||
MBShowDefaultImpl MBHideDefaultImpl MBRetranslateDefaultImpl MBUpdateColorSchemeDefaultImpl
|
||||
MBShowDefaultImpl;
|
||||
MBHideDefaultImpl;
|
||||
MBRetranslateDefaultImpl;
|
||||
MBUpdateColorSchemeDefaultImpl;
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,7 +56,7 @@ QMultiHash<QString, CONFIGROOT> ImportConfigWindow::SelectConnection(bool outbou
|
||||
routeEditBtn->setEnabled(!outboundsOnly);
|
||||
this->exec();
|
||||
QMultiHash<QString, CONFIGROOT> conn;
|
||||
for (const auto connEntry : connections.values())
|
||||
for (const auto &connEntry : connections.values())
|
||||
{
|
||||
conn += connEntry;
|
||||
}
|
||||
@ -64,11 +67,11 @@ int ImportConfigWindow::ImportConnection()
|
||||
{
|
||||
this->exec();
|
||||
int count = 0;
|
||||
for (const auto groupName : connections.keys())
|
||||
for (const auto &groupName : connections.keys())
|
||||
{
|
||||
GroupId groupId = groupName.isEmpty() ? DefaultGroupId : ConnectionManager->CreateGroup(groupName, false);
|
||||
const auto groupObject = connections[groupName];
|
||||
for (const auto connConf : groupObject)
|
||||
for (const auto &connConf : groupObject)
|
||||
{
|
||||
auto connName = groupObject.key(connConf);
|
||||
|
||||
@ -77,7 +80,7 @@ int ImportConfigWindow::ImportConnection()
|
||||
{
|
||||
connName = protocol + "/" + host + ":" + QSTRN(port) + "-" + GenerateRandomString(5);
|
||||
}
|
||||
ConnectionManager->CreateConnection(connName, groupId, connConf);
|
||||
ConnectionManager->CreateConnection(connName, groupId, connConf, true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -164,7 +167,7 @@ void ImportConfigWindow::on_beginImportBtn_clicked()
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto conf : config)
|
||||
for (const auto &conf : config)
|
||||
{
|
||||
AddToGroup(newGroupName, config.key(conf), conf);
|
||||
}
|
||||
@ -173,7 +176,7 @@ void ImportConfigWindow::on_beginImportBtn_clicked()
|
||||
|
||||
if (!linkErrors.isEmpty())
|
||||
{
|
||||
for (auto item : linkErrors)
|
||||
for (const auto &item : linkErrors)
|
||||
{
|
||||
vmessConnectionStringTxt->appendPlainText(linkErrors.key(item));
|
||||
errorsList->addItem(item);
|
||||
@ -213,6 +216,8 @@ void ImportConfigWindow::on_selectImageBtn_clicked()
|
||||
imageFileEdit->setText(dir);
|
||||
//
|
||||
QFile file(dir);
|
||||
if (!file.exists())
|
||||
return;
|
||||
file.open(QFile::OpenModeFlag::ReadOnly);
|
||||
auto buf = file.readAll();
|
||||
file.close();
|
||||
@ -258,7 +263,7 @@ void ImportConfigWindow::on_errorsList_currentItemChanged(QListWidgetItem *curre
|
||||
|
||||
void ImportConfigWindow::on_connectionEditBtn_clicked()
|
||||
{
|
||||
OutboundEditor w(this);
|
||||
OutboundEditor w(OUTBOUND(), this);
|
||||
auto outboundEntry = w.OpenEditor();
|
||||
bool isChanged = w.result() == QDialog::Accepted;
|
||||
QString alias = w.GetFriendlyName();
|
||||
@ -283,7 +288,7 @@ void ImportConfigWindow::on_cancelImportBtn_clicked()
|
||||
void ImportConfigWindow::on_subscriptionButton_clicked()
|
||||
{
|
||||
hide();
|
||||
SubscriptionEditor w;
|
||||
SubscriptionEditor w(this);
|
||||
w.exec();
|
||||
auto importToComplex = !keepImportedInboundCheckBox->isEnabled();
|
||||
connections.clear();
|
||||
|
@ -1,6 +1,6 @@
|
||||
#include "w_MainWindow.hpp"
|
||||
|
||||
#include "components/pac/QvPACHandler.hpp"
|
||||
#include "components/plugins/QvPluginHost.hpp"
|
||||
#include "components/plugins/toolbar/QvToolbar.hpp"
|
||||
#include "components/proxy/QvProxyConfigurator.hpp"
|
||||
#include "components/update/UpdateChecker.hpp"
|
||||
@ -9,6 +9,7 @@
|
||||
#include "ui/editors/w_OutboundEditor.hpp"
|
||||
#include "ui/editors/w_RoutesEditor.hpp"
|
||||
#include "ui/w_ImportConfig.hpp"
|
||||
#include "ui/w_PluginManager.hpp"
|
||||
#include "ui/w_PreferencesWindow.hpp"
|
||||
#include "ui/w_SubscriptionManager.hpp"
|
||||
#include "ui/widgets/ConnectionInfoWidget.hpp"
|
||||
@ -50,11 +51,11 @@ QvMessageBusSlotImpl(MainWindow)
|
||||
|
||||
void MainWindow::UpdateColorScheme()
|
||||
{
|
||||
hTray.setIcon(QIcon(GlobalConfig.uiConfig.useDarkTrayIcon ? ":/assets/icons/ui_dark/tray.png" : ":/assets/icons/ui_light/tray.png"));
|
||||
hTray.setIcon(KernelInstance->CurrentConnection() == NullConnectionId ? Q_TRAYICON("tray.png") : Q_TRAYICON("tray-connected.png"));
|
||||
//
|
||||
importConfigButton->setIcon(QICON_R("import.png"));
|
||||
updownImageBox->setStyleSheet("image: url(" + QV2RAY_UI_COLORSCHEME_ROOT + "netspeed_arrow.png)");
|
||||
updownImageBox_2->setStyleSheet("image: url(" + QV2RAY_UI_COLORSCHEME_ROOT + "netspeed_arrow.png)");
|
||||
updownImageBox->setStyleSheet("image: url(" + QV2RAY_COLORSCHEME_ROOT + "netspeed_arrow.png)");
|
||||
updownImageBox_2->setStyleSheet("image: url(" + QV2RAY_COLORSCHEME_ROOT + "netspeed_arrow.png)");
|
||||
//
|
||||
tray_action_ShowHide->setIcon(this->windowIcon());
|
||||
action_RCM_Start->setIcon(QICON_R("connect.png"));
|
||||
@ -131,17 +132,17 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
||||
UpdateColorScheme();
|
||||
//
|
||||
//
|
||||
connect(ConnectionManager, &QvConfigHandler::OnCrashed, [&] {
|
||||
connect(ConnectionManager, &QvConfigHandler::OnKernelCrashed, [&](const ConnectionId &, const QString &reason) {
|
||||
this->show();
|
||||
QvMessageBoxWarn(this, tr("V2ray vcore terminated."),
|
||||
tr("V2ray vcore terminated unexpectedly.") + NEWLINE + NEWLINE +
|
||||
tr("To solve the problem, read the V2ray log in the log text browser."));
|
||||
QvMessageBoxWarn(this, tr("Kernel terminated."),
|
||||
tr("The kernel terminated unexpectedly:") + NEWLINE + reason + NEWLINE + NEWLINE +
|
||||
tr("To solve the problem, read the kernel log in the log text browser."));
|
||||
});
|
||||
//
|
||||
connect(ConnectionManager, &QvConfigHandler::OnConnected, this, &MainWindow::OnConnected);
|
||||
connect(ConnectionManager, &QvConfigHandler::OnDisconnected, this, &MainWindow::OnDisconnected);
|
||||
connect(ConnectionManager, &QvConfigHandler::OnStatsAvailable, this, &MainWindow::OnStatsAvailable);
|
||||
connect(ConnectionManager, &QvConfigHandler::OnVCoreLogAvailable, this, &MainWindow::OnVCoreLogAvailable);
|
||||
connect(ConnectionManager, &QvConfigHandler::OnKernelLogAvailable, this, &MainWindow::OnVCoreLogAvailable);
|
||||
//
|
||||
connect(ConnectionManager, &QvConfigHandler::OnConnectionDeleted, this, &MainWindow::OnConnectionDeleted);
|
||||
connect(ConnectionManager, &QvConfigHandler::OnConnectionCreated, this, &MainWindow::OnConnectionCreated);
|
||||
@ -229,7 +230,6 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
||||
connectionListRCM_Menu->addAction(action_RCM_Delete);
|
||||
connect(action_RCM_Start, &QAction::triggered, this, &MainWindow::on_action_StartThis_triggered);
|
||||
connect(action_RCM_SetAutoConnection, &QAction::triggered, this, &MainWindow::on_action_RCM_SetAutoConnection_triggered);
|
||||
|
||||
connect(action_RCM_Edit, &QAction::triggered, this, &MainWindow::on_action_RCM_EditThis_triggered);
|
||||
connect(action_RCM_EditJson, &QAction::triggered, this, &MainWindow::on_action_RCM_EditAsJson_triggered);
|
||||
connect(action_RCM_EditComplex, &QAction::triggered, this, &MainWindow::on_action_RCM_EditAsComplex_triggered);
|
||||
@ -357,6 +357,13 @@ void MainWindow::keyPressEvent(QKeyEvent *e)
|
||||
this->close();
|
||||
}
|
||||
}
|
||||
else if (e->modifiers() & Qt::ControlModifier && e->key() == Qt::Key_Q)
|
||||
{
|
||||
if (QvMessageBoxAsk(this, tr("Quit Qv2ray"), tr("Are you sure to exit Qv2ray?"), QMessageBox::No) == QMessageBox::Yes)
|
||||
{
|
||||
ExitQv2ray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::keyReleaseEvent(QKeyEvent *e)
|
||||
@ -383,11 +390,6 @@ void MainWindow::on_action_StartThis_triggered()
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
{
|
||||
if (GlobalConfig.inboundConfig.pacConfig.enablePAC && pacServer != nullptr && pacServer->isRunning())
|
||||
{
|
||||
// Wait for PAC server to finish.
|
||||
pacServer->wait();
|
||||
}
|
||||
hTray.hide();
|
||||
}
|
||||
|
||||
@ -478,10 +480,16 @@ void MainWindow::on_connectionListWidget_customContextMenuRequested(const QPoint
|
||||
auto item = connectionListWidget->itemAt(connectionListWidget->mapFromGlobal(_pos));
|
||||
if (item != nullptr)
|
||||
{
|
||||
if (GetItemWidget(item)->IsConnection())
|
||||
{
|
||||
connectionListRCM_Menu->popup(_pos);
|
||||
}
|
||||
bool isConnection = GetItemWidget(item)->IsConnection();
|
||||
// Disable connection-specific settings.
|
||||
action_RCM_Start->setEnabled(isConnection);
|
||||
action_RCM_SetAutoConnection->setEnabled(isConnection);
|
||||
action_RCM_Edit->setEnabled(isConnection);
|
||||
action_RCM_EditJson->setEnabled(isConnection);
|
||||
action_RCM_EditComplex->setEnabled(isConnection);
|
||||
action_RCM_Rename->setEnabled(isConnection);
|
||||
action_RCM_Duplicate->setEnabled(isConnection);
|
||||
connectionListRCM_Menu->popup(_pos);
|
||||
}
|
||||
}
|
||||
|
||||
@ -492,9 +500,16 @@ void MainWindow::on_action_RCM_DeleteThese_triggered()
|
||||
for (auto item : connectionListWidget->selectedItems())
|
||||
{
|
||||
auto widget = GetItemWidget(item);
|
||||
if (widget->IsConnection())
|
||||
if (widget)
|
||||
{
|
||||
connlist.append(get<1>(widget->Identifier()));
|
||||
if (widget->IsConnection())
|
||||
{
|
||||
connlist.append(get<1>(widget->Identifier()));
|
||||
}
|
||||
else
|
||||
{
|
||||
connlist.append(ConnectionManager->GetGroupMetaObject(get<0>(widget->Identifier())).connections);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -568,13 +583,17 @@ void MainWindow::on_connectionListWidget_itemDoubleClicked(QTreeWidgetItem *item
|
||||
void MainWindow::OnDisconnected(const ConnectionId &id)
|
||||
{
|
||||
Q_UNUSED(id)
|
||||
hTray.setIcon(Q_TRAYICON("tray.png"));
|
||||
tray_action_Start->setEnabled(true);
|
||||
tray_action_Stop->setEnabled(false);
|
||||
tray_action_Restart->setEnabled(false);
|
||||
tray_SystemProxyMenu->setEnabled(false);
|
||||
lastConnectedId = id;
|
||||
locateBtn->setEnabled(false);
|
||||
this->hTray.showMessage("Qv2ray", tr("Disconnected from: ") + GetDisplayName(id), this->windowIcon());
|
||||
if (!GlobalConfig.uiConfig.quietMode)
|
||||
{
|
||||
this->hTray.showMessage("Qv2ray", tr("Disconnected from: ") + GetDisplayName(id), this->windowIcon());
|
||||
}
|
||||
hTray.setToolTip(TRAY_TOOLTIP_PREFIX);
|
||||
netspeedLabel->setText("0.00 B/s" NEWLINE "0.00 B/s");
|
||||
dataamountLabel->setText("0.00 B" NEWLINE "0.00 B");
|
||||
@ -583,16 +602,12 @@ void MainWindow::OnDisconnected(const ConnectionId &id)
|
||||
{
|
||||
ClearSystemProxy();
|
||||
}
|
||||
|
||||
if (GlobalConfig.inboundConfig.pacConfig.enablePAC)
|
||||
{
|
||||
pacServer->stopServer();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::OnConnected(const ConnectionId &id)
|
||||
{
|
||||
Q_UNUSED(id)
|
||||
hTray.setIcon(Q_TRAYICON("tray-connected.png"));
|
||||
tray_action_Start->setEnabled(false);
|
||||
tray_action_Stop->setEnabled(true);
|
||||
tray_action_Restart->setEnabled(true);
|
||||
@ -601,70 +616,14 @@ void MainWindow::OnConnected(const ConnectionId &id)
|
||||
locateBtn->setEnabled(true);
|
||||
on_clearlogButton_clicked();
|
||||
auto name = GetDisplayName(id);
|
||||
this->hTray.showMessage("Qv2ray", tr("Connected: ") + name, this->windowIcon());
|
||||
if (!GlobalConfig.uiConfig.quietMode)
|
||||
{
|
||||
this->hTray.showMessage("Qv2ray", tr("Connected: ") + name, this->windowIcon());
|
||||
}
|
||||
hTray.setToolTip(TRAY_TOOLTIP_PREFIX NEWLINE + tr("Connected: ") + name);
|
||||
connetionStatusLabel->setText(tr("Connected: ") + name);
|
||||
//
|
||||
ConnectionManager->StartLatencyTest(id);
|
||||
bool usePAC = GlobalConfig.inboundConfig.pacConfig.enablePAC;
|
||||
bool pacUseSocks = GlobalConfig.inboundConfig.pacConfig.useSocksProxy;
|
||||
bool httpEnabled = GlobalConfig.inboundConfig.useHTTP;
|
||||
bool socksEnabled = GlobalConfig.inboundConfig.useSocks;
|
||||
|
||||
if (usePAC)
|
||||
{
|
||||
bool canStartPAC = true;
|
||||
QString pacProxyString; // Something like this --> SOCKS5 127.0.0.1:1080; SOCKS
|
||||
// 127.0.0.1:1080; DIRECT; http://proxy:8080
|
||||
auto pacIP = GlobalConfig.inboundConfig.pacConfig.localIP;
|
||||
|
||||
if (pacIP.isEmpty())
|
||||
{
|
||||
LOG(MODULE_PROXY, "PAC Local IP is empty, default to 127.0.0.1")
|
||||
pacIP = "127.0.0.1";
|
||||
}
|
||||
|
||||
if (pacUseSocks)
|
||||
{
|
||||
if (socksEnabled)
|
||||
{
|
||||
pacProxyString = "SOCKS5 " + pacIP + ":" + QSTRN(GlobalConfig.inboundConfig.socks_port);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(MODULE_UI, "PAC is using SOCKS, but it is not enabled")
|
||||
QvMessageBoxWarn(this, tr("Configuring PAC"),
|
||||
tr("Could not start PAC server as it is configured to use SOCKS, but it is not enabled"));
|
||||
canStartPAC = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (httpEnabled)
|
||||
{
|
||||
pacProxyString = "PROXY " + pacIP + ":" + QSTRN(GlobalConfig.inboundConfig.http_port);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(MODULE_UI, "PAC is using HTTP, but it is not enabled")
|
||||
QvMessageBoxWarn(this, tr("Configuring PAC"),
|
||||
tr("Could not start PAC server as it is configured to use HTTP, but it is not enabled"));
|
||||
canStartPAC = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (canStartPAC)
|
||||
{
|
||||
pacServer = new PACServer(this);
|
||||
pacServer->setPACProxyString(pacProxyString);
|
||||
pacServer->start();
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(MODULE_PROXY, "Not starting PAC due to previous error.")
|
||||
}
|
||||
}
|
||||
|
||||
if (GlobalConfig.inboundConfig.setSystemProxy)
|
||||
{
|
||||
MWSetSystemProxy();
|
||||
@ -860,7 +819,7 @@ void MainWindow::OnGroupDeleted(const GroupId &id, const QList<ConnectionId> &co
|
||||
|
||||
void MainWindow::on_locateBtn_clicked()
|
||||
{
|
||||
auto id = ConnectionManager->CurrentConnection();
|
||||
auto id = KernelInstance->CurrentConnection();
|
||||
if (id != NullConnectionId)
|
||||
{
|
||||
connectionListWidget->setCurrentItem(connectionNodes.value(id).get());
|
||||
@ -964,7 +923,10 @@ void MainWindow::on_action_RCM_SetAutoConnection_triggered()
|
||||
auto widget = GetItemWidget(current);
|
||||
auto &conn = get<1>(widget->Identifier());
|
||||
GlobalConfig.autoStartId = conn.toString();
|
||||
hTray.showMessage(tr("Set auto connection"), tr("Set %1 as auto connect.").arg(GetDisplayName(conn)));
|
||||
if (!GlobalConfig.uiConfig.quietMode)
|
||||
{
|
||||
hTray.showMessage(tr("Set auto connection"), tr("Set %1 as auto connect.").arg(GetDisplayName(conn)));
|
||||
}
|
||||
SaveGlobalSettings();
|
||||
}
|
||||
}
|
||||
@ -975,6 +937,17 @@ void MainWindow::on_action_RCM_ClearUsage_triggered()
|
||||
if (current != nullptr)
|
||||
{
|
||||
auto widget = GetItemWidget(current);
|
||||
ConnectionManager->ClearConnectionUsage(get<1>(widget->Identifier()));
|
||||
if (widget)
|
||||
{
|
||||
if (widget->IsConnection())
|
||||
ConnectionManager->ClearConnectionUsage(get<1>(widget->Identifier()));
|
||||
else
|
||||
ConnectionManager->ClearGroupUsage(get<0>(widget->Identifier()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::on_pluginsBtn_clicked()
|
||||
{
|
||||
PluginManageWindow(this).exec();
|
||||
}
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
#include "common/HTTPRequestHelper.hpp"
|
||||
#include "common/LogHighlighter.hpp"
|
||||
#include "components/pac/QvPACHandler.hpp"
|
||||
#include "components/speedchart/speedwidget.hpp"
|
||||
#include "core/handler/ConfigHandler.hpp"
|
||||
#include "ui/messaging/QvMessageBus.hpp"
|
||||
@ -61,6 +60,8 @@ class MainWindow
|
||||
void on_connectionListWidget_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous);
|
||||
void on_masterLogBrowser_textChanged();
|
||||
|
||||
void on_pluginsBtn_clicked();
|
||||
|
||||
private:
|
||||
void on_actionExit_triggered();
|
||||
void on_action_StartThis_triggered();
|
||||
@ -108,7 +109,6 @@ class MainWindow
|
||||
// Charts
|
||||
SpeedWidget *speedChartWidget;
|
||||
QSystemTrayIcon hTray;
|
||||
PACServer *pacServer;
|
||||
SyntaxHighlighter *vCoreLogHighlighter;
|
||||
ConnectionInfoWidget *infoWidget;
|
||||
//
|
||||
|
@ -22,7 +22,7 @@
|
||||
<widget class="QWidget" name="centralWidget">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,1,0">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,0,0,0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,0,0,0,0">
|
||||
<property name="spacing">
|
||||
<number>5</number>
|
||||
</property>
|
||||
@ -33,6 +33,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pluginsBtn">
|
||||
<property name="text">
|
||||
<string>Plugins</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="preferencesBtn">
|
||||
<property name="text">
|
||||
@ -553,7 +560,6 @@
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<tabstops>
|
||||
<tabstop>subsButton</tabstop>
|
||||
<tabstop>preferencesBtn</tabstop>
|
||||
<tabstop>connectionListWidget</tabstop>
|
||||
<tabstop>importConfigButton</tabstop>
|
||||
</tabstops>
|
||||
|
@ -4,79 +4,39 @@
|
||||
|
||||
void MainWindow::MWSetSystemProxy()
|
||||
{
|
||||
bool usePAC = GlobalConfig.inboundConfig.pacConfig.enablePAC;
|
||||
bool pacUseSocks = GlobalConfig.inboundConfig.pacConfig.useSocksProxy;
|
||||
bool httpEnabled = GlobalConfig.inboundConfig.useHTTP;
|
||||
bool socksEnabled = GlobalConfig.inboundConfig.useSocks;
|
||||
//
|
||||
bool isComplex = IsComplexConfig(ConnectionManager->CurrentConnection());
|
||||
auto inboundPorts = KernelInstance->InboundPorts();
|
||||
bool httpEnabled = inboundPorts.contains("http");
|
||||
bool socksEnabled = inboundPorts.contains("socks");
|
||||
auto httpPort = inboundPorts["http"];
|
||||
auto socksPort = inboundPorts["socks"];
|
||||
|
||||
if (!isComplex)
|
||||
QString proxyAddress;
|
||||
|
||||
if (httpEnabled || socksEnabled)
|
||||
{
|
||||
// Is simple config and we will try to set system proxy.
|
||||
LOG(MODULE_UI, "Preparing to set system proxy")
|
||||
//
|
||||
QString proxyAddress;
|
||||
bool canSetSystemProxy = true;
|
||||
|
||||
if (usePAC)
|
||||
proxyAddress = "127.0.0.1";
|
||||
SetSystemProxy(proxyAddress, httpPort, socksPort);
|
||||
hTray.setIcon(Q_TRAYICON("tray-systemproxy.png"));
|
||||
if (!GlobalConfig.uiConfig.quietMode)
|
||||
{
|
||||
if ((httpEnabled && !pacUseSocks) || (socksEnabled && pacUseSocks))
|
||||
{
|
||||
// If we use PAC and socks/http are properly configured for PAC
|
||||
LOG(MODULE_PROXY, "System proxy uses PAC")
|
||||
proxyAddress = "http://" + GlobalConfig.inboundConfig.listenip + ":" + QSTRN(GlobalConfig.inboundConfig.pacConfig.port) + "/pac";
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not properly configured
|
||||
LOG(MODULE_PROXY, "Failed to process pac due to following reasons:")
|
||||
LOG(MODULE_PROXY, " --> PAC is configured to use socks but socks is not enabled.")
|
||||
LOG(MODULE_PROXY, " --> PAC is configuted to use http but http is not enabled.")
|
||||
QvMessageBoxWarn(this, tr("PAC Processing Failed"),
|
||||
tr("HTTP or SOCKS inbound is not properly configured for PAC") + NEWLINE +
|
||||
tr("Qv2ray will continue, but will not set system proxy."));
|
||||
canSetSystemProxy = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not using PAC
|
||||
if (httpEnabled || socksEnabled)
|
||||
{
|
||||
// Not use PAC, System proxy should use HTTP or SOCKS
|
||||
LOG(MODULE_PROXY, "Setting up system proxy.")
|
||||
// A 'proxy host' should be a host WITHOUT `http://` uri scheme
|
||||
proxyAddress = "localhost";
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(MODULE_PROXY, "Neither of HTTP nor SOCKS is enabled, cannot set system proxy.")
|
||||
QvMessageBoxWarn(this, tr("Cannot set system proxy"), tr("Both HTTP and SOCKS inbounds are not enabled"));
|
||||
canSetSystemProxy = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (canSetSystemProxy)
|
||||
{
|
||||
LOG(MODULE_UI, "Setting system proxy for simple config.")
|
||||
auto httpPort = GlobalConfig.inboundConfig.useHTTP ? GlobalConfig.inboundConfig.http_port : 0;
|
||||
auto socksPort = GlobalConfig.inboundConfig.useSocks ? GlobalConfig.inboundConfig.socks_port : 0;
|
||||
//
|
||||
SetSystemProxy(proxyAddress, httpPort, socksPort, usePAC);
|
||||
hTray.showMessage("Qv2ray", tr("System proxy configured."));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
hTray.showMessage("Qv2ray", tr("Didn't set proxy for complex config."), windowIcon());
|
||||
LOG(MODULE_PROXY, "Neither of HTTP nor SOCKS is enabled, cannot set system proxy.")
|
||||
QvMessageBoxWarn(this, tr("Cannot set system proxy"), tr("Both HTTP and SOCKS inbounds are not enabled"));
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::MWClearSystemProxy()
|
||||
{
|
||||
ClearSystemProxy();
|
||||
hTray.showMessage("Qv2ray", tr("System proxy removed."));
|
||||
hTray.setIcon(KernelInstance->CurrentConnection() == NullConnectionId ? Q_TRAYICON("tray.png") : Q_TRAYICON("tray-connected.png"));
|
||||
if (!GlobalConfig.uiConfig.quietMode)
|
||||
{
|
||||
hTray.showMessage("Qv2ray", tr("System proxy removed."));
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::CheckSubscriptionsUpdate()
|
||||
@ -84,22 +44,26 @@ void MainWindow::CheckSubscriptionsUpdate()
|
||||
QStringList updateList;
|
||||
|
||||
auto subscriptions = ConnectionManager->Subscriptions();
|
||||
for (auto entry : subscriptions)
|
||||
for (const auto &entry : subscriptions)
|
||||
{
|
||||
auto into = ConnectionManager->GetGroupMetaObject(entry);
|
||||
const auto info = ConnectionManager->GetGroupMetaObject(entry);
|
||||
//
|
||||
auto lastRenewDate = QDateTime::fromTime_t(into.lastUpdated);
|
||||
auto renewTime = lastRenewDate.addSecs(into.updateInterval * 86400);
|
||||
// The update is ignored.
|
||||
if (info.updateInterval == 0)
|
||||
continue;
|
||||
//
|
||||
const auto &lastRenewDate = QDateTime::fromTime_t(info.lastUpdated);
|
||||
const auto &renewTime = lastRenewDate.addSecs(info.updateInterval * 86400);
|
||||
LOG(MODULE_SUBSCRIPTION, //
|
||||
"Subscription \"" + entry.toString() + "\": " + //
|
||||
"Subscription \"" + info.displayName + "\": " + //
|
||||
NEWLINE + " --> Last renewal time: " + lastRenewDate.toString() + //
|
||||
NEWLINE + " --> Renew interval: " + QSTRN(into.updateInterval) + //
|
||||
NEWLINE + " --> Renew interval: " + QSTRN(info.updateInterval) + //
|
||||
NEWLINE + " --> Ideal renew time: " + renewTime.toString()) //
|
||||
|
||||
if (renewTime <= QDateTime::currentDateTime())
|
||||
{
|
||||
LOG(MODULE_SUBSCRIPTION, "Subscription: " + entry.toString() + " needs to be updated.")
|
||||
updateList.append(entry.toString());
|
||||
LOG(MODULE_SUBSCRIPTION, "Subscription: " + info.displayName + " needs to be updated.")
|
||||
updateList.append(info.displayName);
|
||||
}
|
||||
}
|
||||
|
||||
|
132
src/ui/w_PluginManager.cpp
Normal file
@ -0,0 +1,132 @@
|
||||
#include "w_PluginManager.hpp"
|
||||
|
||||
#include "common/QvHelpers.hpp"
|
||||
#include "components/plugins/QvPluginHost.hpp"
|
||||
#include "core/settings/SettingsBackend.hpp"
|
||||
#include "ui/editors/w_JsonEditor.hpp"
|
||||
|
||||
PluginManageWindow::PluginManageWindow(QWidget *parent) : QDialog(parent)
|
||||
{
|
||||
setupUi(this);
|
||||
for (auto &plugin : PluginHost->AvailablePlugins())
|
||||
{
|
||||
const auto &info = PluginHost->GetPluginMetadata(plugin);
|
||||
auto item = new QListWidgetItem(pluginListWidget);
|
||||
item->setCheckState(PluginHost->GetPluginEnableState(info.InternalName) ? Qt::Checked : Qt::Unchecked);
|
||||
item->setData(Qt::UserRole, info.InternalName);
|
||||
item->setText(info.Name + " (" + (PluginHost->GetPluginLoadState(info.InternalName) ? tr("Loaded") : tr("Not loaded")) + ")");
|
||||
pluginListWidget->addItem(item);
|
||||
}
|
||||
isLoading = false;
|
||||
}
|
||||
|
||||
PluginManageWindow::~PluginManageWindow()
|
||||
{
|
||||
}
|
||||
|
||||
void PluginManageWindow::on_pluginListWidget_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous)
|
||||
{
|
||||
Q_UNUSED(previous)
|
||||
auto &info = PluginHost->GetPluginMetadata(current->data(Qt::UserRole).toString());
|
||||
|
||||
pluginIconLabel->setPixmap(info.Icon.pixmap(pluginIconLabel->size() * devicePixelRatio()));
|
||||
//
|
||||
pluginNameLabel->setText(info.Name);
|
||||
pluginAuthorLabel->setText(info.Author);
|
||||
pluginDescriptionLabel->setText(info.Description);
|
||||
pluginLibPathLabel->setText(PluginHost->GetPluginLibraryPath(info.InternalName));
|
||||
pluginStateLabel->setText(PluginHost->GetPluginLoadState(info.InternalName) ? tr("Loaded") : tr("Not loaded"));
|
||||
pluginTypeLabel->setText(GetPluginTypeString(info.SpecialPluginType));
|
||||
pluginHookTypeLabel->setText(GetPluginCapabilityString(info.Capabilities));
|
||||
//
|
||||
if (!current)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (settingsWidget || settingsWidget.get())
|
||||
{
|
||||
pluginSettingsLayout->removeWidget(settingsWidget.get());
|
||||
settingsWidget.reset();
|
||||
}
|
||||
if (!PluginHost->GetPluginLoadState(info.InternalName))
|
||||
{
|
||||
pluginUnloadLabel->setVisible(true);
|
||||
pluginUnloadLabel->setText(tr("Plugin Not Loaded"));
|
||||
return;
|
||||
}
|
||||
settingsWidget = PluginHost->GetPluginSettingsWidget(info.InternalName);
|
||||
if (settingsWidget)
|
||||
{
|
||||
pluginUnloadLabel->setVisible(false);
|
||||
settingsWidget.get()->setParent(this);
|
||||
pluginSettingsLayout->addWidget(settingsWidget.get());
|
||||
}
|
||||
else
|
||||
{
|
||||
pluginUnloadLabel->setVisible(true);
|
||||
pluginUnloadLabel->setText(tr("Plugin does not have settings widget."));
|
||||
}
|
||||
}
|
||||
|
||||
void PluginManageWindow::on_pluginListWidget_itemClicked(QListWidgetItem *item)
|
||||
{
|
||||
Q_UNUSED(item)
|
||||
// on_pluginListWidget_currentItemChanged(item, nullptr);
|
||||
}
|
||||
|
||||
void PluginManageWindow::on_pluginListWidget_itemChanged(QListWidgetItem *item)
|
||||
{
|
||||
if (isLoading)
|
||||
return;
|
||||
bool isEnabled = item->checkState() == Qt::Checked;
|
||||
auto pluginInternalName = item->data(Qt::UserRole).toString();
|
||||
PluginHost->SetPluginEnableState(pluginInternalName, isEnabled);
|
||||
auto &info = PluginHost->GetPluginMetadata(pluginInternalName);
|
||||
item->setText(info.Name + " (" + (PluginHost->GetPluginLoadState(info.InternalName) ? tr("Loaded") : tr("Not loaded")) + ")");
|
||||
//
|
||||
if (!isEnabled)
|
||||
{
|
||||
QvMessageBoxInfo(this, tr("Disabling a plugin"), tr("This plugin will keep loaded until the next time Qv2ray starts."));
|
||||
}
|
||||
}
|
||||
|
||||
void PluginManageWindow::on_pluginEditSettingsJsonBtn_clicked()
|
||||
{
|
||||
if (const auto ¤t = pluginListWidget->currentItem(); current != nullptr)
|
||||
{
|
||||
const auto &info = PluginHost->GetPluginMetadata(current->data(Qt::UserRole).toString());
|
||||
if (!PluginHost->GetPluginLoadState(info.InternalName))
|
||||
{
|
||||
QvMessageBoxWarn(this, tr("Plugin not loaded"), tr("This plugin is not loaded, please enable or reload the plugin to continue."));
|
||||
return;
|
||||
}
|
||||
JsonEditor w(PluginHost->GetPluginSettings(info.InternalName));
|
||||
auto newConf = w.OpenEditor();
|
||||
if (w.result() == QDialog::Accepted)
|
||||
{
|
||||
PluginHost->SetPluginSettings(info.InternalName, newConf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PluginManageWindow::on_pluginListWidget_itemSelectionChanged()
|
||||
{
|
||||
auto needEnable = !pluginListWidget->selectedItems().isEmpty();
|
||||
pluginEditSettingsJsonBtn->setEnabled(needEnable);
|
||||
}
|
||||
|
||||
void PluginManageWindow::on_openPluginFolder_clicked()
|
||||
{
|
||||
QDir pluginPath(QV2RAY_CONFIG_DIR + "plugins/");
|
||||
if (!pluginPath.exists())
|
||||
{
|
||||
pluginPath.mkpath(QV2RAY_CONFIG_DIR + "plugins/");
|
||||
}
|
||||
QDesktopServices::openUrl(QUrl(pluginPath.absolutePath()));
|
||||
}
|
||||
|
||||
void PluginManageWindow::on_toolButton_clicked()
|
||||
{
|
||||
auto address = GlobalConfig.uiConfig.language.contains("zh") ? "https://qv2ray.github.io/plugins/" : "https://qv2ray.github.io/en/plugins/";
|
||||
QDesktopServices::openUrl(QUrl(address));
|
||||
}
|
33
src/ui/w_PluginManager.hpp
Normal file
@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include "ui_w_PluginManager.h"
|
||||
|
||||
#include <QDialog>
|
||||
#include <memory>
|
||||
|
||||
class PluginManageWindow
|
||||
: public QDialog
|
||||
, private Ui::w_PluginManager
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit PluginManageWindow(QWidget *parent = nullptr);
|
||||
~PluginManageWindow();
|
||||
private slots:
|
||||
void on_pluginListWidget_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous);
|
||||
void on_pluginListWidget_itemClicked(QListWidgetItem *item);
|
||||
void on_pluginListWidget_itemChanged(QListWidgetItem *item);
|
||||
|
||||
void on_pluginEditSettingsJsonBtn_clicked();
|
||||
|
||||
void on_pluginListWidget_itemSelectionChanged();
|
||||
|
||||
void on_openPluginFolder_clicked();
|
||||
|
||||
void on_toolButton_clicked();
|
||||
|
||||
private:
|
||||
std::unique_ptr<QWidget> settingsWidget;
|
||||
bool isLoading = true;
|
||||
};
|
356
src/ui/w_PluginManager.ui
Normal file
@ -0,0 +1,356 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>w_PluginManager</class>
|
||||
<widget class="QDialog" name="w_PluginManager">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>757</width>
|
||||
<height>518</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Plugin Manager</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout" columnstretch="0,1">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Plugins</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QListWidget" name="pluginListWidget"/>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="openPluginFolder">
|
||||
<property name="text">
|
||||
<string>Open Local Plugin Folder</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="toolButton">
|
||||
<property name="toolTip">
|
||||
<string>Online help about plugins</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>?</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1" rowspan="3">
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="tabWidgetPage1">
|
||||
<attribute name="title">
|
||||
<string>Plugin Metadata</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout_2" columnstretch="1,0">
|
||||
<item row="0" column="0">
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="pluginNameLabel">
|
||||
<property name="cursor">
|
||||
<cursorShape>IBeamCursor</cursorShape>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Author</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="pluginAuthorLabel">
|
||||
<property name="cursor">
|
||||
<cursorShape>IBeamCursor</cursorShape>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string>Description</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="pluginDescriptionLabel">
|
||||
<property name="cursor">
|
||||
<cursorShape>IBeamCursor</cursorShape>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="text">
|
||||
<string>Library Path</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLabel" name="pluginLibPathLabel">
|
||||
<property name="cursor">
|
||||
<cursorShape>IBeamCursor</cursorShape>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>State</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QLabel" name="pluginStateLabel">
|
||||
<property name="cursor">
|
||||
<cursorShape>IBeamCursor</cursorShape>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Capability</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QLabel" name="pluginHookTypeLabel">
|
||||
<property name="cursor">
|
||||
<cursorShape>IBeamCursor</cursorShape>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="label_9">
|
||||
<property name="text">
|
||||
<string>Special Type</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<widget class="QLabel" name="pluginTypeLabel">
|
||||
<property name="cursor">
|
||||
<cursorShape>IBeamCursor</cursorShape>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="pluginIconLabel">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>64</width>
|
||||
<height>64</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab">
|
||||
<attribute name="title">
|
||||
<string>Plugin Settings</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2" stretch="1,0">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="pluginSettingsLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="pluginUnloadLabel">
|
||||
<property name="text">
|
||||
<string>Plugin Not Loaded</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pluginEditSettingsJsonBtn">
|
||||
<property name="text">
|
||||
<string>Manually Edit Settings</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>w_PluginManager</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>w_PluginManager</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
@ -4,10 +4,11 @@
|
||||
#include "common/QvHelpers.hpp"
|
||||
#include "common/QvTranslator.hpp"
|
||||
#include "components/autolaunch/QvAutoLaunch.hpp"
|
||||
#include "components/plugins/interface/QvPluginInterface.hpp"
|
||||
#include "components/plugins/toolbar/QvToolbar.hpp"
|
||||
#include "core/connection/ConnectionIO.hpp"
|
||||
#include "core/handler/ConfigHandler.hpp"
|
||||
#include "core/kernel/KernelInteractions.hpp"
|
||||
#include "core/kernel/V2rayKernelInteractions.hpp"
|
||||
#include "core/settings/SettingsBackend.hpp"
|
||||
#include "ui/widgets/RouteSettingsMatrix.hpp"
|
||||
|
||||
@ -69,6 +70,7 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent), Current
|
||||
qvBuildInfo->setText(QV2RAY_BUILD_INFO);
|
||||
qvBuildExInfo->setText(QV2RAY_BUILD_EXTRA_INFO);
|
||||
qvBuildTime->setText(__DATE__ " " __TIME__);
|
||||
qvPluginInterfaceVersionLabel->setText(tr("Version: %1").arg(QSTRN(QV2RAY_PLUGIN_INTERFACE_VERSION)));
|
||||
//
|
||||
// Deep copy
|
||||
CurrentConfig = GlobalConfig;
|
||||
@ -87,14 +89,6 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent), Current
|
||||
//
|
||||
//
|
||||
listenIPTxt->setText(CurrentConfig.inboundConfig.listenip);
|
||||
bool pacEnabled = CurrentConfig.inboundConfig.pacConfig.enablePAC;
|
||||
pacGroupBox->setChecked(pacEnabled);
|
||||
setSysProxyCB->setChecked(CurrentConfig.inboundConfig.setSystemProxy);
|
||||
//
|
||||
// PAC
|
||||
pacPortSB->setValue(CurrentConfig.inboundConfig.pacConfig.port);
|
||||
pacProxyTxt->setText(CurrentConfig.inboundConfig.pacConfig.localIP);
|
||||
pacProxyCB->setCurrentIndex(CurrentConfig.inboundConfig.pacConfig.useSocksProxy ? 1 : 0);
|
||||
//
|
||||
bool have_http = CurrentConfig.inboundConfig.useHTTP;
|
||||
httpGroupBox->setChecked(have_http);
|
||||
@ -134,12 +128,26 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent), Current
|
||||
//
|
||||
localDNSCb->setChecked(CurrentConfig.connectionConfig.withLocalDNS);
|
||||
//
|
||||
pluginKernelV2rayIntegrationCB->setChecked(CurrentConfig.pluginConfig.v2rayIntegration);
|
||||
pluginKernelPortAllocateCB->setValue(CurrentConfig.pluginConfig.portAllocationStart);
|
||||
//
|
||||
qvProxyPortCB->setValue(CurrentConfig.networkConfig.port);
|
||||
qvProxyAddressTxt->setText(CurrentConfig.networkConfig.address);
|
||||
qvProxyTypeCombo->setCurrentText(CurrentConfig.networkConfig.type);
|
||||
qvNetworkUATxt->setText(CurrentConfig.networkConfig.userAgent);
|
||||
qvUseProxyCB->setChecked(CurrentConfig.networkConfig.useCustomProxy);
|
||||
//
|
||||
quietModeCB->setChecked(CurrentConfig.uiConfig.quietMode);
|
||||
//
|
||||
// Advanced config.
|
||||
setAllowInsecureCB->setChecked(CurrentConfig.advancedConfig.setAllowInsecure);
|
||||
setAllowInsecureCiphersCB->setChecked(CurrentConfig.advancedConfig.setAllowInsecureCiphers);
|
||||
setTestLatenctCB->setChecked(CurrentConfig.advancedConfig.testLatencyPeriodcally);
|
||||
//
|
||||
DNSListTxt->clear();
|
||||
|
||||
for (auto dnsStr : CurrentConfig.connectionConfig.dnsList)
|
||||
{
|
||||
auto str = dnsStr.trimmed();
|
||||
|
||||
if (!str.isEmpty())
|
||||
{
|
||||
DNSListTxt->appendPlainText(str);
|
||||
@ -150,11 +158,11 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent), Current
|
||||
updateSettingsGroupBox->setEnabled(false);
|
||||
updateSettingsGroupBox->setToolTip(tr("Update is disabled by your vendor."));
|
||||
#endif
|
||||
|
||||
//
|
||||
updateChannelCombo->setCurrentIndex(CurrentConfig.updateConfig.updateChannel);
|
||||
cancelIgnoreVersionBtn->setEnabled(!CurrentConfig.updateConfig.ignoredVersion.isEmpty());
|
||||
ignoredNextVersion->setText(CurrentConfig.updateConfig.ignoredVersion);
|
||||
|
||||
//
|
||||
for (auto i = 0; i < CurrentConfig.toolBarConfig.Pages.size(); i++)
|
||||
{
|
||||
nsBarPagesList->addItem(tr("Page") + QSTRN(i + 1) + ": " + QSTRN(CurrentConfig.toolBarConfig.Pages[i].Lines.size()) + " " +
|
||||
@ -213,8 +221,7 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent), Current
|
||||
//
|
||||
maxLogLinesSB->setValue(CurrentConfig.uiConfig.maximumLogLines);
|
||||
//
|
||||
pacListenAddrLabel->setText("http://" + (pacProxyTxt->text().isEmpty() ? "127.0.0.1" : pacProxyTxt->text()) + ":" +
|
||||
QSTRN(pacPortSB->value()) + "/pac");
|
||||
setSysProxyCB->setChecked(CurrentConfig.inboundConfig.setSystemProxy);
|
||||
//
|
||||
finishedLoading = true;
|
||||
routeSettingsWidget = new RouteSettingsMatrixWidget(CurrentConfig.kernelConfig.AssetsPath(), this);
|
||||
@ -256,12 +263,6 @@ void PreferencesWindow::on_buttonBox_accepted()
|
||||
ports << CurrentConfig.inboundConfig.socks_port;
|
||||
}
|
||||
|
||||
if (CurrentConfig.inboundConfig.pacConfig.enablePAC)
|
||||
{
|
||||
size++;
|
||||
ports << CurrentConfig.inboundConfig.pacConfig.port;
|
||||
}
|
||||
|
||||
if (!StartupOption.noAPI)
|
||||
{
|
||||
size++;
|
||||
@ -932,84 +933,6 @@ void PreferencesWindow::on_darkTrayCB_stateChanged(int arg1)
|
||||
CurrentConfig.uiConfig.useDarkTrayIcon = arg1 == Qt::Checked;
|
||||
}
|
||||
|
||||
void PreferencesWindow::on_pacGoBtn_clicked()
|
||||
{
|
||||
LOADINGCHECK
|
||||
QString gfwLocation;
|
||||
QString fileContent;
|
||||
pacGoBtn->setEnabled(false);
|
||||
gfwListCB->setEnabled(false);
|
||||
QvHttpRequestHelper request;
|
||||
LOG(MODULE_PROXY, "Downloading GFWList file.")
|
||||
bool withProxy = getGFWListWithProxyCB->isChecked();
|
||||
|
||||
switch (gfwListCB->currentIndex())
|
||||
{
|
||||
case 0:
|
||||
gfwLocation = "https://gitlab.com/gfwlist/gfwlist/raw/master/gfwlist.txt";
|
||||
fileContent = QString::fromUtf8(request.syncget(gfwLocation, withProxy));
|
||||
break;
|
||||
|
||||
case 1:
|
||||
gfwLocation = "https://pagure.io/gfwlist/raw/master/f/gfwlist.txt";
|
||||
fileContent = QString::fromUtf8(request.syncget(gfwLocation, withProxy));
|
||||
break;
|
||||
|
||||
case 2:
|
||||
gfwLocation = "http://repo.or.cz/gfwlist.git/blob_plain/HEAD:/gfwlist.txt";
|
||||
fileContent = QString::fromUtf8(request.syncget(gfwLocation, withProxy));
|
||||
break;
|
||||
|
||||
case 3:
|
||||
gfwLocation = "https://bitbucket.org/gfwlist/gfwlist/raw/HEAD/gfwlist.txt";
|
||||
fileContent = QString::fromUtf8(request.syncget(gfwLocation, withProxy));
|
||||
break;
|
||||
|
||||
case 4:
|
||||
gfwLocation = "https://raw.githubusercontent.com/gfwlist/gfwlist/master/gfwlist.txt";
|
||||
fileContent = QString::fromUtf8(request.syncget(gfwLocation, withProxy));
|
||||
break;
|
||||
|
||||
case 5:
|
||||
gfwLocation = "https://git.tuxfamily.org/gfwlist/gfwlist.git/plain/gfwlist.txt";
|
||||
fileContent = QString::fromUtf8(request.syncget(gfwLocation, withProxy));
|
||||
break;
|
||||
|
||||
case 6:
|
||||
auto file = QFileDialog::getOpenFileName(this, tr("Select GFWList in base64"));
|
||||
|
||||
if (file.isEmpty())
|
||||
{
|
||||
QvMessageBoxWarn(this, tr("Download GFWList"), tr("Operation is cancelled."));
|
||||
return;
|
||||
}
|
||||
|
||||
fileContent = StringFromFile(file);
|
||||
break;
|
||||
}
|
||||
|
||||
LOG(MODULE_NETWORK, "Fetched: " + gfwLocation)
|
||||
QvMessageBoxInfo(this, tr("Download GFWList"), tr("Successfully downloaded GFWList."));
|
||||
pacGoBtn->setEnabled(true);
|
||||
gfwListCB->setEnabled(true);
|
||||
|
||||
if (!QDir(QV2RAY_RULES_DIR).exists())
|
||||
{
|
||||
QDir(QV2RAY_RULES_DIR).mkpath(QV2RAY_RULES_DIR);
|
||||
}
|
||||
|
||||
StringToFile(fileContent, QV2RAY_RULES_GFWLIST_PATH);
|
||||
}
|
||||
|
||||
void PreferencesWindow::on_pacPortSB_valueChanged(int arg1)
|
||||
{
|
||||
LOADINGCHECK
|
||||
NEEDRESTART
|
||||
CurrentConfig.inboundConfig.pacConfig.port = arg1;
|
||||
pacListenAddrLabel->setText("http://" + (pacProxyTxt->text().isEmpty() ? "127.0.0.1" : pacProxyTxt->text()) + ":" +
|
||||
QSTRN(pacPortSB->value()) + "/pac");
|
||||
}
|
||||
|
||||
void PreferencesWindow::on_setSysProxyCB_stateChanged(int arg1)
|
||||
{
|
||||
LOADINGCHECK
|
||||
@ -1017,28 +940,12 @@ void PreferencesWindow::on_setSysProxyCB_stateChanged(int arg1)
|
||||
CurrentConfig.inboundConfig.setSystemProxy = arg1 == Qt::Checked;
|
||||
}
|
||||
|
||||
void PreferencesWindow::on_pacProxyCB_currentIndexChanged(int index)
|
||||
{
|
||||
LOADINGCHECK
|
||||
NEEDRESTART
|
||||
// 0 -> http
|
||||
// 1 -> socks
|
||||
CurrentConfig.inboundConfig.pacConfig.useSocksProxy = index == 1;
|
||||
}
|
||||
|
||||
void PreferencesWindow::on_pushButton_clicked()
|
||||
{
|
||||
LOADINGCHECK
|
||||
QDesktopServices::openUrl(QUrl::fromUserInput(QV2RAY_RULES_DIR));
|
||||
}
|
||||
|
||||
void PreferencesWindow::on_pacProxyTxt_textEdited(const QString &arg1)
|
||||
{
|
||||
LOADINGCHECK
|
||||
NEEDRESTART
|
||||
CurrentConfig.inboundConfig.pacConfig.localIP = arg1;
|
||||
}
|
||||
|
||||
void PreferencesWindow::on_autoStartSubsCombo_currentIndexChanged(const QString &arg1)
|
||||
{
|
||||
LOADINGCHECK if (arg1.isEmpty())
|
||||
@ -1147,23 +1054,6 @@ void PreferencesWindow::on_fpPortSB_valueChanged(int arg1)
|
||||
CurrentConfig.connectionConfig.forwardProxyConfig.port = arg1;
|
||||
}
|
||||
|
||||
void PreferencesWindow::on_pacProxyTxt_textChanged(const QString &arg1)
|
||||
{
|
||||
Q_UNUSED(arg1)
|
||||
|
||||
if (IsValidIPAddress(arg1))
|
||||
{
|
||||
BLACK(pacProxyTxt)
|
||||
}
|
||||
else
|
||||
{
|
||||
RED(pacProxyTxt)
|
||||
}
|
||||
|
||||
pacListenAddrLabel->setText("http://" + (pacProxyTxt->text().isEmpty() ? "127.0.0.1" : pacProxyTxt->text()) + ":" +
|
||||
QSTRN(pacPortSB->value()) + "/pac");
|
||||
}
|
||||
|
||||
void PreferencesWindow::on_checkVCoreSettings_clicked()
|
||||
{
|
||||
auto vcorePath = vCorePathTxt->text();
|
||||
@ -1196,20 +1086,6 @@ void PreferencesWindow::on_socksGroupBox_clicked(bool checked)
|
||||
CurrentConfig.inboundConfig.useSocks = checked;
|
||||
}
|
||||
|
||||
void PreferencesWindow::on_pacGroupBox_clicked(bool checked)
|
||||
{
|
||||
LOADINGCHECK
|
||||
NEEDRESTART
|
||||
CurrentConfig.inboundConfig.pacConfig.enablePAC = checked;
|
||||
if (checked)
|
||||
{
|
||||
QvMessageBoxWarn(this, QObject::tr("Deprecated"),
|
||||
QObject::tr("PAC is now deprecated and is not encouraged to be used anymore.") + NEWLINE +
|
||||
QObject::tr("It will be removed or be provided as a plugin in the future.") + NEWLINE + NEWLINE +
|
||||
QObject::tr("PAC will still work currently, but please switch to the V2ray built-in routing as soon as possible."));
|
||||
}
|
||||
}
|
||||
|
||||
void PreferencesWindow::on_fpGroupBox_clicked(bool checked)
|
||||
{
|
||||
LOADINGCHECK
|
||||
@ -1237,3 +1113,81 @@ void PreferencesWindow::on_updateChannelCombo_currentIndexChanged(int index)
|
||||
CurrentConfig.updateConfig.updateChannel = index;
|
||||
CurrentConfig.updateConfig.ignoredVersion.clear();
|
||||
}
|
||||
|
||||
void PreferencesWindow::on_pluginKernelV2rayIntegrationCB_stateChanged(int arg1)
|
||||
{
|
||||
LOADINGCHECK
|
||||
CurrentConfig.pluginConfig.v2rayIntegration = arg1 == Qt::Checked;
|
||||
}
|
||||
|
||||
void PreferencesWindow::on_pluginKernelPortAllocateCB_valueChanged(int arg1)
|
||||
{
|
||||
LOADINGCHECK
|
||||
CurrentConfig.pluginConfig.portAllocationStart = arg1;
|
||||
}
|
||||
|
||||
void PreferencesWindow::on_qvProxyAddressTxt_textEdited(const QString &arg1)
|
||||
{
|
||||
LOADINGCHECK
|
||||
CurrentConfig.networkConfig.address = arg1;
|
||||
}
|
||||
|
||||
void PreferencesWindow::on_qvProxyTypeCombo_currentTextChanged(const QString &arg1)
|
||||
{
|
||||
LOADINGCHECK
|
||||
CurrentConfig.networkConfig.type = arg1.toLower();
|
||||
}
|
||||
|
||||
void PreferencesWindow::on_qvProxyPortCB_valueChanged(int arg1)
|
||||
{
|
||||
LOADINGCHECK
|
||||
CurrentConfig.networkConfig.port = arg1;
|
||||
}
|
||||
|
||||
void PreferencesWindow::on_qvNetworkUATxt_textEdited(const QString &arg1)
|
||||
{
|
||||
LOADINGCHECK
|
||||
CurrentConfig.networkConfig.userAgent = arg1;
|
||||
}
|
||||
|
||||
void PreferencesWindow::on_qvUseProxyCB_stateChanged(int arg1)
|
||||
{
|
||||
LOADINGCHECK
|
||||
CurrentConfig.networkConfig.useCustomProxy = arg1 == Qt::Checked;
|
||||
}
|
||||
|
||||
void PreferencesWindow::on_setAllowInsecureCB_stateChanged(int arg1)
|
||||
{
|
||||
LOADINGCHECK
|
||||
if (arg1 == Qt::Checked)
|
||||
{
|
||||
QvMessageBoxWarn(this, tr("Dangerous Operation"), tr("You will lose the advantage of TLS and make your connection under MITM attack."));
|
||||
}
|
||||
CurrentConfig.advancedConfig.setAllowInsecure = arg1 == Qt::Checked;
|
||||
}
|
||||
|
||||
void PreferencesWindow::on_setTestLatenctCB_stateChanged(int arg1)
|
||||
{
|
||||
LOADINGCHECK
|
||||
if (arg1 == Qt::Checked)
|
||||
{
|
||||
QvMessageBoxWarn(this, tr("Dangerous Operation"), tr("This will (probably) makes it easy to fingerprint your connection."));
|
||||
}
|
||||
CurrentConfig.advancedConfig.testLatencyPeriodcally = arg1 == Qt::Checked;
|
||||
}
|
||||
|
||||
void PreferencesWindow::on_setAllowInsecureCiphersCB_stateChanged(int arg1)
|
||||
{
|
||||
LOADINGCHECK
|
||||
if (arg1 == Qt::Checked)
|
||||
{
|
||||
QvMessageBoxWarn(this, tr("Dangerous Operation"), tr("You will lose the advantage of TLS and make your connection under MITM attack."));
|
||||
}
|
||||
CurrentConfig.advancedConfig.setAllowInsecureCiphers = arg1 == Qt::Checked;
|
||||
}
|
||||
|
||||
void PreferencesWindow::on_quietModeCB_stateChanged(int arg1)
|
||||
{
|
||||
LOADINGCHECK
|
||||
CurrentConfig.uiConfig.quietMode = arg1 == Qt::Checked;
|
||||
}
|
||||
|
@ -118,18 +118,10 @@ class PreferencesWindow
|
||||
|
||||
void on_darkTrayCB_stateChanged(int arg1);
|
||||
|
||||
void on_pacGoBtn_clicked();
|
||||
|
||||
void on_pacPortSB_valueChanged(int arg1);
|
||||
|
||||
void on_setSysProxyCB_stateChanged(int arg1);
|
||||
|
||||
void on_pacProxyCB_currentIndexChanged(int index);
|
||||
|
||||
void on_pushButton_clicked();
|
||||
|
||||
void on_pacProxyTxt_textEdited(const QString &arg1);
|
||||
|
||||
void on_autoStartSubsCombo_currentIndexChanged(const QString &arg1);
|
||||
|
||||
void on_autoStartConnCombo_currentIndexChanged(const QString &arg1);
|
||||
@ -148,16 +140,12 @@ class PreferencesWindow
|
||||
|
||||
void on_fpPortSB_valueChanged(int arg1);
|
||||
|
||||
void on_pacProxyTxt_textChanged(const QString &arg1);
|
||||
|
||||
void on_checkVCoreSettings_clicked();
|
||||
|
||||
void on_httpGroupBox_clicked(bool checked);
|
||||
|
||||
void on_socksGroupBox_clicked(bool checked);
|
||||
|
||||
void on_pacGroupBox_clicked(bool checked);
|
||||
|
||||
void on_fpGroupBox_clicked(bool checked);
|
||||
|
||||
void on_maxLogLinesSB_valueChanged(int arg1);
|
||||
@ -168,6 +156,28 @@ class PreferencesWindow
|
||||
|
||||
void on_updateChannelCombo_currentIndexChanged(int index);
|
||||
|
||||
void on_pluginKernelV2rayIntegrationCB_stateChanged(int arg1);
|
||||
|
||||
void on_pluginKernelPortAllocateCB_valueChanged(int arg1);
|
||||
|
||||
void on_qvProxyAddressTxt_textEdited(const QString &arg1);
|
||||
|
||||
void on_qvProxyTypeCombo_currentTextChanged(const QString &arg1);
|
||||
|
||||
void on_qvProxyPortCB_valueChanged(int arg1);
|
||||
|
||||
void on_qvNetworkUATxt_textEdited(const QString &arg1);
|
||||
|
||||
void on_qvUseProxyCB_stateChanged(int arg1);
|
||||
|
||||
void on_setAllowInsecureCB_stateChanged(int arg1);
|
||||
|
||||
void on_setTestLatenctCB_stateChanged(int arg1);
|
||||
|
||||
void on_setAllowInsecureCiphersCB_stateChanged(int arg1);
|
||||
|
||||
void on_quietModeCB_stateChanged(int arg1);
|
||||
|
||||
private:
|
||||
//
|
||||
RouteSettingsMatrixWidget *routeSettingsWidget;
|
||||
|
@ -181,9 +181,6 @@
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QDoubleSpinBox" name="updateIntervalSB">
|
||||
<property name="minimum">
|
||||
<double>0.500000000000000</double>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>365.000000000000000</double>
|
||||
</property>
|
||||
|
@ -145,11 +145,14 @@ void ConnectionItemWidget::CancelRename()
|
||||
|
||||
void ConnectionItemWidget::BeginRename()
|
||||
{
|
||||
stackedWidget->setCurrentIndex(1);
|
||||
renameTxt->setStyle(QStyleFactory::create("Fusion"));
|
||||
renameTxt->setStyleSheet("background-color: " + this->palette().color(this->backgroundRole()).name(QColor::HexRgb));
|
||||
renameTxt->setText(originalItemName);
|
||||
renameTxt->setFocus();
|
||||
if (IsConnection())
|
||||
{
|
||||
stackedWidget->setCurrentIndex(1);
|
||||
renameTxt->setStyle(QStyleFactory::create("Fusion"));
|
||||
renameTxt->setStyleSheet("background-color: " + this->palette().color(this->backgroundRole()).name(QColor::HexRgb));
|
||||
renameTxt->setText(originalItemName);
|
||||
renameTxt->setFocus();
|
||||
}
|
||||
}
|
||||
|
||||
ConnectionItemWidget::~ConnectionItemWidget()
|
||||
|
@ -103,7 +103,7 @@ void RouteSettingsMatrixWidget::on_importSchemeBtn_clicked()
|
||||
|
||||
// read the file and parse back to struct.
|
||||
// if error occurred on parsing, an exception will be thrown.
|
||||
auto content = StringFromFile(filePath.value());
|
||||
auto content = StringFromFile(ACCESS_OPTIONAL_VALUE(filePath));
|
||||
auto scheme = StructFromJsonString<Qv2rayRouteScheme>(content);
|
||||
|
||||
// show the information of this scheme to user,
|
||||
@ -175,7 +175,7 @@ void RouteSettingsMatrixWidget::on_exportSchemeBtn_clicked()
|
||||
|
||||
// serialize and write out
|
||||
auto content = StructToJsonString(scheme);
|
||||
StringToFile(content, savePath.value());
|
||||
StringToFile(content, ACCESS_OPTIONAL_VALUE(savePath));
|
||||
|
||||
// done
|
||||
// TODO: Give some success as Notification
|
||||
|
@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>600</width>
|
||||
<height>409</height>
|
||||
<width>582</width>
|
||||
<height>404</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@ -45,93 +45,84 @@
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Route Settings</string>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Lines start with "geoip:" or "geosite:" will have its autocompletion from geoip.dat and geosite.dat</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Lines start with "geoip:" or "geosite:" will have its autocompletion from geoip.dat and geosite.dat</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout" rowstretch="0,1,1" columnstretch="0,1,1,1">
|
||||
<item row="0" column="3">
|
||||
<widget class="QLabel" name="label_82">
|
||||
<property name="text">
|
||||
<string>Block</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="3">
|
||||
<layout class="QGridLayout" name="blockTxtLayout"/>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="label_80">
|
||||
<property name="text">
|
||||
<string>Direct</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Domain</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<layout class="QGridLayout" name="directTxtLayout"/>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<layout class="QGridLayout" name="proxyIPLayout"/>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QLabel" name="label_81">
|
||||
<property name="text">
|
||||
<string>Proxy</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<layout class="QGridLayout" name="proxyTxtLayout"/>
|
||||
</item>
|
||||
<item row="1" column="3">
|
||||
<layout class="QGridLayout" name="blockIPLayout"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>IP</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<layout class="QGridLayout" name="directIPLayout"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout" rowstretch="0,1,1" columnstretch="0,1,1,1">
|
||||
<item row="0" column="3">
|
||||
<widget class="QLabel" name="label_82">
|
||||
<property name="text">
|
||||
<string>Block</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="3">
|
||||
<layout class="QGridLayout" name="blockTxtLayout"/>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="label_80">
|
||||
<property name="text">
|
||||
<string>Direct</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Domain</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<layout class="QGridLayout" name="directTxtLayout"/>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<layout class="QGridLayout" name="proxyIPLayout"/>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QLabel" name="label_81">
|
||||
<property name="text">
|
||||
<string>Proxy</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<layout class="QGridLayout" name="proxyTxtLayout"/>
|
||||
</item>
|
||||
<item row="1" column="3">
|
||||
<layout class="QGridLayout" name="blockIPLayout"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>IP</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<layout class="QGridLayout" name="directIPLayout"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
|
@ -22,7 +22,7 @@ QvMessageBusSlotImpl(StreamSettingsWidget)
|
||||
}
|
||||
}
|
||||
|
||||
StreamSettingsObject StreamSettingsWidget::GetStreamSettings()
|
||||
StreamSettingsObject StreamSettingsWidget::GetStreamSettings() const
|
||||
{
|
||||
return stream;
|
||||
}
|
||||
@ -36,6 +36,7 @@ void StreamSettingsWidget::SetStreamObject(const StreamSettingsObject &sso)
|
||||
tlsCB->setChecked(stream.security == "tls");
|
||||
serverNameTxt->setText(stream.tlsSettings.serverName);
|
||||
allowInsecureCB->setChecked(stream.tlsSettings.allowInsecure);
|
||||
allowInsecureCiphersCB->setChecked(stream.tlsSettings.allowInsecureCiphers);
|
||||
alpnTxt->setPlainText(stream.tlsSettings.alpn.join(NEWLINE));
|
||||
// TCP
|
||||
tcpHeaderTypeCB->setCurrentText(stream.tcpSettings.header.type);
|
||||
@ -47,9 +48,9 @@ void StreamSettingsWidget::SetStreamObject(const StreamSettingsObject &sso)
|
||||
// WS
|
||||
wsPathTxt->setText(stream.wsSettings.path);
|
||||
QString wsHeaders;
|
||||
for (auto index = 0; index < stream.wsSettings.headers.count(); index++)
|
||||
for (auto i = 0; i < stream.wsSettings.headers.count(); i++)
|
||||
{
|
||||
wsHeaders = wsHeaders % stream.wsSettings.headers.keys().at(index) % "|" % stream.wsSettings.headers.values().at(index) % NEWLINE;
|
||||
wsHeaders = wsHeaders % stream.wsSettings.headers.keys().at(i) % "|" % stream.wsSettings.headers.values().at(i) % NEWLINE;
|
||||
}
|
||||
|
||||
wsHeadersTxt->setPlainText(wsHeaders);
|
||||
@ -284,3 +285,8 @@ void StreamSettingsWidget::on_alpnTxt_textChanged()
|
||||
{
|
||||
stream.tlsSettings.alpn = SplitLines(alpnTxt->toPlainText());
|
||||
}
|
||||
|
||||
void StreamSettingsWidget::on_allowInsecureCiphersCB_stateChanged(int arg1)
|
||||
{
|
||||
stream.tlsSettings.allowInsecureCiphers = arg1 == Qt::Checked;
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ class StreamSettingsWidget
|
||||
public:
|
||||
explicit StreamSettingsWidget(QWidget *parent = nullptr);
|
||||
void SetStreamObject(const StreamSettingsObject &sso);
|
||||
StreamSettingsObject GetStreamSettings();
|
||||
StreamSettingsObject GetStreamSettings() const;
|
||||
|
||||
private slots:
|
||||
void on_httpPathTxt_textEdited(const QString &arg1);
|
||||
@ -77,6 +77,8 @@ class StreamSettingsWidget
|
||||
|
||||
void on_alpnTxt_textChanged();
|
||||
|
||||
void on_allowInsecureCiphersCB_stateChanged(int arg1);
|
||||
|
||||
private:
|
||||
QvMessageBusSlotDecl;
|
||||
StreamSettingsObject stream;
|
||||
|
@ -635,37 +635,37 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Server</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<item row="3" column="1">
|
||||
<widget class="QLineEdit" name="serverNameTxt"/>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_14">
|
||||
<property name="text">
|
||||
<string>ALPN</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<item row="4" column="1">
|
||||
<widget class="QPlainTextEdit" name="alpnTxt"/>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_13">
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="tlsCB">
|
||||
<property name="text">
|
||||
<string>TLS</string>
|
||||
<string>Enable TLS</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="tlsCB">
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="allowInsecureCiphersCB">
|
||||
<property name="text">
|
||||
<string>Enabled</string>
|
||||
<string>Allow Insecure Ciphers</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|