30
.github/workflows/build-qv2ray-cmake.yml
vendored
@ -81,10 +81,7 @@ jobs:
|
|||||||
- name: macOS - ${{ matrix.qt_version }} - Build preparation - Install Packages
|
- name: macOS - ${{ matrix.qt_version }} - Build preparation - Install Packages
|
||||||
if: matrix.platform == 'macos-latest'
|
if: matrix.platform == 'macos-latest'
|
||||||
run: |
|
run: |
|
||||||
brew install protobuf grpc ninja wget
|
brew install protobuf grpc ninja
|
||||||
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
|
|
||||||
# --------------------------------------------------------
|
# --------------------------------------------------------
|
||||||
- name: Win-${{ matrix.arch }} - ${{ matrix.qt_version }} - Build preparation - Download Dependencies
|
- name: Win-${{ matrix.arch }} - ${{ matrix.qt_version }} - Build preparation - Download Dependencies
|
||||||
shell: bash
|
shell: bash
|
||||||
@ -99,18 +96,23 @@ jobs:
|
|||||||
pathSource: ./libs/Qv2ray-deps-grpc-${{ matrix.arch }}-windows.7z
|
pathSource: ./libs/Qv2ray-deps-grpc-${{ matrix.arch }}-windows.7z
|
||||||
pathTarget: ./libs
|
pathTarget: ./libs
|
||||||
# ========================================================================================================= Generate MakeFile and Build
|
# ========================================================================================================= 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
|
- name: macOS - ${{ matrix.qt_version }} - Generate Dependencies and Build
|
||||||
shell: bash
|
shell: bash
|
||||||
if: matrix.platform == 'macos-latest'
|
if: matrix.platform == 'macos-latest'
|
||||||
run: |
|
run: |
|
||||||
mkdir build
|
mkdir build
|
||||||
cd 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
|
cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_DEPLOYMENT_TARGET=10.13 -DDS_STORE_SCRIPT=ON
|
||||||
sudo cmake --build . --target package --parallel $(sysctl -n hw.logicalcpu)
|
sudo cmake --build . --parallel $(sysctl -n hw.logicalcpu)
|
||||||
cp qv2ray-*.dmg ../
|
sudo cmake --install .
|
||||||
- name: macOS - Get package name
|
sudo appdmg ../assets/package_dmg.json ../Qv2ray.dmg
|
||||||
id: get_package
|
|
||||||
run: echo ::set-output name=NAME::$(basename qv2ray-*.dmg)
|
|
||||||
# --------------------------------------------------------
|
# --------------------------------------------------------
|
||||||
- name: Windows - ${{ matrix.qt_version }} - Generate Dependencies and Build
|
- name: Windows - ${{ matrix.qt_version }} - Generate Dependencies and Build
|
||||||
shell: bash
|
shell: bash
|
||||||
@ -177,15 +179,15 @@ jobs:
|
|||||||
if: matrix.platform == 'macos-latest'
|
if: matrix.platform == 'macos-latest'
|
||||||
uses: actions/upload-artifact@master
|
uses: actions/upload-artifact@master
|
||||||
with:
|
with:
|
||||||
name: ${{ steps.get_package.outputs.NAME }}
|
name: Qv2ray-${{ github.sha }}.macOS-${{ matrix.arch }}.qt${{ matrix.qt_version }}.dmg
|
||||||
path: ${{ steps.get_package.outputs.NAME }}
|
path: Qv2ray.dmg
|
||||||
- name: macOS - ${{ matrix.qt_version }} - Upload binaries to release
|
- name: macOS - ${{ matrix.qt_version }} - Upload binaries to release
|
||||||
uses: svenstaro/upload-release-action@v1-release
|
uses: svenstaro/upload-release-action@v1-release
|
||||||
if: github.event_name == 'release' && matrix.platform == 'macos-latest' && matrix.qt_version == '5.14.2'
|
if: github.event_name == 'release' && matrix.platform == 'macos-latest' && matrix.qt_version == '5.14.2'
|
||||||
with:
|
with:
|
||||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
file: ${{ steps.get_package.outputs.NAME }}
|
file: Qv2ray.dmg
|
||||||
asset_name: ${{ steps.get_package.outputs.NAME }}
|
asset_name: Qv2ray-${{ steps.get_version.outputs.VERSION }}.macOS-${{ matrix.arch }}.dmg
|
||||||
tag: ${{ github.ref }}
|
tag: ${{ github.ref }}
|
||||||
overwrite: true
|
overwrite: true
|
||||||
# --------------------------------------------------------
|
# --------------------------------------------------------
|
||||||
|
6
.gitmodules
vendored
@ -10,12 +10,12 @@
|
|||||||
[submodule "libs/libqvb"]
|
[submodule "libs/libqvb"]
|
||||||
path = libs/libqvb
|
path = libs/libqvb
|
||||||
url = https://github.com/Qv2ray/QvRPCBridge
|
url = https://github.com/Qv2ray/QvRPCBridge
|
||||||
[submodule "3rdparty/cpp-httplib"]
|
|
||||||
path = 3rdparty/cpp-httplib
|
|
||||||
url = https://github.com/yhirose/cpp-httplib
|
|
||||||
[submodule "libs/puresource"]
|
[submodule "libs/puresource"]
|
||||||
path = libs/puresource
|
path = libs/puresource
|
||||||
url = https://github.com/Qv2ray/PureSource/
|
url = https://github.com/Qv2ray/PureSource/
|
||||||
[submodule "3rdparty/zxing-cpp"]
|
[submodule "3rdparty/zxing-cpp"]
|
||||||
path = 3rdparty/zxing-cpp
|
path = 3rdparty/zxing-cpp
|
||||||
url = https://github.com/nu-book/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
|
language: shell
|
||||||
os: linux
|
os: linux
|
||||||
dist: bionic
|
dist: bionic
|
||||||
arch:
|
|
||||||
- amd64
|
|
||||||
- arm64
|
|
||||||
|
|
||||||
git:
|
|
||||||
depth: false
|
|
||||||
|
|
||||||
branches:
|
|
||||||
only:
|
|
||||||
- dev
|
|
||||||
- /^v\d+\.\d+(\.\d+)?(-\S*)?$/
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
@ -23,30 +12,22 @@ addons:
|
|||||||
- name: snapcraft
|
- name: snapcraft
|
||||||
channel: stable
|
channel: stable
|
||||||
confinement: classic
|
confinement: classic
|
||||||
|
- name: lxd
|
||||||
before_install:
|
channel: stable
|
||||||
- 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
|
|
||||||
|
|
||||||
script:
|
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:
|
after_failure:
|
||||||
- sudo journalctl -u snapd
|
- sudo journalctl -u snapd
|
||||||
|
|
||||||
deploy:
|
deploy:
|
||||||
- provider: snap
|
- provider: launchpad
|
||||||
snap: qv2ray_*.snap
|
slug: "~ymshenyu/qv2ray/+git/trunk"
|
||||||
channel: edge
|
oauth_token: $LAUNCHPAD_OAUTH_TOKEN
|
||||||
skip_cleanup: true
|
oauth_token_secret: $LAUNCHPAD_OAUTH_TOKEN_SECRET
|
||||||
on:
|
on:
|
||||||
branch: dev
|
all_branches: true
|
||||||
- provider: snap
|
|
||||||
snap: qv2ray_*.snap
|
|
||||||
channel: beta
|
|
||||||
skip_cleanup: true
|
|
||||||
on:
|
|
||||||
branch: /^v\d+\.\d+(\.\d+)?(-\S*)?$/
|
|
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
|
|
@ -146,7 +146,11 @@ if(QV2RAY_DISABLE_AUTO_UPDATE)
|
|||||||
add_definitions(-DDISABLE_AUTO_UPDATE)
|
add_definitions(-DDISABLE_AUTO_UPDATE)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
set(QVPLUGIN_INTERFACE_INCLUDE_DIR "src/components/plugins/interface")
|
||||||
|
include(src/components/plugins/interface/QvPluginInterface.cmake)
|
||||||
|
|
||||||
set(QV2RAY_SOURCES
|
set(QV2RAY_SOURCES
|
||||||
|
${QVPLUGIN_INTERFACE_HEADERS}
|
||||||
3rdparty/libsemver/version.cpp
|
3rdparty/libsemver/version.cpp
|
||||||
src/base/Qv2rayLog.cpp
|
src/base/Qv2rayLog.cpp
|
||||||
src/common/CommandArgs.cpp
|
src/common/CommandArgs.cpp
|
||||||
@ -161,11 +165,10 @@ set(QV2RAY_SOURCES
|
|||||||
src/components/geosite/QvGeositeReader.cpp
|
src/components/geosite/QvGeositeReader.cpp
|
||||||
src/components/latency/win/ICMPPinger.cpp
|
src/components/latency/win/ICMPPinger.cpp
|
||||||
src/components/latency/QvTCPing.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.cpp
|
||||||
src/components/plugins/toolbar/QvToolbar_linux.cpp
|
src/components/plugins/toolbar/QvToolbar_linux.cpp
|
||||||
src/components/plugins/toolbar/QvToolbar_win.cpp
|
src/components/plugins/toolbar/QvToolbar_win.cpp
|
||||||
|
src/components/plugins/QvPluginHost.cpp
|
||||||
src/components/proxy/QvProxyConfigurator.cpp
|
src/components/proxy/QvProxyConfigurator.cpp
|
||||||
src/components/route/RouteSchemeIO.cpp
|
src/components/route/RouteSchemeIO.cpp
|
||||||
src/components/speedchart/speedplotview.cpp
|
src/components/speedchart/speedplotview.cpp
|
||||||
@ -180,9 +183,10 @@ set(QV2RAY_SOURCES
|
|||||||
src/core/connection/Serialization_vmess.cpp
|
src/core/connection/Serialization_vmess.cpp
|
||||||
src/core/CoreUtils.cpp
|
src/core/CoreUtils.cpp
|
||||||
src/core/handler/ConfigHandler.cpp
|
src/core/handler/ConfigHandler.cpp
|
||||||
src/core/handler/V2rayInstanceHandler.cpp
|
src/core/handler/KernelInstanceHandler.cpp
|
||||||
src/core/kernel/APIBackend.cpp
|
src/core/kernel/APIBackend.cpp
|
||||||
src/core/kernel/KernelInteractions.cpp
|
src/core/kernel/V2rayKernelInteractions.cpp
|
||||||
|
src/core/kernel/PluginKernelInteractions.cpp
|
||||||
src/core/kernel/QvKernelABIChecker.cpp
|
src/core/kernel/QvKernelABIChecker.cpp
|
||||||
src/core/settings/SettingsBackend.cpp
|
src/core/settings/SettingsBackend.cpp
|
||||||
src/core/settings/SettingsUpgrade.cpp
|
src/core/settings/SettingsUpgrade.cpp
|
||||||
@ -205,6 +209,7 @@ set(QV2RAY_SOURCES
|
|||||||
src/ui/w_MainWindow.cpp
|
src/ui/w_MainWindow.cpp
|
||||||
src/ui/w_MainWindow_extra.cpp
|
src/ui/w_MainWindow_extra.cpp
|
||||||
src/ui/w_PreferencesWindow.cpp
|
src/ui/w_PreferencesWindow.cpp
|
||||||
|
src/ui/w_PluginManager.cpp
|
||||||
src/ui/w_ScreenShot_Core.cpp
|
src/ui/w_ScreenShot_Core.cpp
|
||||||
src/ui/w_SubscriptionManager.cpp
|
src/ui/w_SubscriptionManager.cpp
|
||||||
# ui files
|
# ui files
|
||||||
@ -220,6 +225,7 @@ set(QV2RAY_SOURCES
|
|||||||
src/ui/widgets/RouteSettingsMatrix.ui
|
src/ui/widgets/RouteSettingsMatrix.ui
|
||||||
src/ui/w_MainWindow.ui
|
src/ui/w_MainWindow.ui
|
||||||
src/ui/w_PreferencesWindow.ui
|
src/ui/w_PreferencesWindow.ui
|
||||||
|
src/ui/w_PluginManager.ui
|
||||||
src/ui/w_ScreenShot_Core.ui
|
src/ui/w_ScreenShot_Core.ui
|
||||||
# headers
|
# headers
|
||||||
3rdparty/libsemver/version.hpp
|
3rdparty/libsemver/version.hpp
|
||||||
@ -246,8 +252,8 @@ set(QV2RAY_SOURCES
|
|||||||
src/components/geosite/QvGeositeReader.hpp
|
src/components/geosite/QvGeositeReader.hpp
|
||||||
src/components/latency/win/ICMPPinger.hpp
|
src/components/latency/win/ICMPPinger.hpp
|
||||||
src/components/latency/QvTCPing.hpp
|
src/components/latency/QvTCPing.hpp
|
||||||
src/components/pac/QvPACHandler.hpp
|
|
||||||
src/components/plugins/toolbar/QvToolbar.hpp
|
src/components/plugins/toolbar/QvToolbar.hpp
|
||||||
|
src/components/plugins/QvPluginHost.hpp
|
||||||
src/components/proxy/QvProxyConfigurator.hpp
|
src/components/proxy/QvProxyConfigurator.hpp
|
||||||
src/components/route/RouteSchemeIO.hpp
|
src/components/route/RouteSchemeIO.hpp
|
||||||
src/components/route/presets/RouteScheme_V2rayN.hpp
|
src/components/route/presets/RouteScheme_V2rayN.hpp
|
||||||
@ -260,8 +266,10 @@ set(QV2RAY_SOURCES
|
|||||||
src/core/CoreSafeTypes.hpp
|
src/core/CoreSafeTypes.hpp
|
||||||
src/core/CoreUtils.hpp
|
src/core/CoreUtils.hpp
|
||||||
src/core/handler/ConfigHandler.hpp
|
src/core/handler/ConfigHandler.hpp
|
||||||
|
src/core/handler/KernelInstanceHandler.hpp
|
||||||
src/core/kernel/APIBackend.hpp
|
src/core/kernel/APIBackend.hpp
|
||||||
src/core/kernel/KernelInteractions.hpp
|
src/core/kernel/V2rayKernelInteractions.hpp
|
||||||
|
src/core/kernel/PluginKernelInteractions.hpp
|
||||||
src/core/kernel/QvKernelABIChecker.hpp
|
src/core/kernel/QvKernelABIChecker.hpp
|
||||||
src/core/settings/SettingsBackend.hpp
|
src/core/settings/SettingsBackend.hpp
|
||||||
src/ui/editors/w_InboundEditor.hpp
|
src/ui/editors/w_InboundEditor.hpp
|
||||||
@ -281,6 +289,7 @@ set(QV2RAY_SOURCES
|
|||||||
src/ui/w_ImportConfig.hpp
|
src/ui/w_ImportConfig.hpp
|
||||||
src/ui/w_MainWindow.hpp
|
src/ui/w_MainWindow.hpp
|
||||||
src/ui/w_PreferencesWindow.hpp
|
src/ui/w_PreferencesWindow.hpp
|
||||||
|
src/ui/w_PluginManager.hpp
|
||||||
src/ui/w_ScreenShot_Core.hpp
|
src/ui/w_ScreenShot_Core.hpp
|
||||||
src/ui/w_SubscriptionManager.hpp
|
src/ui/w_SubscriptionManager.hpp
|
||||||
assets/qv2ray.rc
|
assets/qv2ray.rc
|
||||||
@ -356,6 +365,7 @@ if(APPLE)
|
|||||||
)
|
)
|
||||||
|
|
||||||
set(MACOSX_ICON "${CMAKE_SOURCE_DIR}/assets/icons/qv2ray.icns")
|
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}
|
set_source_files_properties(${QM_FILES}
|
||||||
PROPERTIES
|
PROPERTIES
|
||||||
MACOSX_PACKAGE_LOCATION Resources/lang
|
MACOSX_PACKAGE_LOCATION Resources/lang
|
||||||
@ -368,6 +378,7 @@ if(APPLE)
|
|||||||
set_target_properties(${PROJECT_NAME}
|
set_target_properties(${PROJECT_NAME}
|
||||||
PROPERTIES
|
PROPERTIES
|
||||||
MACOSX_BUNDLE TRUE
|
MACOSX_BUNDLE TRUE
|
||||||
|
MACOSX_BUNDLE_INFO_PLIST ${MACOSX_PLIST}
|
||||||
MACOSX_BUNDLE_BUNDLE_NAME "Qv2ray"
|
MACOSX_BUNDLE_BUNDLE_NAME "Qv2ray"
|
||||||
MACOSX_BUNDLE_BUNDLE_VERSION ${QV2RAY_VERSION_STRING}
|
MACOSX_BUNDLE_BUNDLE_VERSION ${QV2RAY_VERSION_STRING}
|
||||||
MACOSX_BUNDLE_COPYRIGHT "Copyright (c) 2019-2020 Qv2ray Development Group"
|
MACOSX_BUNDLE_COPYRIGHT "Copyright (c) 2019-2020 Qv2ray Development Group"
|
||||||
|
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)
|
if(APPLE)
|
||||||
set(CPACK_GENERATOR "DragNDrop")
|
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()
|
endif()
|
||||||
|
|
||||||
include(CPack)
|
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
|
qv2ray (2.4.1-1) unstable; urgency=medium
|
||||||
|
|
||||||
* add: add new semver checker
|
* 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_light/locate.png</file>
|
||||||
<file>assets/icons/ui_dark/sort.png</file>
|
<file>assets/icons/ui_dark/sort.png</file>
|
||||||
<file>assets/icons/ui_light/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>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
@ -83,6 +83,7 @@ parts:
|
|||||||
sed -i 's|^Icon=.*|Icon=/usr/share/icons/hicolor/256x256/apps/qv2ray.png|g' assets/qv2ray.desktop
|
sed -i 's|^Icon=.*|Icon=/usr/share/icons/hicolor/256x256/apps/qv2ray.png|g' assets/qv2ray.desktop
|
||||||
after:
|
after:
|
||||||
- desktop-qt5
|
- desktop-qt5
|
||||||
|
- ppa
|
||||||
|
|
||||||
desktop-qt5:
|
desktop-qt5:
|
||||||
source: https://github.com/ubuntu/snapcraft-desktop-helpers.git
|
source: https://github.com/ubuntu/snapcraft-desktop-helpers.git
|
||||||
@ -108,8 +109,21 @@ parts:
|
|||||||
- locales-all
|
- locales-all
|
||||||
- xdg-user-dirs
|
- xdg-user-dirs
|
||||||
- fcitx-frontend-qt5
|
- fcitx-frontend-qt5
|
||||||
|
after:
|
||||||
|
- ppa
|
||||||
|
|
||||||
qt5-gtk-platform:
|
qt5-gtk-platform:
|
||||||
plugin: nil
|
plugin: nil
|
||||||
stage-packages:
|
stage-packages:
|
||||||
- qt5-gtk-platformtheme
|
- 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_CONFIG_FILE (QV2RAY_CONFIG_DIR + "Qv2ray.conf")
|
||||||
#define QV2RAY_CONNECTIONS_DIR (QV2RAY_CONFIG_DIR + "connections/")
|
#define QV2RAY_CONNECTIONS_DIR (QV2RAY_CONFIG_DIR + "connections/")
|
||||||
#define QV2RAY_SUBSCRIPTION_DIR (QV2RAY_CONFIG_DIR + "subscriptions/")
|
#define QV2RAY_SUBSCRIPTION_DIR (QV2RAY_CONFIG_DIR + "subscriptions/")
|
||||||
|
#define QV2RAY_PLUGIN_SETTINGS_DIR (QV2RAY_CONFIG_DIR + "plugin_settings/")
|
||||||
|
|
||||||
// Get GFWList and PAC file path.
|
// Get GFWList and PAC file path.
|
||||||
#define QV2RAY_RULES_DIR (QV2RAY_CONFIG_DIR + "rules/")
|
#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 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/"))
|
((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)
|
#define QSTRN(num) QString::number(num)
|
||||||
|
|
||||||
@ -127,4 +136,27 @@ namespace Qv2ray
|
|||||||
isExiting = true;
|
isExiting = true;
|
||||||
QApplication::quit();
|
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
|
} // namespace Qv2ray
|
||||||
|
@ -41,6 +41,7 @@ const inline QString MODULE_FILEIO = "COMMON-FILEIO";
|
|||||||
//
|
//
|
||||||
const inline QString MODULE_PROXY = "COMPONENT-PROXY";
|
const inline QString MODULE_PROXY = "COMPONENT-PROXY";
|
||||||
const inline QString MODULE_UPDATE = "COMPONENT-UPDATE";
|
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";
|
const inline QString MODULE_CORE_HANDLER = "QV2RAY-CORE";
|
||||||
|
@ -257,13 +257,14 @@ namespace Qv2ray::base::objects
|
|||||||
{
|
{
|
||||||
QString serverName;
|
QString serverName;
|
||||||
bool allowInsecure;
|
bool allowInsecure;
|
||||||
|
bool allowInsecureCiphers;
|
||||||
QList<QString> alpn;
|
QList<QString> alpn;
|
||||||
QList<CertificateObject> certificates;
|
QList<CertificateObject> certificates;
|
||||||
bool disableSystemRoot;
|
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
|
} // namespace transfer
|
||||||
//
|
//
|
||||||
|
@ -41,18 +41,6 @@ namespace Qv2ray::base::config
|
|||||||
XTOSTRUCT(O(Pages))
|
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
|
struct Qv2rayForwardProxyConfig
|
||||||
{
|
{
|
||||||
bool enableForwardProxy;
|
bool enableForwardProxy;
|
||||||
@ -73,7 +61,6 @@ namespace Qv2ray::base::config
|
|||||||
{
|
{
|
||||||
QString listenip;
|
QString listenip;
|
||||||
bool setSystemProxy;
|
bool setSystemProxy;
|
||||||
Qv2rayPACConfig pacConfig;
|
|
||||||
|
|
||||||
// SOCKS
|
// SOCKS
|
||||||
bool useSocks;
|
bool useSocks;
|
||||||
@ -89,26 +76,27 @@ namespace Qv2ray::base::config
|
|||||||
objects::AccountObject httpAccount;
|
objects::AccountObject httpAccount;
|
||||||
|
|
||||||
Qv2rayInboundsConfig()
|
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()
|
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,
|
XTOSTRUCT(O(setSystemProxy, listenip, useSocks, useHTTP, socks_port, socks_useAuth, socksAccount, socksUDP, socksLocalIP, http_port,
|
||||||
http_port, http_useAuth, httpAccount))
|
http_useAuth, httpAccount))
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Qv2rayUIConfig
|
struct Qv2rayUIConfig
|
||||||
{
|
{
|
||||||
QString theme;
|
QString theme;
|
||||||
QString language;
|
QString language;
|
||||||
|
bool quietMode;
|
||||||
bool useDarkTheme;
|
bool useDarkTheme;
|
||||||
bool useDarkTrayIcon;
|
bool useDarkTrayIcon;
|
||||||
int maximumLogLines;
|
int maximumLogLines;
|
||||||
Qv2rayUIConfig() : theme("Fusion"), language("en_US"), useDarkTheme(false), useDarkTrayIcon(true), maximumLogLines(500)
|
Qv2rayUIConfig() : theme("Fusion"), language("en_US"), useDarkTheme(false), useDarkTrayIcon(true), maximumLogLines(500)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
XTOSTRUCT(O(theme, language, useDarkTheme, useDarkTrayIcon, maximumLogLines))
|
XTOSTRUCT(O(theme, language, quietMode, useDarkTheme, useDarkTrayIcon, maximumLogLines))
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Qv2rayRouteConfig_Impl
|
struct Qv2rayRouteConfig_Impl
|
||||||
@ -141,6 +129,15 @@ namespace Qv2ray::base::config
|
|||||||
XTOSTRUCT(O(domainStrategy, domains, ips))
|
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
|
struct Qv2rayConnectionConfig
|
||||||
{
|
{
|
||||||
bool bypassCN;
|
bool bypassCN;
|
||||||
@ -218,6 +215,25 @@ namespace Qv2ray::base::config
|
|||||||
XTOSTRUCT(O(ignoredVersion, updateChannel))
|
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
|
struct Qv2rayConfig
|
||||||
{
|
{
|
||||||
int config_version;
|
int config_version;
|
||||||
@ -234,19 +250,53 @@ namespace Qv2ray::base::config
|
|||||||
//
|
//
|
||||||
Qv2rayUIConfig uiConfig;
|
Qv2rayUIConfig uiConfig;
|
||||||
Qv2rayAPIConfig apiConfig;
|
Qv2rayAPIConfig apiConfig;
|
||||||
|
Qv2rayPluginConfig pluginConfig;
|
||||||
Qv2rayKernelConfig kernelConfig;
|
Qv2rayKernelConfig kernelConfig;
|
||||||
Qv2rayUpdateConfig updateConfig;
|
Qv2rayUpdateConfig updateConfig;
|
||||||
|
Qv2rayNetworkConfig networkConfig;
|
||||||
Qv2rayToolBarConfig toolBarConfig;
|
Qv2rayToolBarConfig toolBarConfig;
|
||||||
Qv2rayInboundsConfig inboundConfig;
|
Qv2rayInboundsConfig inboundConfig;
|
||||||
|
Qv2rayAdvancedConfig advancedConfig;
|
||||||
Qv2rayConnectionConfig connectionConfig;
|
Qv2rayConnectionConfig connectionConfig;
|
||||||
|
|
||||||
Qv2rayConfig()
|
Qv2rayConfig()
|
||||||
: config_version(QV2RAY_CONFIG_VERSION), tProxySupport(false), logLevel(), autoStartId("null"), groups(), subscriptions(),
|
: config_version(QV2RAY_CONFIG_VERSION), //
|
||||||
connections(), uiConfig(), apiConfig(), kernelConfig(), updateConfig(), toolBarConfig(), inboundConfig(), connectionConfig()
|
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,
|
XTOSTRUCT(O(config_version, //
|
||||||
autoStartId, inboundConfig, connectionConfig, toolBarConfig, apiConfig))
|
tProxySupport, //
|
||||||
|
logLevel, //
|
||||||
|
uiConfig, //
|
||||||
|
pluginConfig, //
|
||||||
|
updateConfig, //
|
||||||
|
kernelConfig, //
|
||||||
|
networkConfig, //
|
||||||
|
groups, //
|
||||||
|
connections, //
|
||||||
|
subscriptions, //
|
||||||
|
autoStartId, //
|
||||||
|
inboundConfig, //
|
||||||
|
connectionConfig, //
|
||||||
|
toolBarConfig, //
|
||||||
|
advancedConfig, //
|
||||||
|
apiConfig //
|
||||||
|
))
|
||||||
};
|
};
|
||||||
} // namespace Qv2ray::base::config
|
} // namespace Qv2ray::base::config
|
||||||
|
@ -16,6 +16,8 @@ namespace Qv2ray
|
|||||||
bool enableToolbarPlguin;
|
bool enableToolbarPlguin;
|
||||||
/// Disable Qt scale factors support.
|
/// Disable Qt scale factors support.
|
||||||
bool noScaleFactors;
|
bool noScaleFactors;
|
||||||
|
/// Disable all plugin features.
|
||||||
|
bool noPlugins;
|
||||||
};
|
};
|
||||||
} // namespace base
|
} // namespace base
|
||||||
inline base::QvStartupOptions StartupOption = base::QvStartupOptions();
|
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.")), //
|
runAsRootOption("I-just-wanna-run-with-root", tr("Explicitly run Qv2ray as root.")), //
|
||||||
debugOption("debug", tr("Enable Debug Output")), //
|
debugOption("debug", tr("Enable Debug Output")), //
|
||||||
noScaleFactorOption("noScaleFactor", tr("Disable manually set QT_SCALE_FACTOR")), //
|
noScaleFactorOption("noScaleFactor", tr("Disable manually set QT_SCALE_FACTOR")), //
|
||||||
|
noPluginsOption("noPlugin", tr("Disable plugin feature")), //
|
||||||
withToolbarOption("withToolbarPlugin", tr("Enable Qv2ray network toolbar plugin")), //
|
withToolbarOption("withToolbarPlugin", tr("Enable Qv2ray network toolbar plugin")), //
|
||||||
//
|
//
|
||||||
helpOption("FAKE"), versionOption("FAKE")
|
helpOption("FAKE"), versionOption("FAKE")
|
||||||
@ -20,6 +21,7 @@ namespace Qv2ray::common
|
|||||||
parser.addOption(runAsRootOption);
|
parser.addOption(runAsRootOption);
|
||||||
parser.addOption(debugOption);
|
parser.addOption(debugOption);
|
||||||
parser.addOption(noScaleFactorOption);
|
parser.addOption(noScaleFactorOption);
|
||||||
|
parser.addOption(noPluginsOption);
|
||||||
parser.addOption(withToolbarOption);
|
parser.addOption(withToolbarOption);
|
||||||
helpOption = parser.addHelpOption();
|
helpOption = parser.addHelpOption();
|
||||||
versionOption = parser.addVersionOption();
|
versionOption = parser.addVersionOption();
|
||||||
@ -63,6 +65,12 @@ namespace Qv2ray::common
|
|||||||
StartupOption.noScaleFactors = true;
|
StartupOption.noScaleFactors = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (parser.isSet(noPluginsOption))
|
||||||
|
{
|
||||||
|
DEBUG(MODULE_INIT, "noPluginOption is set.")
|
||||||
|
StartupOption.noPlugins = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (parser.isSet(withToolbarOption))
|
if (parser.isSet(withToolbarOption))
|
||||||
{
|
{
|
||||||
DEBUG(MODULE_INIT, "withToolbarOption is set.")
|
DEBUG(MODULE_INIT, "withToolbarOption is set.")
|
||||||
|
@ -28,6 +28,7 @@ namespace Qv2ray::common
|
|||||||
QCommandLineOption runAsRootOption;
|
QCommandLineOption runAsRootOption;
|
||||||
QCommandLineOption debugOption;
|
QCommandLineOption debugOption;
|
||||||
QCommandLineOption noScaleFactorOption;
|
QCommandLineOption noScaleFactorOption;
|
||||||
|
QCommandLineOption noPluginsOption;
|
||||||
QCommandLineOption withToolbarOption;
|
QCommandLineOption withToolbarOption;
|
||||||
QCommandLineOption helpOption;
|
QCommandLineOption helpOption;
|
||||||
QCommandLineOption versionOption;
|
QCommandLineOption versionOption;
|
||||||
|
@ -16,112 +16,80 @@ namespace Qv2ray::common
|
|||||||
accessManager.disconnect();
|
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)
|
void QvHttpRequestHelper::setHeader(const QByteArray &key, const QByteArray &value)
|
||||||
{
|
{
|
||||||
DEBUG(MODULE_NETWORK, "Adding HTTP request header: " + key + ":" + value)
|
DEBUG(MODULE_NETWORK, "Adding HTTP request header: " + key + ":" + value)
|
||||||
request.setRawHeader(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)
|
if (useProxy)
|
||||||
{
|
{
|
||||||
auto proxy = QNetworkProxyFactory::systemProxyForQuery();
|
auto p = GlobalConfig.networkConfig.useCustomProxy ?
|
||||||
accessManager.setProxy(proxy.first());
|
QNetworkProxy{
|
||||||
LOG(MODULE_NETWORK, "Sync get is using system proxy settings")
|
GlobalConfig.networkConfig.type == "http" ? QNetworkProxy::HttpProxy : QNetworkProxy::Socks5Proxy, //
|
||||||
|
GlobalConfig.networkConfig.address, //
|
||||||
|
quint16(GlobalConfig.networkConfig.port) //
|
||||||
|
} :
|
||||||
|
QNetworkProxyFactory::systemProxyForQuery().first();
|
||||||
|
accessManager.setProxy(p);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
DEBUG(MODULE_NETWORK, "Get without proxy.")
|
||||||
accessManager.setProxy(QNetworkProxy(QNetworkProxy::ProxyType::NoProxy));
|
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::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
|
||||||
request.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, true);
|
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);
|
reply = accessManager.get(request);
|
||||||
connect(reply, &QNetworkReply::finished, this, &QvHttpRequestHelper::onRequestFinished_p);
|
|
||||||
//
|
//
|
||||||
QEventLoop loop;
|
QEventLoop loop;
|
||||||
connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
||||||
loop.exec();
|
loop.exec();
|
||||||
|
//
|
||||||
// Data or timeout?
|
// Data or timeout?
|
||||||
auto data = reply->readAll();
|
auto data = reply->readAll();
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
void QvHttpRequestHelper::get(const QString &url)
|
void QvHttpRequestHelper::AsyncGet(const QString &url)
|
||||||
{
|
{
|
||||||
this->setUrl(url);
|
request.setUrl({ url });
|
||||||
// request.setRawHeader("Content-Type",
|
if (GlobalConfig.networkConfig.useCustomProxy)
|
||||||
// "application/x-www-form-urlencoded");
|
{
|
||||||
|
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);
|
reply = accessManager.get(request);
|
||||||
connect(reply, &QNetworkReply::finished, this, &QvHttpRequestHelper::onRequestFinished_p);
|
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()
|
void QvHttpRequestHelper::onRequestFinished_p()
|
||||||
{
|
{
|
||||||
if (reply->attribute(QNetworkRequest::HTTP2WasUsedAttribute).toBool())
|
if (reply->attribute(QNetworkRequest::HTTP2WasUsedAttribute).toBool())
|
||||||
@ -134,15 +102,15 @@ namespace Qv2ray::common
|
|||||||
QString error = QMetaEnum::fromType<QNetworkReply::NetworkError>().key(reply->error());
|
QString error = QMetaEnum::fromType<QNetworkReply::NetworkError>().key(reply->error());
|
||||||
LOG(MODULE_NETWORK, "Network request error string: " + error)
|
LOG(MODULE_NETWORK, "Network request error string: " + error)
|
||||||
QByteArray empty;
|
QByteArray empty;
|
||||||
emit httpRequestFinished(empty);
|
emit OnRequestFinished(empty);
|
||||||
}
|
}
|
||||||
else
|
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")
|
DEBUG(MODULE_NETWORK, "A request is now ready read")
|
||||||
this->data += reply->readAll();
|
this->data += reply->readAll();
|
||||||
|
@ -31,26 +31,18 @@ namespace Qv2ray::common
|
|||||||
public:
|
public:
|
||||||
explicit QvHttpRequestHelper(QObject *parent = nullptr);
|
explicit QvHttpRequestHelper(QObject *parent = nullptr);
|
||||||
~QvHttpRequestHelper();
|
~QvHttpRequestHelper();
|
||||||
bool setUrl(const QString &url);
|
|
||||||
void setHeader(const QByteArray &key, const QByteArray &value);
|
|
||||||
// get
|
// get
|
||||||
QByteArray syncget(const QString &url, bool useProxy);
|
void AsyncGet(const QString &url);
|
||||||
void get(const QString &url);
|
QByteArray Get(const QString &url, bool useProxy);
|
||||||
//// 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);
|
|
||||||
signals:
|
signals:
|
||||||
void httpRequestFinished(QByteArray &data);
|
void OnRequestFinished(QByteArray &data);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onRequestFinished_p();
|
void onRequestFinished_p();
|
||||||
void onReadyRead();
|
void onReadyRead_p();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void setHeader(const QByteArray &key, const QByteArray &value);
|
||||||
QByteArray data;
|
QByteArray data;
|
||||||
QUrl url;
|
QUrl url;
|
||||||
QNetworkReply *reply;
|
QNetworkReply *reply;
|
||||||
|
@ -7,14 +7,16 @@
|
|||||||
#include "TextUtfEncoding.h"
|
#include "TextUtfEncoding.h"
|
||||||
#include "base/Qv2rayBase.hpp"
|
#include "base/Qv2rayBase.hpp"
|
||||||
|
|
||||||
namespace Qv2ray::components
|
namespace Qv2ray::common
|
||||||
{
|
{
|
||||||
using namespace ZXing;
|
using namespace ZXing;
|
||||||
QString DecodeQRCode(const QImage &source)
|
QString DecodeQRCode(const QImage &source)
|
||||||
{
|
{
|
||||||
|
if (source.isNull())
|
||||||
|
return "";
|
||||||
QImage img = source.copy();
|
QImage img = source.copy();
|
||||||
auto result =
|
const auto result =
|
||||||
ReadBarcode(img.width(), img.height(), img.bits(), img.width() * 4, 4, 0, 1, 2, { BarcodeFormatFromString("") }, true, true);
|
ReadBarcode(img.width(), img.height(), img.bits(), img.width() * 4, 4, 0, 1, 2, { ZXing::BarcodeFormat::QR_CODE }, true, true);
|
||||||
|
|
||||||
if (result.isValid())
|
if (result.isValid())
|
||||||
{
|
{
|
||||||
@ -34,17 +36,15 @@ namespace Qv2ray::components
|
|||||||
int eccLevel = 1;
|
int eccLevel = 1;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
auto barcodeFormat = BarcodeFormatFromString("QR_CODE");
|
MultiFormatWriter writer(ZXing::BarcodeFormat::QR_CODE);
|
||||||
|
|
||||||
MultiFormatWriter writer(barcodeFormat);
|
|
||||||
writer.setMargin(1);
|
writer.setMargin(1);
|
||||||
writer.setEccLevel(eccLevel);
|
writer.setEccLevel(eccLevel);
|
||||||
|
|
||||||
auto bitmap = writer.encode(content.toStdWString(), size.width(), size.height());
|
const auto bitmap = writer.encode(content.toStdWString(), size.width(), size.height());
|
||||||
auto BM = bitmap.toByteMatrix();
|
const auto BM = bitmap.toByteMatrix();
|
||||||
//
|
//
|
||||||
const QRgb black = qRgba(0, 0, 0, 255);
|
const auto black = qRgba(0, 0, 0, 255);
|
||||||
const QRgb white = qRgba(255, 255, 255, 255);
|
const auto white = qRgba(255, 255, 255, 255);
|
||||||
//
|
//
|
||||||
auto image = QImage(bitmap.width(), bitmap.width(), QImage::Format_ARGB32);
|
auto image = QImage(bitmap.width(), bitmap.width(), QImage::Format_ARGB32);
|
||||||
image.fill(white);
|
image.fill(white);
|
||||||
@ -67,4 +67,4 @@ namespace Qv2ray::components
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // namespace Qv2ray::components
|
} // namespace Qv2ray::common
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
namespace Qv2ray::components
|
namespace Qv2ray::common
|
||||||
{
|
{
|
||||||
QString DecodeQRCode(const QImage &img);
|
QString DecodeQRCode(const QImage &img);
|
||||||
QImage EncodeQRCode(const QString &content, const QSize &size);
|
QImage EncodeQRCode(const QString &content, const QSize &size);
|
||||||
} // namespace Qv2ray::components
|
} // namespace Qv2ray::common
|
||||||
using namespace Qv2ray::components;
|
using namespace Qv2ray::common;
|
||||||
|
@ -17,33 +17,14 @@ using namespace Qv2ray::base;
|
|||||||
QStringList getLanguageSearchPaths()
|
QStringList getLanguageSearchPaths()
|
||||||
{
|
{
|
||||||
// Configuration Path
|
// Configuration Path
|
||||||
QStringList list;
|
QStringList list = Qv2rayAssetsPaths("lang");
|
||||||
list << QV2RAY_CONFIG_DIR + "lang";
|
|
||||||
//
|
|
||||||
#ifdef EMBED_TRANSLATIONS
|
#ifdef EMBED_TRANSLATIONS
|
||||||
// If the translations have been embedded.
|
// If the translations have been embedded.
|
||||||
list << QString(":/translations/");
|
list << QString(":/translations/");
|
||||||
#endif
|
#endif
|
||||||
//
|
|
||||||
//
|
|
||||||
#ifdef QV2RAY_TRANSLATION_PATH
|
#ifdef QV2RAY_TRANSLATION_PATH
|
||||||
// Platform-specific dir, if specified.
|
// Platform-specific dir, if specified.
|
||||||
list << QString(QV2RAY_TRANSLATION_PATH);
|
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
|
#endif
|
||||||
return list;
|
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,14 +89,14 @@ namespace Qv2ray::components::plugins
|
|||||||
case 104:
|
case 104:
|
||||||
{
|
{
|
||||||
// Current Connection Name
|
// Current Connection Name
|
||||||
CL.Message = GetDisplayName(ConnectionManager->CurrentConnection());
|
CL.Message = GetDisplayName(KernelInstance->CurrentConnection());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 105:
|
case 105:
|
||||||
{
|
{
|
||||||
// Current Connection Status
|
// Current Connection Status
|
||||||
CL.Message = ConnectionManager->CurrentConnection() == NullConnectionId ? QObject::tr("Not connected") :
|
CL.Message = KernelInstance->CurrentConnection() == NullConnectionId ? QObject::tr("Not connected") :
|
||||||
QObject::tr("Connected");
|
QObject::tr("Connected");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -132,14 +132,14 @@ namespace Qv2ray::components::plugins
|
|||||||
case 301:
|
case 301:
|
||||||
{
|
{
|
||||||
// Total Upload
|
// Total Upload
|
||||||
CL.Message = FormatBytes(get<0>(GetConnectionUsageAmount(ConnectionManager->CurrentConnection())));
|
CL.Message = FormatBytes(get<0>(GetConnectionUsageAmount(KernelInstance->CurrentConnection())));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 302:
|
case 302:
|
||||||
{
|
{
|
||||||
// Total download
|
// Total download
|
||||||
CL.Message = FormatBytes(get<1>(GetConnectionUsageAmount(ConnectionManager->CurrentConnection())));
|
CL.Message = FormatBytes(get<1>(GetConnectionUsageAmount(KernelInstance->CurrentConnection())));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,7 +160,7 @@ namespace Qv2ray::components::plugins
|
|||||||
case 305:
|
case 305:
|
||||||
{
|
{
|
||||||
// Connection latency
|
// Connection latency
|
||||||
CL.Message = QSTRN(GetConnectionLatency(ConnectionManager->CurrentConnection())) + " ms";
|
CL.Message = QSTRN(GetConnectionLatency(KernelInstance->CurrentConnection())) + " ms";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -43,7 +43,7 @@ namespace Qv2ray::components::plugins::Toolbar
|
|||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
LOG(MODULE_PLUGIN, "Closing a broken socket.")
|
LOG(MODULE_PLUGINHOST, "Closing a broken socket.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void DataMessageQThread()
|
void DataMessageQThread()
|
||||||
@ -66,8 +66,8 @@ namespace Qv2ray::components::plugins::Toolbar
|
|||||||
while (!isExiting)
|
while (!isExiting)
|
||||||
{
|
{
|
||||||
bool result = server->waitForNewConnection(5000, &timeOut);
|
bool result = server->waitForNewConnection(5000, &timeOut);
|
||||||
DEBUG(MODULE_PLUGIN, "Plugin thread listening failed: " + server->errorString())
|
DEBUG(MODULE_PLUGINHOST, "Plugin thread listening failed: " + server->errorString())
|
||||||
DEBUG(MODULE_PLUGIN, "waitForNewConnection: " + QString(result ? "true" : "false") + ", " + QString(timeOut ? "true" : "false"))
|
DEBUG(MODULE_PLUGINHOST, "waitForNewConnection: " + QString(result ? "true" : "false") + ", " + QString(timeOut ? "true" : "false"))
|
||||||
}
|
}
|
||||||
|
|
||||||
server->close();
|
server->close();
|
||||||
@ -85,7 +85,7 @@ namespace Qv2ray::components::plugins::Toolbar
|
|||||||
|
|
||||||
if (linuxWorkerThread->isRunning())
|
if (linuxWorkerThread->isRunning())
|
||||||
{
|
{
|
||||||
LOG(MODULE_PLUGIN, "Waiting for linuxWorkerThread to stop.")
|
LOG(MODULE_PLUGINHOST, "Waiting for linuxWorkerThread to stop.")
|
||||||
linuxWorkerThread->wait();
|
linuxWorkerThread->wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ namespace Qv2ray::components::plugins::Toolbar
|
|||||||
|
|
||||||
if (hThread == nullptr)
|
if (hThread == nullptr)
|
||||||
{
|
{
|
||||||
LOG(MODULE_PLUGIN, "CreateThread failed, GLE=" + QSTRN(GetLastError()))
|
LOG(MODULE_PLUGINHOST, "CreateThread failed, GLE=" + QSTRN(GetLastError()))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -52,7 +52,7 @@ namespace Qv2ray::components::plugins::Toolbar
|
|||||||
|
|
||||||
if (hPipe == INVALID_HANDLE_VALUE)
|
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);
|
return static_cast<DWORD>(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,12 +60,12 @@ namespace Qv2ray::components::plugins::Toolbar
|
|||||||
|
|
||||||
if (fConnected)
|
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);
|
ThreadHandle = CreateThread(nullptr, 0, InstanceThread, hPipe, 0, &dwThreadId);
|
||||||
|
|
||||||
if (ThreadHandle == nullptr)
|
if (ThreadHandle == nullptr)
|
||||||
{
|
{
|
||||||
LOG(MODULE_PLUGIN, "CreateThread failed, GLE=" + QSTRN(GetLastError()))
|
LOG(MODULE_PLUGINHOST, "CreateThread failed, GLE=" + QSTRN(GetLastError()))
|
||||||
return static_cast<DWORD>(-1);
|
return static_cast<DWORD>(-1);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -93,11 +93,11 @@ namespace Qv2ray::components::plugins::Toolbar
|
|||||||
{
|
{
|
||||||
if (GetLastError() == ERROR_BROKEN_PIPE)
|
if (GetLastError() == ERROR_BROKEN_PIPE)
|
||||||
{
|
{
|
||||||
LOG(MODULE_PLUGIN, "InstanceThread: client disconnected, GLE=" + QSTRN(GetLastError()))
|
LOG(MODULE_PLUGINHOST, "InstanceThread: client disconnected, GLE=" + QSTRN(GetLastError()))
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LOG(MODULE_PLUGIN, "InstanceThread ReadFile failed, GLE=" + QSTRN(GetLastError()))
|
LOG(MODULE_PLUGINHOST, "InstanceThread ReadFile failed, GLE=" + QSTRN(GetLastError()))
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -120,7 +120,7 @@ namespace Qv2ray::components::plugins::Toolbar
|
|||||||
|
|
||||||
if (!fSuccess || cbReplyBytes != cbWritten)
|
if (!fSuccess || cbReplyBytes != cbWritten)
|
||||||
{
|
{
|
||||||
LOG(MODULE_PLUGIN, "InstanceThread WriteFile failed, GLE=" + QSTRN(GetLastError()))
|
LOG(MODULE_PLUGINHOST, "InstanceThread WriteFile failed, GLE=" + QSTRN(GetLastError()))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#include "QvProxyConfigurator.hpp"
|
#include "QvProxyConfigurator.hpp"
|
||||||
|
|
||||||
#include "common/QvHelpers.hpp"
|
#include "common/QvHelpers.hpp"
|
||||||
|
#include "components/plugins/QvPluginHost.hpp"
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
#include <WinInet.h>
|
#include <WinInet.h>
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
@ -188,24 +189,18 @@ namespace Qv2ray::components::proxy
|
|||||||
}
|
}
|
||||||
#endif
|
#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")
|
LOG(MODULE_PROXY, "Setting up System Proxy")
|
||||||
bool hasHTTP = (httpPort != 0);
|
bool hasHTTP = (httpPort != 0);
|
||||||
bool hasSOCKS = (socksPort != 0);
|
bool hasSOCKS = (socksPort != 0);
|
||||||
|
|
||||||
if (!(hasHTTP || hasSOCKS || usePAC))
|
if (!(hasHTTP || hasSOCKS))
|
||||||
{
|
{
|
||||||
LOG(MODULE_PROXY, "Nothing?")
|
LOG(MODULE_PROXY, "Nothing?")
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (usePAC)
|
|
||||||
{
|
|
||||||
LOG(MODULE_PROXY, "Qv2ray will set system proxy to use PAC file")
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (hasHTTP)
|
if (hasHTTP)
|
||||||
{
|
{
|
||||||
LOG(MODULE_PROXY, "Qv2ray will set system proxy to use HTTP")
|
LOG(MODULE_PROXY, "Qv2ray will set system proxy to use HTTP")
|
||||||
@ -215,19 +210,9 @@ namespace Qv2ray::components::proxy
|
|||||||
{
|
{
|
||||||
LOG(MODULE_PROXY, "Qv2ray will set system proxy to use SOCKS")
|
LOG(MODULE_PROXY, "Qv2ray will set system proxy to use SOCKS")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
QString __a;
|
QString __a = (hasHTTP ? "http://" : "socks5://") + address + ":" + QSTRN(hasHTTP ? httpPort : socksPort);
|
||||||
|
|
||||||
if (usePAC)
|
|
||||||
{
|
|
||||||
__a = address;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
__a = (hasHTTP ? "http://" : "socks5://") + address + ":" + QSTRN(httpPort);
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG(MODULE_PROXY, "Windows proxy string: " + __a)
|
LOG(MODULE_PROXY, "Windows proxy string: " + __a)
|
||||||
auto proxyStrW = new WCHAR[__a.length() + 1];
|
auto proxyStrW = new WCHAR[__a.length() + 1];
|
||||||
@ -235,7 +220,7 @@ namespace Qv2ray::components::proxy
|
|||||||
//
|
//
|
||||||
__QueryProxyOptions();
|
__QueryProxyOptions();
|
||||||
|
|
||||||
if (!__SetProxyOptions(proxyStrW, usePAC))
|
if (!__SetProxyOptions(proxyStrW, false))
|
||||||
{
|
{
|
||||||
LOG(MODULE_PROXY, "Failed to set proxy.")
|
LOG(MODULE_PROXY, "Failed to set proxy.")
|
||||||
}
|
}
|
||||||
@ -243,28 +228,13 @@ namespace Qv2ray::components::proxy
|
|||||||
__QueryProxyOptions();
|
__QueryProxyOptions();
|
||||||
#elif defined(Q_OS_LINUX)
|
#elif defined(Q_OS_LINUX)
|
||||||
QStringList actions;
|
QStringList actions;
|
||||||
auto proxyMode = usePAC ? "auto" : "manual";
|
auto proxyMode = "manual";
|
||||||
actions << QString("gsettings set org.gnome.system.proxy mode '%1'").arg(proxyMode);
|
actions << QString("gsettings set org.gnome.system.proxy mode '%1'").arg(proxyMode);
|
||||||
bool isKDE = qEnvironmentVariable("XDG_SESSION_DESKTOP") == "KDE";
|
bool isKDE = qEnvironmentVariable("XDG_SESSION_DESKTOP") == "KDE";
|
||||||
if (isKDE)
|
if (isKDE)
|
||||||
{
|
{
|
||||||
LOG(MODULE_PROXY, "KDE detected")
|
LOG(MODULE_PROXY, "KDE detected")
|
||||||
}
|
}
|
||||||
//
|
|
||||||
if (usePAC)
|
|
||||||
{
|
|
||||||
actions << QString("gsettings set org.gnome.system.proxy autoconfig-url '%1'").arg(address);
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (isKDE)
|
if (isKDE)
|
||||||
{
|
{
|
||||||
actions << QString("kwriteconfig5 --file " + QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) +
|
actions << QString("kwriteconfig5 --file " + QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) +
|
||||||
@ -304,7 +274,6 @@ namespace Qv2ray::components::proxy
|
|||||||
.arg(QSTRN(socksPort));
|
.arg(QSTRN(socksPort));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// note: do not use std::all_of / any_of / none_of,
|
// note: do not use std::all_of / any_of / none_of,
|
||||||
// because those are short-circuit and cannot guarantee atomicity.
|
// because those are short-circuit and cannot guarantee atomicity.
|
||||||
@ -326,13 +295,6 @@ namespace Qv2ray::components::proxy
|
|||||||
{
|
{
|
||||||
LOG(MODULE_PROXY, "Setting proxy for interface: " + service)
|
LOG(MODULE_PROXY, "Setting proxy for interface: " + service)
|
||||||
|
|
||||||
if (usePAC)
|
|
||||||
{
|
|
||||||
QProcess::execute("/usr/sbin/networksetup -setautoproxystate " + service + " on");
|
|
||||||
QProcess::execute("/usr/sbin/networksetup -setautoproxyurl " + service + " " + address);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (hasHTTP)
|
if (hasHTTP)
|
||||||
{
|
{
|
||||||
QProcess::execute("/usr/sbin/networksetup -setwebproxystate " + service + " on");
|
QProcess::execute("/usr/sbin/networksetup -setwebproxystate " + service + " on");
|
||||||
@ -347,9 +309,17 @@ namespace Qv2ray::components::proxy
|
|||||||
QProcess::execute("/usr/sbin/networksetup -setsocksfirewallproxy " + service + " " + address + " " + QSTRN(socksPort));
|
QProcess::execute("/usr/sbin/networksetup -setsocksfirewallproxy " + service + " " + address + " " + QSTRN(socksPort));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
#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()
|
void ClearSystemProxy()
|
||||||
@ -403,5 +373,9 @@ namespace Qv2ray::components::proxy
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
//
|
||||||
|
// Trigger plugin events
|
||||||
|
PluginHost->Send_SystemProxyEvent(
|
||||||
|
Events::SystemProxy::EventObject{ {}, Events::SystemProxy::SystemProxyStateType::SystemProxyState_ClearProxy });
|
||||||
}
|
}
|
||||||
} // namespace Qv2ray::components::proxy
|
} // namespace Qv2ray::components::proxy
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
namespace Qv2ray::components::proxy
|
namespace Qv2ray::components::proxy
|
||||||
{
|
{
|
||||||
void ClearSystemProxy();
|
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
|
} // namespace Qv2ray::components::proxy
|
||||||
|
|
||||||
using namespace Qv2ray::components;
|
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> DomainsBlock{ "geosite:category-ads-all" };
|
||||||
const inline QList<QString> DomainsProxy{ "geosite:google", "geosite:github", "geosite:netflix", "geosite:steam",
|
const inline QList<QString> DomainsProxy{ "geosite:google", "geosite:github", "geosite:netflix", "geosite:steam",
|
||||||
"geosite:telegram", "geosite:tumblr", "geosite:speedtest", "geosite:bbc",
|
"geosite:telegram", "geosite:tumblr", "domain:naver.com", "geosite:bbc",
|
||||||
"domain:gvt1.com", "domain:textnow.com", "domain:twitch.tv", "domain:wikileaks.org",
|
"domain:gvt1.com", "domain:textnow.com", "domain:twitch.tv", "domain:wikileaks.org" };
|
||||||
"domain:naver.com" };
|
|
||||||
|
|
||||||
const inline QList<QString> IPsProxy{
|
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",
|
"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)
|
QvUpdateChecker::QvUpdateChecker(QObject *parent) : QObject(parent)
|
||||||
{
|
{
|
||||||
requestHelper = new QvHttpRequestHelper(this);
|
requestHelper = new QvHttpRequestHelper(this);
|
||||||
connect(requestHelper, &QvHttpRequestHelper::httpRequestFinished, this, &QvUpdateChecker::VersionUpdate);
|
connect(requestHelper, &QvHttpRequestHelper::OnRequestFinished, this, &QvUpdateChecker::VersionUpdate);
|
||||||
}
|
}
|
||||||
QvUpdateChecker::~QvUpdateChecker()
|
QvUpdateChecker::~QvUpdateChecker()
|
||||||
{
|
{
|
||||||
@ -29,7 +29,7 @@ namespace Qv2ray::components
|
|||||||
#ifndef DISABLE_AUTO_UPDATE
|
#ifndef DISABLE_AUTO_UPDATE
|
||||||
auto updateChannel = GlobalConfig.updateConfig.updateChannel;
|
auto updateChannel = GlobalConfig.updateConfig.updateChannel;
|
||||||
LOG(MODULE_NETWORK, "Start checking update for channel ID: " + QSTRN(updateChannel))
|
LOG(MODULE_NETWORK, "Start checking update for channel ID: " + QSTRN(updateChannel))
|
||||||
requestHelper->get(UpdateChannelLink[updateChannel]);
|
requestHelper->AsyncGet(UpdateChannelLink[updateChannel]);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
void QvUpdateChecker::VersionUpdate(QByteArray &data)
|
void QvUpdateChecker::VersionUpdate(QByteArray &data)
|
||||||
|
@ -56,7 +56,11 @@ namespace Qv2ray::core
|
|||||||
}
|
}
|
||||||
else
|
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;
|
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
|
} // namespace Qv2ray::core
|
||||||
|
@ -43,9 +43,11 @@ namespace Qv2ray::core
|
|||||||
const QString GetDisplayName(const ConnectionId &id, int limit = -1);
|
const QString GetDisplayName(const ConnectionId &id, int limit = -1);
|
||||||
const QString GetDisplayName(const GroupId &id, int limit = -1);
|
const QString GetDisplayName(const GroupId &id, int limit = -1);
|
||||||
//
|
//
|
||||||
|
|
||||||
const GroupId GetConnectionGroupId(const ConnectionId &id);
|
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
|
} // namespace Qv2ray::core
|
||||||
|
|
||||||
using namespace Qv2ray::core;
|
using namespace Qv2ray::core;
|
||||||
|
@ -308,7 +308,7 @@ namespace Qv2ray::core::connection
|
|||||||
if (!root.contains("inbounds") || root.value("inbounds").toArray().empty())
|
if (!root.contains("inbounds") || root.value("inbounds").toArray().empty())
|
||||||
{
|
{
|
||||||
INBOUNDS inboundsList;
|
INBOUNDS inboundsList;
|
||||||
|
QJsonObject sniffingObject{ { "enabled", false } };
|
||||||
// HTTP Inbound
|
// HTTP Inbound
|
||||||
if (GlobalConfig.inboundConfig.useHTTP)
|
if (GlobalConfig.inboundConfig.useHTTP)
|
||||||
{
|
{
|
||||||
@ -317,6 +317,7 @@ namespace Qv2ray::core::connection
|
|||||||
httpInBoundObject.insert("port", GlobalConfig.inboundConfig.http_port);
|
httpInBoundObject.insert("port", GlobalConfig.inboundConfig.http_port);
|
||||||
httpInBoundObject.insert("protocol", "http");
|
httpInBoundObject.insert("protocol", "http");
|
||||||
httpInBoundObject.insert("tag", "http_IN");
|
httpInBoundObject.insert("tag", "http_IN");
|
||||||
|
httpInBoundObject.insert("sniffing", sniffingObject);
|
||||||
|
|
||||||
if (GlobalConfig.inboundConfig.http_useAuth)
|
if (GlobalConfig.inboundConfig.http_useAuth)
|
||||||
{
|
{
|
||||||
@ -335,6 +336,7 @@ namespace Qv2ray::core::connection
|
|||||||
socksInBoundObject.insert("port", GlobalConfig.inboundConfig.socks_port);
|
socksInBoundObject.insert("port", GlobalConfig.inboundConfig.socks_port);
|
||||||
socksInBoundObject.insert("protocol", "socks");
|
socksInBoundObject.insert("protocol", "socks");
|
||||||
socksInBoundObject.insert("tag", "socks_IN");
|
socksInBoundObject.insert("tag", "socks_IN");
|
||||||
|
socksInBoundObject.insert("sniffing", sniffingObject);
|
||||||
auto socksInSettings = GenerateSocksIN(GlobalConfig.inboundConfig.socks_useAuth ? "password" : "noauth",
|
auto socksInSettings = GenerateSocksIN(GlobalConfig.inboundConfig.socks_useAuth ? "password" : "noauth",
|
||||||
QList<AccountObject>() << GlobalConfig.inboundConfig.socksAccount,
|
QList<AccountObject>() << GlobalConfig.inboundConfig.socksAccount,
|
||||||
GlobalConfig.inboundConfig.socksUDP, GlobalConfig.inboundConfig.socksLocalIP);
|
GlobalConfig.inboundConfig.socksUDP, GlobalConfig.inboundConfig.socksLocalIP);
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "Generation.hpp"
|
#include "Generation.hpp"
|
||||||
#include "common/QvHelpers.hpp"
|
#include "common/QvHelpers.hpp"
|
||||||
|
#include "components/plugins/QvPluginHost.hpp"
|
||||||
#include "core/CoreUtils.hpp"
|
#include "core/CoreUtils.hpp"
|
||||||
#include "core/handler/ConfigHandler.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> ConvertConfigFromString(const QString &link, QString *prefix, QString *errMessage, QString *newGroupName)
|
||||||
{
|
{
|
||||||
QMultiHash<QString, CONFIGROOT> config;
|
QMultiHash<QString, CONFIGROOT> connectionConf;
|
||||||
if (link.startsWith("vmess://"))
|
if (link.startsWith("vmess://"))
|
||||||
{
|
{
|
||||||
auto conf = ConvertConfigFromVMessString(link, prefix, errMessage);
|
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://"))
|
else if (link.startsWith("ss://"))
|
||||||
{
|
{
|
||||||
auto conf = ConvertConfigFromSSString(link, prefix, errMessage);
|
auto conf = ConvertConfigFromSSString(link, prefix, errMessage);
|
||||||
config.insert(*prefix, conf);
|
connectionConf.insert(*prefix, conf);
|
||||||
}
|
}
|
||||||
else if (link.startsWith("ssd://"))
|
else if (link.startsWith("ssd://"))
|
||||||
{
|
{
|
||||||
QStringList errMessageList;
|
QStringList errMessageList;
|
||||||
config = ConvertConfigFromSSDString(link, newGroupName, &errMessageList);
|
connectionConf = ConvertConfigFromSSDString(link, newGroupName, &errMessageList);
|
||||||
*errMessage = errMessageList.join(NEWLINE);
|
*errMessage = errMessageList.join(NEWLINE);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
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.");
|
*errMessage = QObject::tr("Unsupported share link format.");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return config;
|
return connectionConf;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString ConvertConfigToString(const ConnectionId &id, bool isSip002)
|
const QString ConvertConfigToString(const ConnectionId &id, bool isSip002)
|
||||||
@ -45,51 +78,51 @@ namespace Qv2ray::core::connection
|
|||||||
return QV2RAY_SERIALIZATION_COMPLEX_CONFIG_PLACEHOLDER;
|
return QV2RAY_SERIALIZATION_COMPLEX_CONFIG_PLACEHOLDER;
|
||||||
}
|
}
|
||||||
auto server = ConnectionManager->GetConnectionRoot(id);
|
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());
|
const auto outbound = OUTBOUND(server["outbounds"].toArray().first().toObject());
|
||||||
auto type = outbound["protocol"].toString();
|
const auto type = outbound["protocol"].toString();
|
||||||
|
const auto &settings = outbound["settings"].toObject();
|
||||||
QString sharelink = "";
|
QString sharelink = "";
|
||||||
|
|
||||||
if (type == "vmess")
|
if (type == "vmess")
|
||||||
{
|
{
|
||||||
auto vmessServer =
|
auto vmessServer = StructFromJsonString<VMessServerObject>(JsonToString(settings["vnext"].toArray().first().toObject()));
|
||||||
StructFromJsonString<VMessServerObject>(JsonToString(outbound["settings"].toObject()["vnext"].toArray().first().toObject()));
|
|
||||||
auto transport = StructFromJsonString<StreamSettingsObject>(JsonToString(outbound["streamSettings"].toObject()));
|
auto transport = StructFromJsonString<StreamSettingsObject>(JsonToString(outbound["streamSettings"].toObject()));
|
||||||
sharelink = vmess::ConvertConfigToVMessString(transport, vmessServer, alias);
|
sharelink = vmess::ConvertConfigToVMessString(transport, vmessServer, alias);
|
||||||
}
|
}
|
||||||
else if (type == "shadowsocks")
|
else if (type == "shadowsocks")
|
||||||
{
|
{
|
||||||
auto ssServer = StructFromJsonString<ShadowSocksServerObject>(
|
auto ssServer = StructFromJsonString<ShadowSocksServerObject>(JsonToString(settings["servers"].toArray().first().toObject()));
|
||||||
JsonToString(outbound["settings"].toObject()["servers"].toArray().first().toObject()));
|
|
||||||
sharelink = ss::ConvertConfigToSSString(ssServer, alias, isSip002);
|
sharelink = ss::ConvertConfigToSSString(ssServer, alias, isSip002);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!type.isEmpty())
|
if (type.isEmpty())
|
||||||
{
|
{
|
||||||
// DEBUG(MODULE_CONNECTION, "WARNING: Unsupported outbound type: " + type)
|
DEBUG(MODULE_CONNECTION, "WARNING: Empty outbound type.")
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
DEBUG(MODULE_CONNECTION, "WARNING: Empty outbound type.")
|
bool ok = false;
|
||||||
|
sharelink = PluginHost->TrySerializeShareLink(type, settings, alias, groupName, &ok);
|
||||||
|
Q_UNUSED(ok)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return sharelink;
|
return sharelink;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString DecodeSubscriptionString(QByteArray arr)
|
QString DecodeSubscriptionString(const QByteArray &arr)
|
||||||
{
|
{
|
||||||
// String may start with: vmess:// and ss://
|
// String may start with: vmess:// and ss://
|
||||||
// We only process vmess:// here
|
// We only process vmess:// here
|
||||||
// Some subscription providers may use plain vmess:// saperated by
|
// Some subscription providers may use plain vmess:// saperated by
|
||||||
// lines But others may use base64 of above.
|
// lines But others may use base64 of above.
|
||||||
auto result = QString::fromUtf8(arr).trimmed();
|
auto result = QString::fromUtf8(arr).trimmed();
|
||||||
return result.startsWith("vmess://") ? result : Base64Decode(result);
|
return result.contains("://") ? result : Base64Decode(result);
|
||||||
}
|
}
|
||||||
} // namespace Serialization
|
} // namespace Serialization
|
||||||
} // namespace Qv2ray::core::connection
|
} // 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)");
|
inline auto QV2RAY_SSD_DEFAULT_NAME_PATTERN = QObject::tr("%1 - %2 (rate %3)");
|
||||||
//
|
//
|
||||||
// General
|
// General
|
||||||
QString DecodeSubscriptionString(QByteArray arr);
|
QString DecodeSubscriptionString(const QByteArray &arr);
|
||||||
QMultiHash<QString, CONFIGROOT> ConvertConfigFromString(const QString &link, QString *aliasPrefix, QString *errMessage,
|
QMultiHash<QString, CONFIGROOT> ConvertConfigFromString(const QString &link, QString *aliasPrefix, QString *errMessage,
|
||||||
QString *newGroupName = nullptr);
|
QString *newGroupName = nullptr);
|
||||||
const QString ConvertConfigToString(const ConnectionId &id, bool isSip002 = false);
|
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
|
// VMess URI Protocol
|
||||||
namespace vmess
|
namespace vmess
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
#include "Serialization.hpp"
|
#include "Serialization.hpp"
|
||||||
#include "common/QvHelpers.hpp"
|
#include "common/QvHelpers.hpp"
|
||||||
#include "core/CoreUtils.hpp"
|
#include "core/CoreUtils.hpp"
|
||||||
#include "core/handler/ConfigHandler.hpp"
|
|
||||||
|
|
||||||
namespace Qv2ray::core::connection
|
namespace Qv2ray::core::connection
|
||||||
{
|
{
|
||||||
|
@ -83,7 +83,7 @@ namespace Qv2ray::core::connection::Serialization
|
|||||||
}
|
}
|
||||||
|
|
||||||
// decode base64
|
// 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());
|
const auto decodedJSON = QByteArray::fromBase64(ssdURIBody.toUtf8());
|
||||||
|
|
||||||
if (decodedJSON.length() == 0)
|
if (decodedJSON.length() == 0)
|
||||||
@ -161,13 +161,12 @@ namespace Qv2ray::core::connection::Serialization
|
|||||||
{
|
{
|
||||||
ssObject.port = port;
|
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;
|
ssObject.port = currPort;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
*logList << QObject::tr("Invalid port encountered. using fallback value.");
|
|
||||||
ssObject.port = port;
|
ssObject.port = port;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,7 +185,6 @@ namespace Qv2ray::core::connection::Serialization
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
*logList << QObject::tr("Invalid name encountered. using fallback value.");
|
|
||||||
nodeName = QString("%1:%2").arg(ssObject.address).arg(ssObject.port);
|
nodeName = QString("%1:%2").arg(ssObject.address).arg(ssObject.port);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,7 +199,7 @@ namespace Qv2ray::core::connection::Serialization
|
|||||||
}
|
}
|
||||||
else if (!serverObject["ratio"].isUndefined())
|
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.
|
// format the total name of the node.
|
||||||
@ -209,10 +207,8 @@ namespace Qv2ray::core::connection::Serialization
|
|||||||
// appending to the total list
|
// appending to the total list
|
||||||
CONFIGROOT root;
|
CONFIGROOT root;
|
||||||
OUTBOUNDS outbounds;
|
OUTBOUNDS outbounds;
|
||||||
outbounds.append(
|
outbounds.append(GenerateOutboundEntry("shadowsocks", GenerateShadowSocksOUT({ ssObject }), {}));
|
||||||
GenerateOutboundEntry("shadowsocks", GenerateShadowSocksOUT(QList<ShadowSocksServerObject>{ ssObject }), QJsonObject()));
|
|
||||||
JADD(outbounds)
|
JADD(outbounds)
|
||||||
|
|
||||||
serverList.insertMulti(totalName, root);
|
serverList.insertMulti(totalName, root);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#include "ConfigHandler.hpp"
|
#include "ConfigHandler.hpp"
|
||||||
|
|
||||||
#include "common/QvHelpers.hpp"
|
#include "common/QvHelpers.hpp"
|
||||||
|
#include "components/plugins/QvPluginHost.hpp"
|
||||||
#include "core/connection/Serialization.hpp"
|
#include "core/connection/Serialization.hpp"
|
||||||
#include "core/settings/SettingsBackend.hpp"
|
#include "core/settings/SettingsBackend.hpp"
|
||||||
|
|
||||||
@ -12,7 +13,7 @@ namespace Qv2ray::core::handlers
|
|||||||
DEBUG(MODULE_CORE_HANDLER, "ConnectionHandler Constructor.")
|
DEBUG(MODULE_CORE_HANDLER, "ConnectionHandler Constructor.")
|
||||||
|
|
||||||
// Do we need to check how many of them are loaded?
|
// 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++)
|
for (auto i = 0; i < GlobalConfig.connections.count(); i++)
|
||||||
{
|
{
|
||||||
auto const &id = ConnectionId(GlobalConfig.connections.keys().at(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].displayName = tr("Default Group");
|
||||||
groups[DefaultGroupId].isSubscription = false;
|
groups[DefaultGroupId].isSubscription = false;
|
||||||
//
|
//
|
||||||
vCoreInstance = new V2rayKernelInstance();
|
kernelHandler = new KernelInstanceHandler(this);
|
||||||
connect(vCoreInstance, &V2rayKernelInstance::OnProcessErrored, this, &QvConfigHandler::OnVCoreCrashed);
|
connect(kernelHandler, &KernelInstanceHandler::OnCrashed, this, &QvConfigHandler::OnKernelCrashed_p);
|
||||||
connect(vCoreInstance, &V2rayKernelInstance::OnNewStatsDataArrived, this, &QvConfigHandler::OnStatsDataArrived);
|
connect(kernelHandler, &KernelInstanceHandler::OnStatsDataAvailable, this, &QvConfigHandler::OnStatsDataArrived_p);
|
||||||
// Directly connected to a signal.
|
connect(kernelHandler, &KernelInstanceHandler::OnKernelLogAvailable, this, &QvConfigHandler::OnKernelLogAvailable);
|
||||||
connect(vCoreInstance, &V2rayKernelInstance::OnProcessOutputReadyRead, this, &QvConfigHandler::OnVCoreLogAvailable);
|
connect(kernelHandler, &KernelInstanceHandler::OnConnected, this, &QvConfigHandler::OnConnected);
|
||||||
|
connect(kernelHandler, &KernelInstanceHandler::OnDisconnected, this, &QvConfigHandler::OnDisconnected);
|
||||||
//
|
//
|
||||||
tcpingHelper = new QvTCPingHelper(5, this);
|
tcpingHelper = new QvTCPingHelper(5, this);
|
||||||
httpHelper = new QvHttpRequestHelper(this);
|
httpHelper = new QvHttpRequestHelper(this);
|
||||||
connect(tcpingHelper, &QvTCPingHelper::OnLatencyTestCompleted, this, &QvConfigHandler::OnLatencyDataArrived);
|
connect(tcpingHelper, &QvTCPingHelper::OnLatencyTestCompleted, this, &QvConfigHandler::OnLatencyDataArrived_p);
|
||||||
//
|
//
|
||||||
// Save per 2 minutes.
|
// Save per 1 minutes.
|
||||||
saveTimerId = startTimer(2 * 60 * 1000);
|
saveTimerId = startTimer(1 * 60 * 1000);
|
||||||
// Do not ping all...
|
// Do not ping all...
|
||||||
// pingAllTimerId = startTimer(5 * 60 * 1000);
|
|
||||||
pingConnectionTimerId = startTimer(60 * 1000);
|
pingConnectionTimerId = startTimer(60 * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,9 +139,10 @@ namespace Qv2ray::core::handlers
|
|||||||
}
|
}
|
||||||
else if (event->timerId() == pingConnectionTimerId)
|
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;
|
return NullGroupId;
|
||||||
}
|
}
|
||||||
|
void QvConfigHandler::ClearGroupUsage(const GroupId &id)
|
||||||
const optional<QString> QvConfigHandler::ClearConnectionUsage(const ConnectionId &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].upLinkData = 0;
|
||||||
connections[id].downLinkData = 0;
|
connections[id].downLinkData = 0;
|
||||||
emit OnStatsAvailable(id, 0, 0, 0, 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)
|
const optional<QString> QvConfigHandler::RenameConnection(const ConnectionId &id, const QString &newName)
|
||||||
{
|
{
|
||||||
CheckConnectionExistance(id);
|
CheckConnectionExistance(id);
|
||||||
OnConnectionRenamed(id, connections[id].displayName, newName);
|
OnConnectionRenamed(id, connections[id].displayName, newName);
|
||||||
|
PluginHost->Send_ConnectionEvent({ newName, connections[id].displayName, Events::ConnectionEntry::ConnectionEvent_Renamed });
|
||||||
connections[id].displayName = newName;
|
connections[id].displayName = newName;
|
||||||
CHSaveConfigData_p();
|
CHSaveConfigData_p();
|
||||||
return {};
|
return {};
|
||||||
@ -232,6 +242,7 @@ namespace Qv2ray::core::handlers
|
|||||||
QFile connectionFile((groups[groupId].isSubscription ? QV2RAY_SUBSCRIPTION_DIR : QV2RAY_CONNECTIONS_DIR) + groupId.toString() + "/" +
|
QFile connectionFile((groups[groupId].isSubscription ? QV2RAY_SUBSCRIPTION_DIR : QV2RAY_CONNECTIONS_DIR) + groupId.toString() + "/" +
|
||||||
id.toString() + QV2RAY_CONFIG_FILE_EXTENSION);
|
id.toString() + QV2RAY_CONFIG_FILE_EXTENSION);
|
||||||
//
|
//
|
||||||
|
PluginHost->Send_ConnectionEvent({ connections[id].displayName, "", Events::ConnectionEntry::ConnectionEvent_Deleted });
|
||||||
connections.remove(id);
|
connections.remove(id);
|
||||||
groups[groupId].connections.removeAll(id);
|
groups[groupId].connections.removeAll(id);
|
||||||
//
|
//
|
||||||
@ -279,6 +290,8 @@ namespace Qv2ray::core::handlers
|
|||||||
groups[newGroupId].connections.append(id);
|
groups[newGroupId].connections.append(id);
|
||||||
connections[id].groupId = newGroupId;
|
connections[id].groupId = newGroupId;
|
||||||
//
|
//
|
||||||
|
PluginHost->Send_ConnectionEvent({ connections[id].displayName, "", Events::ConnectionEntry::ConnectionEvent_Updated });
|
||||||
|
//
|
||||||
emit OnConnectionGroupChanged(id, oldgid, newGroupId);
|
emit OnConnectionGroupChanged(id, oldgid, newGroupId);
|
||||||
//
|
//
|
||||||
return {};
|
return {};
|
||||||
@ -308,6 +321,8 @@ namespace Qv2ray::core::handlers
|
|||||||
QDir(QV2RAY_CONNECTIONS_DIR + id.toString()).removeRecursively();
|
QDir(QV2RAY_CONNECTIONS_DIR + id.toString()).removeRecursively();
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
|
PluginHost->Send_ConnectionEvent({ groups[id].displayName, "", Events::ConnectionEntry::ConnectionEvent_Deleted });
|
||||||
|
//
|
||||||
groups.remove(id);
|
groups.remove(id);
|
||||||
CHSaveConfigData_p();
|
CHSaveConfigData_p();
|
||||||
emit OnGroupDeleted(id, list);
|
emit OnGroupDeleted(id, list);
|
||||||
@ -321,55 +336,41 @@ namespace Qv2ray::core::handlers
|
|||||||
const optional<QString> QvConfigHandler::StartConnection(const ConnectionId &id)
|
const optional<QString> QvConfigHandler::StartConnection(const ConnectionId &id)
|
||||||
{
|
{
|
||||||
CheckConnectionExistance(id);
|
CheckConnectionExistance(id);
|
||||||
|
connections[id].lastConnected = system_clock::to_time_t(system_clock::now());
|
||||||
if (currentConnectionId != NullConnectionId)
|
|
||||||
{
|
|
||||||
StopConnection();
|
|
||||||
}
|
|
||||||
|
|
||||||
CONFIGROOT root = GetConnectionRoot(id);
|
CONFIGROOT root = GetConnectionRoot(id);
|
||||||
return CHStartConnection_p(id, root);
|
return kernelHandler->StartConnection(id, root);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QvConfigHandler::RestartConnection() // const ConnectionId &id
|
void QvConfigHandler::RestartConnection() // const ConnectionId &id
|
||||||
{
|
{
|
||||||
auto conn = currentConnectionId;
|
kernelHandler->RestartConnection();
|
||||||
if (conn != NullConnectionId)
|
|
||||||
{
|
|
||||||
StopConnection();
|
|
||||||
StartConnection(conn);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void QvConfigHandler::StopConnection() // const ConnectionId &id
|
void QvConfigHandler::StopConnection() // const ConnectionId &id
|
||||||
{
|
{
|
||||||
// Currently just simply stop it.
|
kernelHandler->StopConnection();
|
||||||
//_UNUSED(id)
|
|
||||||
// if (currentConnectionId == id) {
|
|
||||||
//}
|
|
||||||
CHStopConnection_p();
|
|
||||||
CHSaveConfigData_p();
|
CHSaveConfigData_p();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QvConfigHandler::IsConnected(const ConnectionId &id) const
|
bool QvConfigHandler::IsConnected(const ConnectionId &id) const
|
||||||
{
|
{
|
||||||
CheckConnectionExistanceEx(id, false);
|
return kernelHandler->isConnected(id);
|
||||||
return currentConnectionId == 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()
|
QvConfigHandler::~QvConfigHandler()
|
||||||
{
|
{
|
||||||
LOG(MODULE_CORE_HANDLER, "Triggering save settings from destructor")
|
LOG(MODULE_CORE_HANDLER, "Triggering save settings from destructor")
|
||||||
CHSaveConfigData_p();
|
delete kernelHandler;
|
||||||
|
|
||||||
if (vCoreInstance->KernelStarted)
|
|
||||||
{
|
|
||||||
vCoreInstance->StopConnection();
|
|
||||||
LOG(MODULE_CORE_HANDLER, "Stopped connection from destructor.")
|
|
||||||
}
|
|
||||||
|
|
||||||
delete vCoreInstance;
|
|
||||||
delete httpHelper;
|
delete httpHelper;
|
||||||
|
CHSaveConfigData_p();
|
||||||
}
|
}
|
||||||
|
|
||||||
const CONFIGROOT QvConfigHandler::GetConnectionRoot(const ConnectionId &id) const
|
const CONFIGROOT QvConfigHandler::GetConnectionRoot(const ConnectionId &id) const
|
||||||
@ -378,7 +379,7 @@ namespace Qv2ray::core::handlers
|
|||||||
return connectionRootCache.value(id);
|
return connectionRootCache.value(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QvConfigHandler::OnLatencyDataArrived(const QvTCPingResultObject &result)
|
void QvConfigHandler::OnLatencyDataArrived_p(const QvTCPingResultObject &result)
|
||||||
{
|
{
|
||||||
CheckConnectionExistanceEx(result.connectionId, nothing);
|
CheckConnectionExistanceEx(result.connectionId, nothing);
|
||||||
connections[result.connectionId].latency = result.avg;
|
connections[result.connectionId].latency = result.avg;
|
||||||
@ -399,7 +400,8 @@ namespace Qv2ray::core::handlers
|
|||||||
connectionRootCache[id] = root;
|
connectionRootCache[id] = root;
|
||||||
//
|
//
|
||||||
emit OnConnectionModified(id);
|
emit OnConnectionModified(id);
|
||||||
if (!skipRestart && id == currentConnectionId)
|
PluginHost->Send_ConnectionEvent({ connections[id].displayName, "", Events::ConnectionEntry::ConnectionEvent_Updated });
|
||||||
|
if (!skipRestart && kernelHandler->isConnected(id))
|
||||||
{
|
{
|
||||||
emit RestartConnection();
|
emit RestartConnection();
|
||||||
}
|
}
|
||||||
@ -412,6 +414,7 @@ namespace Qv2ray::core::handlers
|
|||||||
groups[id].displayName = displayName;
|
groups[id].displayName = displayName;
|
||||||
groups[id].isSubscription = isSubscription;
|
groups[id].isSubscription = isSubscription;
|
||||||
groups[id].importDate = system_clock::to_time_t(system_clock::now());
|
groups[id].importDate = system_clock::to_time_t(system_clock::now());
|
||||||
|
PluginHost->Send_ConnectionEvent({ displayName, "", Events::ConnectionEntry::ConnectionEvent_Created });
|
||||||
emit OnGroupCreated(id, displayName);
|
emit OnGroupCreated(id, displayName);
|
||||||
CHSaveConfigData_p();
|
CHSaveConfigData_p();
|
||||||
return id;
|
return id;
|
||||||
@ -425,6 +428,7 @@ namespace Qv2ray::core::handlers
|
|||||||
return tr("Group does not exist");
|
return tr("Group does not exist");
|
||||||
}
|
}
|
||||||
OnGroupRenamed(id, groups[id].displayName, newName);
|
OnGroupRenamed(id, groups[id].displayName, newName);
|
||||||
|
PluginHost->Send_ConnectionEvent({ newName, groups[id].displayName, Events::ConnectionEntry::ConnectionEvent_Renamed });
|
||||||
groups[id].displayName = newName;
|
groups[id].displayName = newName;
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@ -468,7 +472,7 @@ namespace Qv2ray::core::handlers
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
isHttpRequestInProgress = true;
|
isHttpRequestInProgress = true;
|
||||||
auto data = httpHelper->syncget(groups[id].address, useSystemProxy);
|
auto data = httpHelper->Get(groups[id].address, useSystemProxy);
|
||||||
isHttpRequestInProgress = false;
|
isHttpRequestInProgress = false;
|
||||||
return CHUpdateSubscription_p(id, data);
|
return CHUpdateSubscription_p(id, data);
|
||||||
}
|
}
|
||||||
@ -583,7 +587,17 @@ namespace Qv2ray::core::handlers
|
|||||||
return hasErrorOccured;
|
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)
|
LOG(MODULE_CORE_HANDLER, "Creating new connection: " + displayName)
|
||||||
ConnectionId newId(GenerateUuid());
|
ConnectionId newId(GenerateUuid());
|
||||||
@ -592,8 +606,12 @@ namespace Qv2ray::core::handlers
|
|||||||
connections[newId].importDate = system_clock::to_time_t(system_clock::now());
|
connections[newId].importDate = system_clock::to_time_t(system_clock::now());
|
||||||
connections[newId].displayName = displayName;
|
connections[newId].displayName = displayName;
|
||||||
emit OnConnectionCreated(newId, displayName);
|
emit OnConnectionCreated(newId, displayName);
|
||||||
|
PluginHost->Send_ConnectionEvent({ displayName, "", Events::ConnectionEntry::ConnectionEvent_Created });
|
||||||
UpdateConnection(newId, root);
|
UpdateConnection(newId, root);
|
||||||
|
if (!skipSaveConfig)
|
||||||
|
{
|
||||||
CHSaveConfigData_p();
|
CHSaveConfigData_p();
|
||||||
|
}
|
||||||
return newId;
|
return newId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
#include "core/CoreSafeTypes.hpp"
|
#include "core/CoreSafeTypes.hpp"
|
||||||
#include "core/CoreUtils.hpp"
|
#include "core/CoreUtils.hpp"
|
||||||
#include "core/connection/ConnectionIO.hpp"
|
#include "core/connection/ConnectionIO.hpp"
|
||||||
#include "core/kernel/KernelInteractions.hpp"
|
#include "core/handler/KernelInstanceHandler.hpp"
|
||||||
|
|
||||||
#define CheckIdExistance(type, id, val) \
|
#define CheckIdExistance(type, id, val) \
|
||||||
if (!type.contains(id)) \
|
if (!type.contains(id)) \
|
||||||
@ -21,7 +21,6 @@
|
|||||||
#define CheckConnectionExistance(id) CheckConnectionExistanceEx(id, tr("Connection does not exist"))
|
#define CheckConnectionExistance(id) CheckConnectionExistanceEx(id, tr("Connection does not exist"))
|
||||||
namespace Qv2ray::core::handlers
|
namespace Qv2ray::core::handlers
|
||||||
{
|
{
|
||||||
//
|
|
||||||
class QvConfigHandler : public QObject
|
class QvConfigHandler : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -30,11 +29,6 @@ namespace Qv2ray::core::handlers
|
|||||||
~QvConfigHandler();
|
~QvConfigHandler();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
//
|
|
||||||
inline const ConnectionId CurrentConnection() const
|
|
||||||
{
|
|
||||||
return currentConnectionId;
|
|
||||||
}
|
|
||||||
inline const QList<ConnectionId> Connections() const
|
inline const QList<ConnectionId> Connections() const
|
||||||
{
|
{
|
||||||
return connections.keys();
|
return connections.keys();
|
||||||
@ -80,11 +74,13 @@ namespace Qv2ray::core::handlers
|
|||||||
//
|
//
|
||||||
// Connection Operations.
|
// Connection Operations.
|
||||||
bool UpdateConnection(const ConnectionId &id, const CONFIGROOT &root, bool skipRestart = false);
|
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> DeleteConnection(const ConnectionId &id);
|
||||||
const optional<QString> RenameConnection(const ConnectionId &id, const QString &newName);
|
const optional<QString> RenameConnection(const ConnectionId &id, const QString &newName);
|
||||||
const optional<QString> MoveConnectionGroup(const ConnectionId &id, const GroupId &newGroupId);
|
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
|
// Get Conncetion Property
|
||||||
const CONFIGROOT GetConnectionRoot(const ConnectionId &id) const;
|
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;
|
const tuple<QString, int64_t, float> GetSubscriptionData(const GroupId &id) const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void OnCrashed();
|
void OnKernelLogAvailable(const ConnectionId &id, const QString &log);
|
||||||
void OnConnected(const ConnectionId &id);
|
|
||||||
void OnDisconnected(const ConnectionId &id);
|
|
||||||
void OnVCoreLogAvailable(const ConnectionId &id, const QString &log);
|
|
||||||
void OnStatsAvailable(const ConnectionId &id, const quint64 upS, const quint64 downS, const quint64 upD, const quint64 downD);
|
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);
|
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 OnGroupDeleted(const GroupId &id, const QList<ConnectionId> &connections);
|
||||||
//
|
//
|
||||||
void OnSubscriptionUpdateFinished(const GroupId &id);
|
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:
|
private slots:
|
||||||
void OnStatsDataArrived(const ConnectionId &id, const quint64 uploadSpeed, const quint64 downloadSpeed);
|
void OnKernelCrashed_p(const ConnectionId &id, const QString &errMessage);
|
||||||
void OnVCoreCrashed(const ConnectionId &id);
|
void OnLatencyDataArrived_p(const QvTCPingResultObject &data);
|
||||||
void OnLatencyDataArrived(const QvTCPingResultObject &data);
|
void OnStatsDataArrived_p(const ConnectionId &id, const quint64 uploadSpeed, const quint64 downloadSpeed);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void timerEvent(QTimerEvent *event) override;
|
void timerEvent(QTimerEvent *event) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void CHSaveConfigData_p();
|
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);
|
bool CHUpdateSubscription_p(const GroupId &id, const QByteArray &subscriptionData);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -155,13 +148,7 @@ namespace Qv2ray::core::handlers
|
|||||||
QvHttpRequestHelper *httpHelper;
|
QvHttpRequestHelper *httpHelper;
|
||||||
bool isHttpRequestInProgress = false;
|
bool isHttpRequestInProgress = false;
|
||||||
QvTCPingHelper *tcpingHelper;
|
QvTCPingHelper *tcpingHelper;
|
||||||
// We only support one cuncurrent connection currently.
|
KernelInstanceHandler *kernelHandler;
|
||||||
#ifdef QV2RAY_MULTIPlE_ONNECTION
|
|
||||||
QHash<ConnectionId, *V2rayKernelInstance> kernelInstances;
|
|
||||||
#else
|
|
||||||
ConnectionId currentConnectionId = NullConnectionId;
|
|
||||||
V2rayKernelInstance *vCoreInstance = nullptr;
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
inline ::Qv2ray::core::handlers::QvConfigHandler *ConnectionManager = nullptr;
|
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_up = 0;
|
||||||
qint64 value_down = 0;
|
qint64 value_down = 0;
|
||||||
|
|
||||||
for (auto tag : inboundTags)
|
for (const auto &tag : inboundTags)
|
||||||
{
|
{
|
||||||
value_up += CallStatsAPIByName("inbound>>>" + tag + ">>>traffic>>>uplink");
|
value_up += CallStatsAPIByName("inbound>>>" + tag + ">>>traffic>>>uplink");
|
||||||
value_down += CallStatsAPIByName("inbound>>>" + tag + ">>>traffic>>>downlink");
|
value_down += CallStatsAPIByName("inbound>>>" + tag + ">>>traffic>>>downlink");
|
||||||
@ -105,7 +105,6 @@ namespace Qv2ray::core::kernel
|
|||||||
|
|
||||||
if (running)
|
if (running)
|
||||||
{
|
{
|
||||||
apiFailedCounter = 0;
|
|
||||||
emit OnDataReady(value_up, value_down);
|
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()) + ")")
|
LOG(MODULE_VCORE, "API call returns: " + QSTRN(status.error_code()) + " (" + QString::fromStdString(status.error_message()) + ")")
|
||||||
apiFailedCounter++;
|
apiFailedCounter++;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
apiFailedCounter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
qint64 data = response.stat().value();
|
qint64 data = response.stat().value();
|
||||||
#else
|
#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
|
namespace Qv2ray::core::kernel::abi
|
||||||
{
|
{
|
||||||
[[nodiscard]] QvKernelABICompatibility checkCompatibility(QvKernelABIType hostType, QvKernelABIType targetType)
|
QvKernelABICompatibility checkCompatibility(QvKernelABIType hostType, QvKernelABIType targetType)
|
||||||
{
|
{
|
||||||
switch (hostType)
|
switch (hostType)
|
||||||
{
|
{
|
||||||
case ABI_WIN32: [[fallthrough]];
|
case ABI_WIN32:
|
||||||
case ABI_MACH_O: [[fallthrough]];
|
case ABI_MACH_O:
|
||||||
case ABI_ELF_AARCH64: [[fallthrough]];
|
case ABI_ELF_AARCH64:
|
||||||
|
case ABI_ELF_ARM:
|
||||||
case ABI_ELF_X86: return targetType == hostType ? ABI_PERFECT : ABI_NOPE;
|
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_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;
|
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);
|
QFile file(pathCoreExecutable);
|
||||||
if (!file.exists())
|
if (!file.exists())
|
||||||
@ -43,6 +44,8 @@ namespace Qv2ray::core::kernel::abi
|
|||||||
return { QvKernelABIType::ABI_ELF_X86, std::nullopt };
|
return { QvKernelABIType::ABI_ELF_X86, std::nullopt };
|
||||||
else if (elfInstruction == 0xB700u)
|
else if (elfInstruction == 0xB700u)
|
||||||
return { QvKernelABIType::ABI_ELF_AARCH64, std::nullopt };
|
return { QvKernelABIType::ABI_ELF_AARCH64, std::nullopt };
|
||||||
|
else if (elfInstruction == 0x2800u)
|
||||||
|
return { QvKernelABIType::ABI_ELF_ARM, std::nullopt };
|
||||||
else
|
else
|
||||||
return { QvKernelABIType::ABI_ELF_OTHER, std::nullopt };
|
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) };
|
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)
|
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: return QObject::tr("ELF x86 executable");
|
||||||
case ABI_ELF_X86_64: return QObject::tr("ELF amd64 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_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");
|
case ABI_ELF_OTHER: return QObject::tr("other ELF executable");
|
||||||
default: return QObject::tr("unknown abi");
|
default: return QObject::tr("unknown abi");
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ namespace Qv2ray::core::kernel
|
|||||||
ABI_ELF_X86,
|
ABI_ELF_X86,
|
||||||
ABI_ELF_X86_64,
|
ABI_ELF_X86_64,
|
||||||
ABI_ELF_AARCH64,
|
ABI_ELF_AARCH64,
|
||||||
|
ABI_ELF_ARM,
|
||||||
ABI_ELF_OTHER,
|
ABI_ELF_OTHER,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -38,10 +39,14 @@ namespace Qv2ray::core::kernel
|
|||||||
QvKernelABIType::ABI_WIN32;
|
QvKernelABIType::ABI_WIN32;
|
||||||
#elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_ARM_64)
|
#elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_ARM_64)
|
||||||
QvKernelABIType::ABI_ELF_AARCH64;
|
QvKernelABIType::ABI_ELF_AARCH64;
|
||||||
|
#elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_ARM_V7)
|
||||||
|
QvKernelABIType::ABI_ELF_ARM;
|
||||||
|
#else
|
||||||
|
#error "unknown architecture"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
[[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);
|
||||||
[[nodiscard]] QvKernelABICompatibility checkCompatibility(QvKernelABIType hostType, QvKernelABIType targetType);
|
QvKernelABICompatibility checkCompatibility(QvKernelABIType hostType, QvKernelABIType targetType);
|
||||||
[[nodiscard]] QString abiToString(QvKernelABIType abi);
|
QString abiToString(QvKernelABIType abi);
|
||||||
} // namespace abi
|
} // namespace abi
|
||||||
} // namespace Qv2ray::core::kernel
|
} // namespace Qv2ray::core::kernel
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "KernelInteractions.hpp"
|
#include "V2rayKernelInteractions.hpp"
|
||||||
|
|
||||||
#include "APIBackend.hpp"
|
#include "APIBackend.hpp"
|
||||||
#include "common/QvHelpers.hpp"
|
#include "common/QvHelpers.hpp"
|
||||||
@ -31,33 +31,42 @@ namespace Qv2ray::core::kernel
|
|||||||
}
|
}
|
||||||
|
|
||||||
coreFile.close();
|
coreFile.close();
|
||||||
|
|
||||||
// Get Core ABI.
|
// Get Core ABI.
|
||||||
auto [abi, err] = kernel::abi::deduceKernelABI(vCorePath);
|
auto [abi, err] = kernel::abi::deduceKernelABI(vCorePath);
|
||||||
if (err)
|
if (err)
|
||||||
{
|
{
|
||||||
LOG(MODULE_VCORE, "Core ABI deduction failed: " + err.value())
|
LOG(MODULE_VCORE, "Core ABI deduction failed: " + ACCESS_OPTIONAL_VALUE(err))
|
||||||
*message = err.value();
|
*message = ACCESS_OPTIONAL_VALUE(err);
|
||||||
return false;
|
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
|
// Get Compiled ABI
|
||||||
auto compiledABI = kernel::abi::COMPILED_ABI_TYPE;
|
auto compiledABI = kernel::abi::COMPILED_ABI_TYPE;
|
||||||
LOG(MODULE_VCORE, "Host ABI: " + kernel::abi::abiToString(compiledABI))
|
LOG(MODULE_VCORE, "Host ABI: " + kernel::abi::abiToString(compiledABI))
|
||||||
|
|
||||||
// Check ABI Compatibility.
|
// Check ABI Compatibility.
|
||||||
switch (kernel::abi::checkCompatibility(compiledABI, abi.value()))
|
switch (kernel::abi::checkCompatibility(compiledABI, ACCESS_OPTIONAL_VALUE(abi)))
|
||||||
{
|
{
|
||||||
case kernel::abi::ABI_NOPE:
|
case kernel::abi::ABI_NOPE:
|
||||||
|
{
|
||||||
LOG(MODULE_VCORE, "Host is incompatible with core")
|
LOG(MODULE_VCORE, "Host is incompatible with core")
|
||||||
*message = tr("V2Ray core is incompatible with your platform.\r\n" //
|
*message = tr("V2Ray core is incompatible with your platform.\r\n" //
|
||||||
"Expected core ABI is %1, but got actual %2.\r\n" //
|
"Expected core ABI is %1, but got actual %2.\r\n" //
|
||||||
"Maybe you have downloaded the wrong core?")
|
"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;
|
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
|
else
|
||||||
{
|
{
|
||||||
QvMessageBoxWarn(nullptr, tr("Cannot start V2ray"),
|
QvMessageBoxWarn(nullptr, tr("Cannot start V2ray"), //
|
||||||
tr("V2ray core settings is incorrect.") + NEWLINE + NEWLINE + tr("The error is: ") + NEWLINE + v2rayCheckResult);
|
tr("V2ray core settings is incorrect.") + NEWLINE + NEWLINE + //
|
||||||
|
tr("The error is: ") + NEWLINE + v2rayCheckResult);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
V2rayKernelInstance::V2rayKernelInstance()
|
V2rayKernelInstance::V2rayKernelInstance(QObject *parent) : QObject(parent)
|
||||||
{
|
{
|
||||||
vProcess = new QProcess();
|
vProcess = new QProcess();
|
||||||
connect(vProcess, &QProcess::readyReadStandardOutput, this,
|
connect(vProcess, &QProcess::readyReadStandardOutput, this,
|
||||||
[&]() { emit OnProcessOutputReadyRead(id, vProcess->readAllStandardOutput().trimmed()); });
|
[&]() { emit OnProcessOutputReadyRead(vProcess->readAllStandardOutput().trimmed()); });
|
||||||
connect(vProcess, &QProcess::stateChanged, [&](QProcess::ProcessState state) {
|
connect(vProcess, &QProcess::stateChanged, [&](QProcess::ProcessState state) {
|
||||||
DEBUG(MODULE_VCORE, "V2ray kernel process status changed: " + QVariant::fromValue(state).toString())
|
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.")
|
LOG(MODULE_VCORE, "V2ray kernel crashed.")
|
||||||
StopConnection();
|
StopConnection();
|
||||||
emit OnProcessErrored(id);
|
emit OnProcessErrored("V2ray kernel crashed.");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
apiWorker = new APIWorker();
|
apiWorker = new APIWorker();
|
||||||
@ -183,7 +193,7 @@ namespace Qv2ray::core::kernel
|
|||||||
KernelStarted = false;
|
KernelStarted = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
optional<QString> V2rayKernelInstance::StartConnection(const ConnectionId &id, const CONFIGROOT &root)
|
optional<QString> V2rayKernelInstance::StartConnection(const CONFIGROOT &root)
|
||||||
{
|
{
|
||||||
if (KernelStarted)
|
if (KernelStarted)
|
||||||
{
|
{
|
||||||
@ -206,8 +216,6 @@ namespace Qv2ray::core::kernel
|
|||||||
vProcess->waitForStarted();
|
vProcess->waitForStarted();
|
||||||
DEBUG(MODULE_VCORE, "V2ray core started.")
|
DEBUG(MODULE_VCORE, "V2ray core started.")
|
||||||
KernelStarted = true;
|
KernelStarted = true;
|
||||||
// Set Connection ID
|
|
||||||
this->id = id;
|
|
||||||
QStringList inboundTags;
|
QStringList inboundTags;
|
||||||
|
|
||||||
for (auto item : root["inbounds"].toArray())
|
for (auto item : root["inbounds"].toArray())
|
||||||
@ -285,6 +293,6 @@ namespace Qv2ray::core::kernel
|
|||||||
|
|
||||||
void V2rayKernelInstance::onAPIDataReady(const quint64 speedUp, const quint64 speedDown)
|
void V2rayKernelInstance::onAPIDataReady(const quint64 speedUp, const quint64 speedDown)
|
||||||
{
|
{
|
||||||
emit OnNewStatsDataArrived(id, speedUp, speedDown);
|
emit OnNewStatsDataArrived(speedUp, speedDown);
|
||||||
}
|
}
|
||||||
} // namespace Qv2ray::core::kernel
|
} // namespace Qv2ray::core::kernel
|
@ -12,7 +12,7 @@ namespace Qv2ray::core::kernel
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit V2rayKernelInstance();
|
explicit V2rayKernelInstance(QObject *parent = nullptr);
|
||||||
~V2rayKernelInstance() override;
|
~V2rayKernelInstance() override;
|
||||||
//
|
//
|
||||||
// Speed
|
// Speed
|
||||||
@ -21,7 +21,7 @@ namespace Qv2ray::core::kernel
|
|||||||
qulonglong getAllSpeedUp();
|
qulonglong getAllSpeedUp();
|
||||||
qulonglong getAllSpeedDown();
|
qulonglong getAllSpeedDown();
|
||||||
//
|
//
|
||||||
optional<QString> StartConnection(const ConnectionId &id, const CONFIGROOT &root);
|
optional<QString> StartConnection(const CONFIGROOT &root);
|
||||||
void StopConnection();
|
void StopConnection();
|
||||||
bool KernelStarted = false;
|
bool KernelStarted = false;
|
||||||
//
|
//
|
||||||
@ -29,19 +29,17 @@ namespace Qv2ray::core::kernel
|
|||||||
static bool ValidateKernel(const QString &vCorePath, const QString &vAssetsPath, QString *message);
|
static bool ValidateKernel(const QString &vCorePath, const QString &vAssetsPath, QString *message);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void OnProcessErrored(const ConnectionId &id);
|
void OnProcessErrored(const QString &errMessage);
|
||||||
void OnProcessOutputReadyRead(const ConnectionId &id, const QString &output);
|
void OnProcessOutputReadyRead(const QString &output);
|
||||||
void OnNewStatsDataArrived(const ConnectionId &id, const quint64 speedUp, const quint64 speedDown);
|
void OnNewStatsDataArrived(const quint64 speedUp, const quint64 speedDown);
|
||||||
|
|
||||||
public slots:
|
private slots:
|
||||||
void onAPIDataReady(const quint64 speedUp, const quint64 speedDown);
|
void onAPIDataReady(const quint64 speedUp, const quint64 speedDown);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
APIWorker *apiWorker;
|
APIWorker *apiWorker;
|
||||||
QProcess *vProcess;
|
QProcess *vProcess;
|
||||||
bool apiEnabled;
|
bool apiEnabled;
|
||||||
//
|
|
||||||
ConnectionId id = NullConnectionId;
|
|
||||||
};
|
};
|
||||||
} // namespace Qv2ray::core::kernel
|
} // namespace Qv2ray::core::kernel
|
||||||
|
|
14
src/main.cpp
@ -4,6 +4,7 @@
|
|||||||
#include "common/QvTranslator.hpp"
|
#include "common/QvTranslator.hpp"
|
||||||
#include "core/handler/ConfigHandler.hpp"
|
#include "core/handler/ConfigHandler.hpp"
|
||||||
#include "core/settings/SettingsBackend.hpp"
|
#include "core/settings/SettingsBackend.hpp"
|
||||||
|
#include "src/components/plugins/QvPluginHost.hpp"
|
||||||
#include "ui/w_MainWindow.hpp"
|
#include "ui/w_MainWindow.hpp"
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
@ -139,7 +140,7 @@ bool initialiseQv2ray()
|
|||||||
Qv2rayConfig conf;
|
Qv2rayConfig conf;
|
||||||
conf.kernelConfig.KernelPath(QString(QV2RAY_DEFAULT_VCORE_PATH));
|
conf.kernelConfig.KernelPath(QString(QV2RAY_DEFAULT_VCORE_PATH));
|
||||||
conf.kernelConfig.AssetsPath(QString(QV2RAY_DEFAULT_VASSETS_PATH));
|
conf.kernelConfig.AssetsPath(QString(QV2RAY_DEFAULT_VASSETS_PATH));
|
||||||
conf.logLevel = 2;
|
conf.logLevel = 3;
|
||||||
conf.uiConfig.language = QLocale::system().name();
|
conf.uiConfig.language = QLocale::system().name();
|
||||||
//
|
//
|
||||||
// Save initial config.
|
// Save initial config.
|
||||||
@ -229,7 +230,7 @@ int main(int argc, char *argv[])
|
|||||||
#endif
|
#endif
|
||||||
if (StartupOption.noScaleFactors)
|
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"))
|
LOG(MODULE_UI, "Original QT_SCALE_FACTOR was: " + qEnvironmentVariable("QT_SCALE_FACTOR"))
|
||||||
qputenv("QT_SCALE_FACTOR", "1");
|
qputenv("QT_SCALE_FACTOR", "1");
|
||||||
}
|
}
|
||||||
@ -237,6 +238,9 @@ int main(int argc, char *argv[])
|
|||||||
{
|
{
|
||||||
LOG(MODULE_INIT, "High DPI scaling is enabled.")
|
LOG(MODULE_INIT, "High DPI scaling is enabled.")
|
||||||
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
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 _qApp(argc, argv, false,
|
||||||
SingleApplication::User | SingleApplication::ExcludeAppPath | SingleApplication::ExcludeAppVersion);
|
SingleApplication::User | SingleApplication::ExcludeAppPath | SingleApplication::ExcludeAppVersion);
|
||||||
@ -404,11 +408,8 @@ int main(int argc, char *argv[])
|
|||||||
#endif
|
#endif
|
||||||
//_qApp.setAttribute(Qt::AA_DontUseNativeMenuBar);
|
//_qApp.setAttribute(Qt::AA_DontUseNativeMenuBar);
|
||||||
// Initialise Connection Handler
|
// Initialise Connection Handler
|
||||||
|
PluginHost = new QvPluginHost();
|
||||||
ConnectionManager = new QvConfigHandler();
|
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
|
// Show MainWindow
|
||||||
MainWindow w;
|
MainWindow w;
|
||||||
QObject::connect(&_qApp, &SingleApplication::instanceStarted, [&]() {
|
QObject::connect(&_qApp, &SingleApplication::instanceStarted, [&]() {
|
||||||
@ -423,6 +424,7 @@ int main(int argc, char *argv[])
|
|||||||
#endif
|
#endif
|
||||||
auto rcode = _qApp.exec();
|
auto rcode = _qApp.exec();
|
||||||
delete ConnectionManager;
|
delete ConnectionManager;
|
||||||
|
delete PluginHost;
|
||||||
LOG(MODULE_INIT, "Quitting normally")
|
LOG(MODULE_INIT, "Quitting normally")
|
||||||
return rcode;
|
return rcode;
|
||||||
#ifndef QT_DEBUG
|
#ifndef QT_DEBUG
|
||||||
|
@ -9,78 +9,53 @@
|
|||||||
#include <QIntValidator>
|
#include <QIntValidator>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
OutboundEditor::OutboundEditor(QWidget *parent) : QDialog(parent), Tag(""), Mux(), vmess(), shadowsocks()
|
OutboundEditor::OutboundEditor(QWidget *parent) : QDialog(parent), tag(OUTBOUND_TAG_PROXY)
|
||||||
{
|
{
|
||||||
QvMessageBusConnect(OutboundEditor);
|
QvMessageBusConnect(OutboundEditor);
|
||||||
setupUi(this);
|
setupUi(this);
|
||||||
//
|
//
|
||||||
ssWidget = new StreamSettingsWidget(this);
|
outboundType = "vmess";
|
||||||
transportFrame->addWidget(ssWidget);
|
|
||||||
//
|
//
|
||||||
shadowsocks = ShadowSocksServerObject();
|
streamSettingsWidget = new StreamSettingsWidget(this);
|
||||||
socks = SocksServerObject();
|
streamSettingsWidget->SetStreamObject({});
|
||||||
vmess = VMessServerObject();
|
transportFrame->addWidget(streamSettingsWidget);
|
||||||
socks.users.push_back(SocksServerObject::UserObject());
|
|
||||||
vmess.users.push_back(VMessServerObject::UserObject());
|
|
||||||
//
|
//
|
||||||
auto stream = StreamSettingsObject();
|
socks.users.push_back({});
|
||||||
ssWidget->SetStreamObject(stream);
|
vmess.users.push_back({});
|
||||||
//
|
//
|
||||||
OutboundType = "vmess";
|
auto pluginEditorWidgetsInfo = PluginHost->GetOutboundEditorWidgets();
|
||||||
Tag = OUTBOUND_TAG_PROXY;
|
for (const auto &plugin : pluginEditorWidgetsInfo)
|
||||||
useFProxy = false;
|
{
|
||||||
ReloadGUI();
|
for (const auto &_d : plugin->OutboundCapabilities())
|
||||||
Result = GenerateConnectionJson();
|
{
|
||||||
|
outBoundTypeCombo->addItem(_d.displayName, _d.protocol);
|
||||||
|
auto index = outboundTypeStackView->addWidget(plugin);
|
||||||
|
pluginWidgets.insert(index, { _d, plugin });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//
|
||||||
|
outboundType = "vmess";
|
||||||
|
useForwardProxy = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QvMessageBusSlotImpl(OutboundEditor)
|
QvMessageBusSlotImpl(OutboundEditor)
|
||||||
{
|
{
|
||||||
switch (msg)
|
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;
|
originalConfig = 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
ReloadGUI();
|
ReloadGUI();
|
||||||
Result = GenerateConnectionJson();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OutboundEditor::~OutboundEditor()
|
OutboundEditor::~OutboundEditor()
|
||||||
@ -90,124 +65,187 @@ OutboundEditor::~OutboundEditor()
|
|||||||
OUTBOUND OutboundEditor::OpenEditor()
|
OUTBOUND OutboundEditor::OpenEditor()
|
||||||
{
|
{
|
||||||
int resultCode = this->exec();
|
int resultCode = this->exec();
|
||||||
return resultCode == QDialog::Accepted ? Result : Original;
|
return resultCode == QDialog::Accepted ? resultConfig : originalConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString OutboundEditor::GetFriendlyName()
|
QString OutboundEditor::GetFriendlyName()
|
||||||
{
|
{
|
||||||
auto host = ipLineEdit->text().replace(":", "-").replace("/", "_").replace("\\", "_");
|
auto host = ipLineEdit->text().replace(":", "-").replace("/", "_").replace("\\", "_");
|
||||||
auto port = portLineEdit->text().replace(":", "-").replace("/", "_").replace("\\", "_");
|
auto port = portLineEdit->text().replace(":", "-").replace("/", "_").replace("\\", "_");
|
||||||
auto type = OutboundType;
|
auto type = outboundType;
|
||||||
QString name = Tag.isEmpty() ? host + "-[" + port + "]-" + type : Tag;
|
QString name = tag.isEmpty() ? host + "-[" + port + "]-" + type : tag;
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
OUTBOUND OutboundEditor::GenerateConnectionJson()
|
OUTBOUND OutboundEditor::GenerateConnectionJson()
|
||||||
{
|
{
|
||||||
OUTBOUNDSETTING settings;
|
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": [] }
|
// VMess is only a ServerObject, and we need an array { "vnext": [] }
|
||||||
QJsonArray vnext;
|
QJsonArray vnext;
|
||||||
|
vmess.address = address;
|
||||||
|
vmess.port = port;
|
||||||
vnext.append(GetRootObject(vmess));
|
vnext.append(GetRootObject(vmess));
|
||||||
settings.insert("vnext", vnext);
|
settings.insert("vnext", vnext);
|
||||||
}
|
}
|
||||||
else if (OutboundType == "shadowsocks")
|
else if (outboundType == "shadowsocks")
|
||||||
{
|
{
|
||||||
streaming = QJsonObject();
|
streaming = QJsonObject();
|
||||||
LOG(MODULE_CONNECTION, "Shadowsocks outbound does not need StreamSettings.")
|
LOG(MODULE_CONNECTION, "Shadowsocks outbound does not need StreamSettings.")
|
||||||
QJsonArray servers;
|
QJsonArray servers;
|
||||||
|
shadowsocks.address = address;
|
||||||
|
shadowsocks.port = port;
|
||||||
servers.append(GetRootObject(shadowsocks));
|
servers.append(GetRootObject(shadowsocks));
|
||||||
settings["servers"] = servers;
|
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())
|
if (!socks.users.isEmpty() && socks.users.first().user.isEmpty() && socks.users.first().pass.isEmpty())
|
||||||
{
|
{
|
||||||
LOG(MODULE_UI, "Removed empty user form SOCKS settings")
|
LOG(MODULE_UI, "Removed empty user form SOCKS settings")
|
||||||
socks.users.clear();
|
socks.users.clear();
|
||||||
}
|
}
|
||||||
|
socks.address = address;
|
||||||
|
socks.port = port;
|
||||||
streaming = QJsonObject();
|
streaming = QJsonObject();
|
||||||
LOG(MODULE_CONNECTION, "Socks outbound does not need StreamSettings.")
|
LOG(MODULE_CONNECTION, "Socks outbound does not need StreamSettings.")
|
||||||
QJsonArray servers;
|
QJsonArray servers;
|
||||||
servers.append(GetRootObject(socks));
|
servers.append(GetRootObject(socks));
|
||||||
settings["servers"] = servers;
|
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);
|
auto root = GenerateOutboundEntry(outboundType, settings, streaming, muxConfig, "0.0.0.0", tag);
|
||||||
root[QV2RAY_USE_FPROXY_KEY] = useFProxy;
|
root[QV2RAY_USE_FPROXY_KEY] = useForwardProxy;
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OutboundEditor::ReloadGUI()
|
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);
|
outBoundTypeCombo->setCurrentIndex(0);
|
||||||
ipLineEdit->setText(vmess.address);
|
vmess = StructFromJsonString<VMessServerObject>(JsonToString(settings["vnext"].toArray().first().toObject()));
|
||||||
portLineEdit->setText(QSTRN(vmess.port));
|
if (vmess.users.empty())
|
||||||
|
{
|
||||||
|
vmess.users.push_back({});
|
||||||
|
}
|
||||||
|
address = vmess.address;
|
||||||
|
port = vmess.port;
|
||||||
idLineEdit->setText(vmess.users.front().id);
|
idLineEdit->setText(vmess.users.front().id);
|
||||||
alterLineEdit->setValue(vmess.users.front().alterId);
|
alterLineEdit->setValue(vmess.users.front().alterId);
|
||||||
securityCombo->setCurrentText(vmess.users.front().security);
|
securityCombo->setCurrentText(vmess.users.front().security);
|
||||||
}
|
}
|
||||||
else if (OutboundType == "shadowsocks")
|
else if (outboundType == "shadowsocks")
|
||||||
{
|
{
|
||||||
outBoundTypeCombo->setCurrentIndex(1);
|
outBoundTypeCombo->setCurrentIndex(1);
|
||||||
|
shadowsocks = StructFromJsonString<ShadowSocksServerObject>(JsonToString(settings["servers"].toArray().first().toObject()));
|
||||||
|
address = shadowsocks.address;
|
||||||
|
port = shadowsocks.port;
|
||||||
// ShadowSocks Configs
|
// ShadowSocks Configs
|
||||||
ipLineEdit->setText(shadowsocks.address);
|
|
||||||
portLineEdit->setText(QSTRN(shadowsocks.port));
|
|
||||||
ss_emailTxt->setText(shadowsocks.email);
|
ss_emailTxt->setText(shadowsocks.email);
|
||||||
ss_levelSpin->setValue(shadowsocks.level);
|
ss_levelSpin->setValue(shadowsocks.level);
|
||||||
ss_otaCheckBox->setChecked(shadowsocks.ota);
|
ss_otaCheckBox->setChecked(shadowsocks.ota);
|
||||||
ss_passwordTxt->setText(shadowsocks.password);
|
ss_passwordTxt->setText(shadowsocks.password);
|
||||||
ss_encryptionMethod->setCurrentText(shadowsocks.method);
|
ss_encryptionMethod->setCurrentText(shadowsocks.method);
|
||||||
}
|
}
|
||||||
else if (OutboundType == "socks")
|
else if (outboundType == "socks")
|
||||||
{
|
{
|
||||||
outBoundTypeCombo->setCurrentIndex(2);
|
outBoundTypeCombo->setCurrentIndex(2);
|
||||||
ipLineEdit->setText(socks.address);
|
socks = StructFromJsonString<SocksServerObject>(JsonToString(settings["servers"].toArray().first().toObject()));
|
||||||
portLineEdit->setText(QSTRN(socks.port));
|
address = socks.address;
|
||||||
|
port = socks.port;
|
||||||
if (socks.users.empty())
|
if (socks.users.empty())
|
||||||
socks.users.push_back(SocksServerObject::UserObject());
|
{
|
||||||
|
socks.users.push_back({});
|
||||||
|
}
|
||||||
socks_PasswordTxt->setText(socks.users.front().pass);
|
socks_PasswordTxt->setText(socks.users.front().pass);
|
||||||
socks_UserNameTxt->setText(socks.users.front().user);
|
socks_UserNameTxt->setText(socks.users.front().user);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
useFPCB->setChecked(useFProxy);
|
{
|
||||||
muxEnabledCB->setChecked(Mux["enabled"].toBool());
|
bool processed = false;
|
||||||
muxConcurrencyTxt->setValue(Mux["concurrency"].toInt());
|
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()
|
void OutboundEditor::on_buttonBox_accepted()
|
||||||
{
|
{
|
||||||
Result = GenerateConnectionJson();
|
resultConfig = GenerateConnectionJson();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OutboundEditor::on_ipLineEdit_textEdited(const QString &arg1)
|
void OutboundEditor::on_ipLineEdit_textEdited(const QString &arg1)
|
||||||
{
|
{
|
||||||
vmess.address = arg1;
|
address = arg1;
|
||||||
shadowsocks.address = arg1;
|
|
||||||
socks.address = arg1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OutboundEditor::on_portLineEdit_textEdited(const QString &arg1)
|
void OutboundEditor::on_portLineEdit_textEdited(const QString &arg1)
|
||||||
{
|
{
|
||||||
if (arg1 != "")
|
port = arg1.toInt();
|
||||||
{
|
|
||||||
vmess.port = arg1.toInt();
|
|
||||||
shadowsocks.port = arg1.toInt();
|
|
||||||
socks.port = arg1.toInt();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OutboundEditor::on_idLineEdit_textEdited(const QString &arg1)
|
void OutboundEditor::on_idLineEdit_textEdited(const QString &arg1)
|
||||||
{
|
{
|
||||||
if (vmess.users.empty())
|
if (vmess.users.empty())
|
||||||
vmess.users.push_back(VMessServerObject::UserObject());
|
vmess.users.push_back({});
|
||||||
|
|
||||||
vmess.users.front().id = arg1;
|
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)
|
void OutboundEditor::on_securityCombo_currentIndexChanged(const QString &arg1)
|
||||||
{
|
{
|
||||||
if (vmess.users.empty())
|
if (vmess.users.empty())
|
||||||
vmess.users.push_back(VMessServerObject::UserObject());
|
vmess.users.push_back({});
|
||||||
|
|
||||||
vmess.users.front().security = arg1;
|
vmess.users.front().security = arg1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OutboundEditor::on_tagTxt_textEdited(const QString &arg1)
|
void OutboundEditor::on_tagTxt_textEdited(const QString &arg1)
|
||||||
{
|
{
|
||||||
Tag = arg1;
|
tag = arg1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OutboundEditor::on_muxEnabledCB_stateChanged(int 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)
|
void OutboundEditor::on_muxConcurrencyTxt_valueChanged(int arg1)
|
||||||
{
|
{
|
||||||
Mux["concurrency"] = arg1;
|
muxConfig["concurrency"] = arg1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OutboundEditor::on_alterLineEdit_valueChanged(int arg1)
|
void OutboundEditor::on_alterLineEdit_valueChanged(int arg1)
|
||||||
{
|
{
|
||||||
if (vmess.users.empty())
|
if (vmess.users.empty())
|
||||||
vmess.users.push_back(VMessServerObject::UserObject());
|
vmess.users.push_back({});
|
||||||
|
|
||||||
vmess.users.front().alterId = arg1;
|
vmess.users.front().alterId = arg1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OutboundEditor::on_useFPCB_stateChanged(int arg1)
|
void OutboundEditor::on_useFPCB_stateChanged(int arg1)
|
||||||
{
|
{
|
||||||
useFProxy = arg1 == Qt::Checked;
|
useForwardProxy = arg1 == Qt::Checked;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OutboundEditor::on_outBoundTypeCombo_currentIndexChanged(int index)
|
void OutboundEditor::on_outBoundTypeCombo_currentIndexChanged(int index)
|
||||||
{
|
{
|
||||||
|
// 0, 1, 2 as built-in vmess, ss, socks
|
||||||
outboundTypeStackView->setCurrentIndex(index);
|
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)
|
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)
|
void OutboundEditor::on_socks_UserNameTxt_textEdited(const QString &arg1)
|
||||||
{
|
{
|
||||||
if (socks.users.isEmpty())
|
if (socks.users.isEmpty())
|
||||||
socks.users.push_back(SocksServerObject::UserObject());
|
socks.users.push_back({});
|
||||||
socks.users.front().user = arg1;
|
socks.users.front().user = arg1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OutboundEditor::on_socks_PasswordTxt_textEdited(const QString &arg1)
|
void OutboundEditor::on_socks_PasswordTxt_textEdited(const QString &arg1)
|
||||||
{
|
{
|
||||||
if (socks.users.isEmpty())
|
if (socks.users.isEmpty())
|
||||||
socks.users.push_back(SocksServerObject::UserObject());
|
socks.users.push_back({});
|
||||||
socks.users.front().pass = arg1;
|
socks.users.front().pass = arg1;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "base/Qv2rayBase.hpp"
|
#include "base/Qv2rayBase.hpp"
|
||||||
|
#include "components/plugins/QvPluginHost.hpp"
|
||||||
#include "ui/messaging/QvMessageBus.hpp"
|
#include "ui/messaging/QvMessageBus.hpp"
|
||||||
#include "ui/widgets/StreamSettingsWidget.hpp"
|
#include "ui/widgets/StreamSettingsWidget.hpp"
|
||||||
#include "ui_w_OutboundEditor.h"
|
#include "ui_w_OutboundEditor.h"
|
||||||
@ -13,68 +14,55 @@ class OutboundEditor
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit OutboundEditor(QWidget *parent = nullptr);
|
explicit OutboundEditor(const OUTBOUND &outboundEntry, QWidget *parent = nullptr);
|
||||||
explicit OutboundEditor(OUTBOUND outboundEntry, QWidget *parent = nullptr);
|
|
||||||
~OutboundEditor();
|
~OutboundEditor();
|
||||||
OUTBOUND OpenEditor();
|
OUTBOUND OpenEditor();
|
||||||
QString GetFriendlyName();
|
QString GetFriendlyName();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
explicit OutboundEditor(QWidget *parent = nullptr);
|
||||||
QvMessageBusSlotDecl;
|
QvMessageBusSlotDecl;
|
||||||
signals:
|
signals:
|
||||||
void s_reload_config(bool need_restart);
|
void s_reload_config(bool need_restart);
|
||||||
private slots:
|
private slots:
|
||||||
void on_buttonBox_accepted();
|
void on_buttonBox_accepted();
|
||||||
|
|
||||||
void on_ipLineEdit_textEdited(const QString &arg1);
|
void on_ipLineEdit_textEdited(const QString &arg1);
|
||||||
|
|
||||||
void on_portLineEdit_textEdited(const QString &arg1);
|
void on_portLineEdit_textEdited(const QString &arg1);
|
||||||
|
|
||||||
void on_idLineEdit_textEdited(const QString &arg1);
|
void on_idLineEdit_textEdited(const QString &arg1);
|
||||||
|
|
||||||
void on_tagTxt_textEdited(const QString &arg1);
|
void on_tagTxt_textEdited(const QString &arg1);
|
||||||
|
|
||||||
void on_muxEnabledCB_stateChanged(int arg1);
|
void on_muxEnabledCB_stateChanged(int arg1);
|
||||||
|
|
||||||
void on_muxConcurrencyTxt_valueChanged(int arg1);
|
void on_muxConcurrencyTxt_valueChanged(int arg1);
|
||||||
|
|
||||||
void on_alterLineEdit_valueChanged(int arg1);
|
void on_alterLineEdit_valueChanged(int arg1);
|
||||||
|
|
||||||
void on_useFPCB_stateChanged(int arg1);
|
void on_useFPCB_stateChanged(int arg1);
|
||||||
|
|
||||||
void on_outBoundTypeCombo_currentIndexChanged(int index);
|
void on_outBoundTypeCombo_currentIndexChanged(int index);
|
||||||
|
|
||||||
void on_ss_emailTxt_textEdited(const QString &arg1);
|
void on_ss_emailTxt_textEdited(const QString &arg1);
|
||||||
|
|
||||||
void on_ss_passwordTxt_textEdited(const QString &arg1);
|
void on_ss_passwordTxt_textEdited(const QString &arg1);
|
||||||
|
|
||||||
void on_ss_encryptionMethod_currentIndexChanged(const QString &arg1);
|
void on_ss_encryptionMethod_currentIndexChanged(const QString &arg1);
|
||||||
|
|
||||||
void on_ss_levelSpin_valueChanged(int arg1);
|
void on_ss_levelSpin_valueChanged(int arg1);
|
||||||
|
|
||||||
void on_ss_otaCheckBox_stateChanged(int arg1);
|
void on_ss_otaCheckBox_stateChanged(int arg1);
|
||||||
|
|
||||||
void on_socks_UserNameTxt_textEdited(const QString &arg1);
|
void on_socks_UserNameTxt_textEdited(const QString &arg1);
|
||||||
|
|
||||||
void on_socks_PasswordTxt_textEdited(const QString &arg1);
|
void on_socks_PasswordTxt_textEdited(const QString &arg1);
|
||||||
|
|
||||||
void on_securityCombo_currentIndexChanged(const QString &arg1);
|
void on_securityCombo_currentIndexChanged(const QString &arg1);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString Tag;
|
QString tag;
|
||||||
void ReloadGUI();
|
void ReloadGUI();
|
||||||
bool useFProxy;
|
bool useForwardProxy;
|
||||||
OUTBOUND GenerateConnectionJson();
|
OUTBOUND GenerateConnectionJson();
|
||||||
OUTBOUND Original;
|
OUTBOUND originalConfig;
|
||||||
OUTBOUND Result;
|
OUTBOUND resultConfig;
|
||||||
QJsonObject Mux;
|
QJsonObject muxConfig;
|
||||||
//
|
//
|
||||||
// Connection Configs
|
// Connection Configs
|
||||||
QString OutboundType;
|
QString outboundType;
|
||||||
|
QString address;
|
||||||
|
int port;
|
||||||
//
|
//
|
||||||
VMessServerObject vmess;
|
VMessServerObject vmess;
|
||||||
ShadowSocksServerObject shadowsocks;
|
ShadowSocksServerObject shadowsocks;
|
||||||
SocksServerObject socks;
|
SocksServerObject socks;
|
||||||
//
|
//
|
||||||
StreamSettingsWidget *ssWidget;
|
StreamSettingsWidget *streamSettingsWidget;
|
||||||
|
//
|
||||||
|
QMap<int, QPair<QvPluginOutboundProtocolObject, QvPluginEditor *>> pluginWidgets;
|
||||||
};
|
};
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
#include "core/connection/ConnectionIO.hpp"
|
#include "core/connection/ConnectionIO.hpp"
|
||||||
#include "core/connection/Serialization.hpp"
|
#include "core/connection/Serialization.hpp"
|
||||||
#include "core/handler/ConfigHandler.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_JsonEditor.hpp"
|
||||||
#include "ui/editors/w_OutboundEditor.hpp"
|
#include "ui/editors/w_OutboundEditor.hpp"
|
||||||
#include "ui/editors/w_RoutesEditor.hpp"
|
#include "ui/editors/w_RoutesEditor.hpp"
|
||||||
@ -37,7 +37,10 @@ QvMessageBusSlotImpl(ImportConfigWindow)
|
|||||||
{
|
{
|
||||||
switch (msg)
|
switch (msg)
|
||||||
{
|
{
|
||||||
MBShowDefaultImpl MBHideDefaultImpl MBRetranslateDefaultImpl MBUpdateColorSchemeDefaultImpl
|
MBShowDefaultImpl;
|
||||||
|
MBHideDefaultImpl;
|
||||||
|
MBRetranslateDefaultImpl;
|
||||||
|
MBUpdateColorSchemeDefaultImpl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,7 +56,7 @@ QMultiHash<QString, CONFIGROOT> ImportConfigWindow::SelectConnection(bool outbou
|
|||||||
routeEditBtn->setEnabled(!outboundsOnly);
|
routeEditBtn->setEnabled(!outboundsOnly);
|
||||||
this->exec();
|
this->exec();
|
||||||
QMultiHash<QString, CONFIGROOT> conn;
|
QMultiHash<QString, CONFIGROOT> conn;
|
||||||
for (const auto connEntry : connections.values())
|
for (const auto &connEntry : connections.values())
|
||||||
{
|
{
|
||||||
conn += connEntry;
|
conn += connEntry;
|
||||||
}
|
}
|
||||||
@ -64,11 +67,11 @@ int ImportConfigWindow::ImportConnection()
|
|||||||
{
|
{
|
||||||
this->exec();
|
this->exec();
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for (const auto groupName : connections.keys())
|
for (const auto &groupName : connections.keys())
|
||||||
{
|
{
|
||||||
GroupId groupId = groupName.isEmpty() ? DefaultGroupId : ConnectionManager->CreateGroup(groupName, false);
|
GroupId groupId = groupName.isEmpty() ? DefaultGroupId : ConnectionManager->CreateGroup(groupName, false);
|
||||||
const auto groupObject = connections[groupName];
|
const auto groupObject = connections[groupName];
|
||||||
for (const auto connConf : groupObject)
|
for (const auto &connConf : groupObject)
|
||||||
{
|
{
|
||||||
auto connName = groupObject.key(connConf);
|
auto connName = groupObject.key(connConf);
|
||||||
|
|
||||||
@ -77,7 +80,7 @@ int ImportConfigWindow::ImportConnection()
|
|||||||
{
|
{
|
||||||
connName = protocol + "/" + host + ":" + QSTRN(port) + "-" + GenerateRandomString(5);
|
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
|
else
|
||||||
{
|
{
|
||||||
for (auto conf : config)
|
for (const auto &conf : config)
|
||||||
{
|
{
|
||||||
AddToGroup(newGroupName, config.key(conf), conf);
|
AddToGroup(newGroupName, config.key(conf), conf);
|
||||||
}
|
}
|
||||||
@ -173,7 +176,7 @@ void ImportConfigWindow::on_beginImportBtn_clicked()
|
|||||||
|
|
||||||
if (!linkErrors.isEmpty())
|
if (!linkErrors.isEmpty())
|
||||||
{
|
{
|
||||||
for (auto item : linkErrors)
|
for (const auto &item : linkErrors)
|
||||||
{
|
{
|
||||||
vmessConnectionStringTxt->appendPlainText(linkErrors.key(item));
|
vmessConnectionStringTxt->appendPlainText(linkErrors.key(item));
|
||||||
errorsList->addItem(item);
|
errorsList->addItem(item);
|
||||||
@ -213,6 +216,8 @@ void ImportConfigWindow::on_selectImageBtn_clicked()
|
|||||||
imageFileEdit->setText(dir);
|
imageFileEdit->setText(dir);
|
||||||
//
|
//
|
||||||
QFile file(dir);
|
QFile file(dir);
|
||||||
|
if (!file.exists())
|
||||||
|
return;
|
||||||
file.open(QFile::OpenModeFlag::ReadOnly);
|
file.open(QFile::OpenModeFlag::ReadOnly);
|
||||||
auto buf = file.readAll();
|
auto buf = file.readAll();
|
||||||
file.close();
|
file.close();
|
||||||
@ -258,7 +263,7 @@ void ImportConfigWindow::on_errorsList_currentItemChanged(QListWidgetItem *curre
|
|||||||
|
|
||||||
void ImportConfigWindow::on_connectionEditBtn_clicked()
|
void ImportConfigWindow::on_connectionEditBtn_clicked()
|
||||||
{
|
{
|
||||||
OutboundEditor w(this);
|
OutboundEditor w(OUTBOUND(), this);
|
||||||
auto outboundEntry = w.OpenEditor();
|
auto outboundEntry = w.OpenEditor();
|
||||||
bool isChanged = w.result() == QDialog::Accepted;
|
bool isChanged = w.result() == QDialog::Accepted;
|
||||||
QString alias = w.GetFriendlyName();
|
QString alias = w.GetFriendlyName();
|
||||||
@ -283,7 +288,7 @@ void ImportConfigWindow::on_cancelImportBtn_clicked()
|
|||||||
void ImportConfigWindow::on_subscriptionButton_clicked()
|
void ImportConfigWindow::on_subscriptionButton_clicked()
|
||||||
{
|
{
|
||||||
hide();
|
hide();
|
||||||
SubscriptionEditor w;
|
SubscriptionEditor w(this);
|
||||||
w.exec();
|
w.exec();
|
||||||
auto importToComplex = !keepImportedInboundCheckBox->isEnabled();
|
auto importToComplex = !keepImportedInboundCheckBox->isEnabled();
|
||||||
connections.clear();
|
connections.clear();
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#include "w_MainWindow.hpp"
|
#include "w_MainWindow.hpp"
|
||||||
|
|
||||||
#include "components/pac/QvPACHandler.hpp"
|
#include "components/plugins/QvPluginHost.hpp"
|
||||||
#include "components/plugins/toolbar/QvToolbar.hpp"
|
#include "components/plugins/toolbar/QvToolbar.hpp"
|
||||||
#include "components/proxy/QvProxyConfigurator.hpp"
|
#include "components/proxy/QvProxyConfigurator.hpp"
|
||||||
#include "components/update/UpdateChecker.hpp"
|
#include "components/update/UpdateChecker.hpp"
|
||||||
@ -9,6 +9,7 @@
|
|||||||
#include "ui/editors/w_OutboundEditor.hpp"
|
#include "ui/editors/w_OutboundEditor.hpp"
|
||||||
#include "ui/editors/w_RoutesEditor.hpp"
|
#include "ui/editors/w_RoutesEditor.hpp"
|
||||||
#include "ui/w_ImportConfig.hpp"
|
#include "ui/w_ImportConfig.hpp"
|
||||||
|
#include "ui/w_PluginManager.hpp"
|
||||||
#include "ui/w_PreferencesWindow.hpp"
|
#include "ui/w_PreferencesWindow.hpp"
|
||||||
#include "ui/w_SubscriptionManager.hpp"
|
#include "ui/w_SubscriptionManager.hpp"
|
||||||
#include "ui/widgets/ConnectionInfoWidget.hpp"
|
#include "ui/widgets/ConnectionInfoWidget.hpp"
|
||||||
@ -50,11 +51,11 @@ QvMessageBusSlotImpl(MainWindow)
|
|||||||
|
|
||||||
void MainWindow::UpdateColorScheme()
|
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"));
|
importConfigButton->setIcon(QICON_R("import.png"));
|
||||||
updownImageBox->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_UI_COLORSCHEME_ROOT + "netspeed_arrow.png)");
|
updownImageBox_2->setStyleSheet("image: url(" + QV2RAY_COLORSCHEME_ROOT + "netspeed_arrow.png)");
|
||||||
//
|
//
|
||||||
tray_action_ShowHide->setIcon(this->windowIcon());
|
tray_action_ShowHide->setIcon(this->windowIcon());
|
||||||
action_RCM_Start->setIcon(QICON_R("connect.png"));
|
action_RCM_Start->setIcon(QICON_R("connect.png"));
|
||||||
@ -131,17 +132,17 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
|||||||
UpdateColorScheme();
|
UpdateColorScheme();
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
connect(ConnectionManager, &QvConfigHandler::OnCrashed, [&] {
|
connect(ConnectionManager, &QvConfigHandler::OnKernelCrashed, [&](const ConnectionId &, const QString &reason) {
|
||||||
this->show();
|
this->show();
|
||||||
QvMessageBoxWarn(this, tr("V2ray vcore terminated."),
|
QvMessageBoxWarn(this, tr("Kernel terminated."),
|
||||||
tr("V2ray vcore terminated unexpectedly.") + NEWLINE + NEWLINE +
|
tr("The kernel terminated unexpectedly:") + NEWLINE + reason + NEWLINE + NEWLINE +
|
||||||
tr("To solve the problem, read the V2ray log in the log text browser."));
|
tr("To solve the problem, read the kernel log in the log text browser."));
|
||||||
});
|
});
|
||||||
//
|
//
|
||||||
connect(ConnectionManager, &QvConfigHandler::OnConnected, this, &MainWindow::OnConnected);
|
connect(ConnectionManager, &QvConfigHandler::OnConnected, this, &MainWindow::OnConnected);
|
||||||
connect(ConnectionManager, &QvConfigHandler::OnDisconnected, this, &MainWindow::OnDisconnected);
|
connect(ConnectionManager, &QvConfigHandler::OnDisconnected, this, &MainWindow::OnDisconnected);
|
||||||
connect(ConnectionManager, &QvConfigHandler::OnStatsAvailable, this, &MainWindow::OnStatsAvailable);
|
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::OnConnectionDeleted, this, &MainWindow::OnConnectionDeleted);
|
||||||
connect(ConnectionManager, &QvConfigHandler::OnConnectionCreated, this, &MainWindow::OnConnectionCreated);
|
connect(ConnectionManager, &QvConfigHandler::OnConnectionCreated, this, &MainWindow::OnConnectionCreated);
|
||||||
@ -229,7 +230,6 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
|||||||
connectionListRCM_Menu->addAction(action_RCM_Delete);
|
connectionListRCM_Menu->addAction(action_RCM_Delete);
|
||||||
connect(action_RCM_Start, &QAction::triggered, this, &MainWindow::on_action_StartThis_triggered);
|
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_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_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_EditJson, &QAction::triggered, this, &MainWindow::on_action_RCM_EditAsJson_triggered);
|
||||||
connect(action_RCM_EditComplex, &QAction::triggered, this, &MainWindow::on_action_RCM_EditAsComplex_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();
|
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)
|
void MainWindow::keyReleaseEvent(QKeyEvent *e)
|
||||||
@ -383,11 +390,6 @@ void MainWindow::on_action_StartThis_triggered()
|
|||||||
|
|
||||||
MainWindow::~MainWindow()
|
MainWindow::~MainWindow()
|
||||||
{
|
{
|
||||||
if (GlobalConfig.inboundConfig.pacConfig.enablePAC && pacServer != nullptr && pacServer->isRunning())
|
|
||||||
{
|
|
||||||
// Wait for PAC server to finish.
|
|
||||||
pacServer->wait();
|
|
||||||
}
|
|
||||||
hTray.hide();
|
hTray.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -478,12 +480,18 @@ void MainWindow::on_connectionListWidget_customContextMenuRequested(const QPoint
|
|||||||
auto item = connectionListWidget->itemAt(connectionListWidget->mapFromGlobal(_pos));
|
auto item = connectionListWidget->itemAt(connectionListWidget->mapFromGlobal(_pos));
|
||||||
if (item != nullptr)
|
if (item != nullptr)
|
||||||
{
|
{
|
||||||
if (GetItemWidget(item)->IsConnection())
|
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);
|
connectionListRCM_Menu->popup(_pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::on_action_RCM_DeleteThese_triggered()
|
void MainWindow::on_action_RCM_DeleteThese_triggered()
|
||||||
{
|
{
|
||||||
@ -492,10 +500,17 @@ void MainWindow::on_action_RCM_DeleteThese_triggered()
|
|||||||
for (auto item : connectionListWidget->selectedItems())
|
for (auto item : connectionListWidget->selectedItems())
|
||||||
{
|
{
|
||||||
auto widget = GetItemWidget(item);
|
auto widget = GetItemWidget(item);
|
||||||
|
if (widget)
|
||||||
|
{
|
||||||
if (widget->IsConnection())
|
if (widget->IsConnection())
|
||||||
{
|
{
|
||||||
connlist.append(get<1>(widget->Identifier()));
|
connlist.append(get<1>(widget->Identifier()));
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
connlist.append(ConnectionManager->GetGroupMetaObject(get<0>(widget->Identifier())).connections);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG(MODULE_UI, "Selected " + QSTRN(connlist.count()) + " items")
|
LOG(MODULE_UI, "Selected " + QSTRN(connlist.count()) + " items")
|
||||||
@ -568,13 +583,17 @@ void MainWindow::on_connectionListWidget_itemDoubleClicked(QTreeWidgetItem *item
|
|||||||
void MainWindow::OnDisconnected(const ConnectionId &id)
|
void MainWindow::OnDisconnected(const ConnectionId &id)
|
||||||
{
|
{
|
||||||
Q_UNUSED(id)
|
Q_UNUSED(id)
|
||||||
|
hTray.setIcon(Q_TRAYICON("tray.png"));
|
||||||
tray_action_Start->setEnabled(true);
|
tray_action_Start->setEnabled(true);
|
||||||
tray_action_Stop->setEnabled(false);
|
tray_action_Stop->setEnabled(false);
|
||||||
tray_action_Restart->setEnabled(false);
|
tray_action_Restart->setEnabled(false);
|
||||||
tray_SystemProxyMenu->setEnabled(false);
|
tray_SystemProxyMenu->setEnabled(false);
|
||||||
lastConnectedId = id;
|
lastConnectedId = id;
|
||||||
locateBtn->setEnabled(false);
|
locateBtn->setEnabled(false);
|
||||||
|
if (!GlobalConfig.uiConfig.quietMode)
|
||||||
|
{
|
||||||
this->hTray.showMessage("Qv2ray", tr("Disconnected from: ") + GetDisplayName(id), this->windowIcon());
|
this->hTray.showMessage("Qv2ray", tr("Disconnected from: ") + GetDisplayName(id), this->windowIcon());
|
||||||
|
}
|
||||||
hTray.setToolTip(TRAY_TOOLTIP_PREFIX);
|
hTray.setToolTip(TRAY_TOOLTIP_PREFIX);
|
||||||
netspeedLabel->setText("0.00 B/s" NEWLINE "0.00 B/s");
|
netspeedLabel->setText("0.00 B/s" NEWLINE "0.00 B/s");
|
||||||
dataamountLabel->setText("0.00 B" NEWLINE "0.00 B");
|
dataamountLabel->setText("0.00 B" NEWLINE "0.00 B");
|
||||||
@ -583,16 +602,12 @@ void MainWindow::OnDisconnected(const ConnectionId &id)
|
|||||||
{
|
{
|
||||||
ClearSystemProxy();
|
ClearSystemProxy();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GlobalConfig.inboundConfig.pacConfig.enablePAC)
|
|
||||||
{
|
|
||||||
pacServer->stopServer();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::OnConnected(const ConnectionId &id)
|
void MainWindow::OnConnected(const ConnectionId &id)
|
||||||
{
|
{
|
||||||
Q_UNUSED(id)
|
Q_UNUSED(id)
|
||||||
|
hTray.setIcon(Q_TRAYICON("tray-connected.png"));
|
||||||
tray_action_Start->setEnabled(false);
|
tray_action_Start->setEnabled(false);
|
||||||
tray_action_Stop->setEnabled(true);
|
tray_action_Stop->setEnabled(true);
|
||||||
tray_action_Restart->setEnabled(true);
|
tray_action_Restart->setEnabled(true);
|
||||||
@ -601,70 +616,14 @@ void MainWindow::OnConnected(const ConnectionId &id)
|
|||||||
locateBtn->setEnabled(true);
|
locateBtn->setEnabled(true);
|
||||||
on_clearlogButton_clicked();
|
on_clearlogButton_clicked();
|
||||||
auto name = GetDisplayName(id);
|
auto name = GetDisplayName(id);
|
||||||
|
if (!GlobalConfig.uiConfig.quietMode)
|
||||||
|
{
|
||||||
this->hTray.showMessage("Qv2ray", tr("Connected: ") + name, this->windowIcon());
|
this->hTray.showMessage("Qv2ray", tr("Connected: ") + name, this->windowIcon());
|
||||||
|
}
|
||||||
hTray.setToolTip(TRAY_TOOLTIP_PREFIX NEWLINE + tr("Connected: ") + name);
|
hTray.setToolTip(TRAY_TOOLTIP_PREFIX NEWLINE + tr("Connected: ") + name);
|
||||||
connetionStatusLabel->setText(tr("Connected: ") + name);
|
connetionStatusLabel->setText(tr("Connected: ") + name);
|
||||||
//
|
//
|
||||||
ConnectionManager->StartLatencyTest(id);
|
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)
|
if (GlobalConfig.inboundConfig.setSystemProxy)
|
||||||
{
|
{
|
||||||
MWSetSystemProxy();
|
MWSetSystemProxy();
|
||||||
@ -860,7 +819,7 @@ void MainWindow::OnGroupDeleted(const GroupId &id, const QList<ConnectionId> &co
|
|||||||
|
|
||||||
void MainWindow::on_locateBtn_clicked()
|
void MainWindow::on_locateBtn_clicked()
|
||||||
{
|
{
|
||||||
auto id = ConnectionManager->CurrentConnection();
|
auto id = KernelInstance->CurrentConnection();
|
||||||
if (id != NullConnectionId)
|
if (id != NullConnectionId)
|
||||||
{
|
{
|
||||||
connectionListWidget->setCurrentItem(connectionNodes.value(id).get());
|
connectionListWidget->setCurrentItem(connectionNodes.value(id).get());
|
||||||
@ -964,7 +923,10 @@ void MainWindow::on_action_RCM_SetAutoConnection_triggered()
|
|||||||
auto widget = GetItemWidget(current);
|
auto widget = GetItemWidget(current);
|
||||||
auto &conn = get<1>(widget->Identifier());
|
auto &conn = get<1>(widget->Identifier());
|
||||||
GlobalConfig.autoStartId = conn.toString();
|
GlobalConfig.autoStartId = conn.toString();
|
||||||
|
if (!GlobalConfig.uiConfig.quietMode)
|
||||||
|
{
|
||||||
hTray.showMessage(tr("Set auto connection"), tr("Set %1 as auto connect.").arg(GetDisplayName(conn)));
|
hTray.showMessage(tr("Set auto connection"), tr("Set %1 as auto connect.").arg(GetDisplayName(conn)));
|
||||||
|
}
|
||||||
SaveGlobalSettings();
|
SaveGlobalSettings();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -975,6 +937,17 @@ void MainWindow::on_action_RCM_ClearUsage_triggered()
|
|||||||
if (current != nullptr)
|
if (current != nullptr)
|
||||||
{
|
{
|
||||||
auto widget = GetItemWidget(current);
|
auto widget = GetItemWidget(current);
|
||||||
|
if (widget)
|
||||||
|
{
|
||||||
|
if (widget->IsConnection())
|
||||||
ConnectionManager->ClearConnectionUsage(get<1>(widget->Identifier()));
|
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/HTTPRequestHelper.hpp"
|
||||||
#include "common/LogHighlighter.hpp"
|
#include "common/LogHighlighter.hpp"
|
||||||
#include "components/pac/QvPACHandler.hpp"
|
|
||||||
#include "components/speedchart/speedwidget.hpp"
|
#include "components/speedchart/speedwidget.hpp"
|
||||||
#include "core/handler/ConfigHandler.hpp"
|
#include "core/handler/ConfigHandler.hpp"
|
||||||
#include "ui/messaging/QvMessageBus.hpp"
|
#include "ui/messaging/QvMessageBus.hpp"
|
||||||
@ -61,6 +60,8 @@ class MainWindow
|
|||||||
void on_connectionListWidget_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous);
|
void on_connectionListWidget_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous);
|
||||||
void on_masterLogBrowser_textChanged();
|
void on_masterLogBrowser_textChanged();
|
||||||
|
|
||||||
|
void on_pluginsBtn_clicked();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void on_actionExit_triggered();
|
void on_actionExit_triggered();
|
||||||
void on_action_StartThis_triggered();
|
void on_action_StartThis_triggered();
|
||||||
@ -108,7 +109,6 @@ class MainWindow
|
|||||||
// Charts
|
// Charts
|
||||||
SpeedWidget *speedChartWidget;
|
SpeedWidget *speedChartWidget;
|
||||||
QSystemTrayIcon hTray;
|
QSystemTrayIcon hTray;
|
||||||
PACServer *pacServer;
|
|
||||||
SyntaxHighlighter *vCoreLogHighlighter;
|
SyntaxHighlighter *vCoreLogHighlighter;
|
||||||
ConnectionInfoWidget *infoWidget;
|
ConnectionInfoWidget *infoWidget;
|
||||||
//
|
//
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
<widget class="QWidget" name="centralWidget">
|
<widget class="QWidget" name="centralWidget">
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,1,0">
|
<layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,1,0">
|
||||||
<item>
|
<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">
|
<property name="spacing">
|
||||||
<number>5</number>
|
<number>5</number>
|
||||||
</property>
|
</property>
|
||||||
@ -33,6 +33,13 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="pluginsBtn">
|
||||||
|
<property name="text">
|
||||||
|
<string>Plugins</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="preferencesBtn">
|
<widget class="QPushButton" name="preferencesBtn">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@ -553,7 +560,6 @@
|
|||||||
<layoutdefault spacing="6" margin="11"/>
|
<layoutdefault spacing="6" margin="11"/>
|
||||||
<tabstops>
|
<tabstops>
|
||||||
<tabstop>subsButton</tabstop>
|
<tabstop>subsButton</tabstop>
|
||||||
<tabstop>preferencesBtn</tabstop>
|
|
||||||
<tabstop>connectionListWidget</tabstop>
|
<tabstop>connectionListWidget</tabstop>
|
||||||
<tabstop>importConfigButton</tabstop>
|
<tabstop>importConfigButton</tabstop>
|
||||||
</tabstops>
|
</tabstops>
|
||||||
|
@ -4,102 +4,66 @@
|
|||||||
|
|
||||||
void MainWindow::MWSetSystemProxy()
|
void MainWindow::MWSetSystemProxy()
|
||||||
{
|
{
|
||||||
bool usePAC = GlobalConfig.inboundConfig.pacConfig.enablePAC;
|
auto inboundPorts = KernelInstance->InboundPorts();
|
||||||
bool pacUseSocks = GlobalConfig.inboundConfig.pacConfig.useSocksProxy;
|
bool httpEnabled = inboundPorts.contains("http");
|
||||||
bool httpEnabled = GlobalConfig.inboundConfig.useHTTP;
|
bool socksEnabled = inboundPorts.contains("socks");
|
||||||
bool socksEnabled = GlobalConfig.inboundConfig.useSocks;
|
auto httpPort = inboundPorts["http"];
|
||||||
//
|
auto socksPort = inboundPorts["socks"];
|
||||||
bool isComplex = IsComplexConfig(ConnectionManager->CurrentConnection());
|
|
||||||
|
|
||||||
if (!isComplex)
|
|
||||||
{
|
|
||||||
// Is simple config and we will try to set system proxy.
|
|
||||||
LOG(MODULE_UI, "Preparing to set system proxy")
|
|
||||||
//
|
|
||||||
QString proxyAddress;
|
QString proxyAddress;
|
||||||
bool canSetSystemProxy = true;
|
|
||||||
|
|
||||||
if (usePAC)
|
|
||||||
{
|
|
||||||
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)
|
if (httpEnabled || socksEnabled)
|
||||||
{
|
{
|
||||||
// Not use PAC, System proxy should use HTTP or SOCKS
|
proxyAddress = "127.0.0.1";
|
||||||
LOG(MODULE_PROXY, "Setting up system proxy.")
|
SetSystemProxy(proxyAddress, httpPort, socksPort);
|
||||||
// A 'proxy host' should be a host WITHOUT `http://` uri scheme
|
hTray.setIcon(Q_TRAYICON("tray-systemproxy.png"));
|
||||||
proxyAddress = "localhost";
|
if (!GlobalConfig.uiConfig.quietMode)
|
||||||
}
|
|
||||||
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."));
|
hTray.showMessage("Qv2ray", tr("System proxy configured."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
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()
|
void MainWindow::MWClearSystemProxy()
|
||||||
{
|
{
|
||||||
ClearSystemProxy();
|
ClearSystemProxy();
|
||||||
|
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."));
|
hTray.showMessage("Qv2ray", tr("System proxy removed."));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::CheckSubscriptionsUpdate()
|
void MainWindow::CheckSubscriptionsUpdate()
|
||||||
{
|
{
|
||||||
QStringList updateList;
|
QStringList updateList;
|
||||||
|
|
||||||
auto subscriptions = ConnectionManager->Subscriptions();
|
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);
|
// The update is ignored.
|
||||||
auto renewTime = lastRenewDate.addSecs(into.updateInterval * 86400);
|
if (info.updateInterval == 0)
|
||||||
|
continue;
|
||||||
|
//
|
||||||
|
const auto &lastRenewDate = QDateTime::fromTime_t(info.lastUpdated);
|
||||||
|
const auto &renewTime = lastRenewDate.addSecs(info.updateInterval * 86400);
|
||||||
LOG(MODULE_SUBSCRIPTION, //
|
LOG(MODULE_SUBSCRIPTION, //
|
||||||
"Subscription \"" + entry.toString() + "\": " + //
|
"Subscription \"" + info.displayName + "\": " + //
|
||||||
NEWLINE + " --> Last renewal time: " + lastRenewDate.toString() + //
|
NEWLINE + " --> Last renewal time: " + lastRenewDate.toString() + //
|
||||||
NEWLINE + " --> Renew interval: " + QSTRN(into.updateInterval) + //
|
NEWLINE + " --> Renew interval: " + QSTRN(info.updateInterval) + //
|
||||||
NEWLINE + " --> Ideal renew time: " + renewTime.toString()) //
|
NEWLINE + " --> Ideal renew time: " + renewTime.toString()) //
|
||||||
|
|
||||||
if (renewTime <= QDateTime::currentDateTime())
|
if (renewTime <= QDateTime::currentDateTime())
|
||||||
{
|
{
|
||||||
LOG(MODULE_SUBSCRIPTION, "Subscription: " + entry.toString() + " needs to be updated.")
|
LOG(MODULE_SUBSCRIPTION, "Subscription: " + info.displayName + " needs to be updated.")
|
||||||
updateList.append(entry.toString());
|
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/QvHelpers.hpp"
|
||||||
#include "common/QvTranslator.hpp"
|
#include "common/QvTranslator.hpp"
|
||||||
#include "components/autolaunch/QvAutoLaunch.hpp"
|
#include "components/autolaunch/QvAutoLaunch.hpp"
|
||||||
|
#include "components/plugins/interface/QvPluginInterface.hpp"
|
||||||
#include "components/plugins/toolbar/QvToolbar.hpp"
|
#include "components/plugins/toolbar/QvToolbar.hpp"
|
||||||
#include "core/connection/ConnectionIO.hpp"
|
#include "core/connection/ConnectionIO.hpp"
|
||||||
#include "core/handler/ConfigHandler.hpp"
|
#include "core/handler/ConfigHandler.hpp"
|
||||||
#include "core/kernel/KernelInteractions.hpp"
|
#include "core/kernel/V2rayKernelInteractions.hpp"
|
||||||
#include "core/settings/SettingsBackend.hpp"
|
#include "core/settings/SettingsBackend.hpp"
|
||||||
#include "ui/widgets/RouteSettingsMatrix.hpp"
|
#include "ui/widgets/RouteSettingsMatrix.hpp"
|
||||||
|
|
||||||
@ -69,6 +70,7 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent), Current
|
|||||||
qvBuildInfo->setText(QV2RAY_BUILD_INFO);
|
qvBuildInfo->setText(QV2RAY_BUILD_INFO);
|
||||||
qvBuildExInfo->setText(QV2RAY_BUILD_EXTRA_INFO);
|
qvBuildExInfo->setText(QV2RAY_BUILD_EXTRA_INFO);
|
||||||
qvBuildTime->setText(__DATE__ " " __TIME__);
|
qvBuildTime->setText(__DATE__ " " __TIME__);
|
||||||
|
qvPluginInterfaceVersionLabel->setText(tr("Version: %1").arg(QSTRN(QV2RAY_PLUGIN_INTERFACE_VERSION)));
|
||||||
//
|
//
|
||||||
// Deep copy
|
// Deep copy
|
||||||
CurrentConfig = GlobalConfig;
|
CurrentConfig = GlobalConfig;
|
||||||
@ -87,14 +89,6 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent), Current
|
|||||||
//
|
//
|
||||||
//
|
//
|
||||||
listenIPTxt->setText(CurrentConfig.inboundConfig.listenip);
|
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;
|
bool have_http = CurrentConfig.inboundConfig.useHTTP;
|
||||||
httpGroupBox->setChecked(have_http);
|
httpGroupBox->setChecked(have_http);
|
||||||
@ -134,12 +128,26 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent), Current
|
|||||||
//
|
//
|
||||||
localDNSCb->setChecked(CurrentConfig.connectionConfig.withLocalDNS);
|
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();
|
DNSListTxt->clear();
|
||||||
|
|
||||||
for (auto dnsStr : CurrentConfig.connectionConfig.dnsList)
|
for (auto dnsStr : CurrentConfig.connectionConfig.dnsList)
|
||||||
{
|
{
|
||||||
auto str = dnsStr.trimmed();
|
auto str = dnsStr.trimmed();
|
||||||
|
|
||||||
if (!str.isEmpty())
|
if (!str.isEmpty())
|
||||||
{
|
{
|
||||||
DNSListTxt->appendPlainText(str);
|
DNSListTxt->appendPlainText(str);
|
||||||
@ -150,11 +158,11 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent), Current
|
|||||||
updateSettingsGroupBox->setEnabled(false);
|
updateSettingsGroupBox->setEnabled(false);
|
||||||
updateSettingsGroupBox->setToolTip(tr("Update is disabled by your vendor."));
|
updateSettingsGroupBox->setToolTip(tr("Update is disabled by your vendor."));
|
||||||
#endif
|
#endif
|
||||||
|
//
|
||||||
updateChannelCombo->setCurrentIndex(CurrentConfig.updateConfig.updateChannel);
|
updateChannelCombo->setCurrentIndex(CurrentConfig.updateConfig.updateChannel);
|
||||||
cancelIgnoreVersionBtn->setEnabled(!CurrentConfig.updateConfig.ignoredVersion.isEmpty());
|
cancelIgnoreVersionBtn->setEnabled(!CurrentConfig.updateConfig.ignoredVersion.isEmpty());
|
||||||
ignoredNextVersion->setText(CurrentConfig.updateConfig.ignoredVersion);
|
ignoredNextVersion->setText(CurrentConfig.updateConfig.ignoredVersion);
|
||||||
|
//
|
||||||
for (auto i = 0; i < CurrentConfig.toolBarConfig.Pages.size(); i++)
|
for (auto i = 0; i < CurrentConfig.toolBarConfig.Pages.size(); i++)
|
||||||
{
|
{
|
||||||
nsBarPagesList->addItem(tr("Page") + QSTRN(i + 1) + ": " + QSTRN(CurrentConfig.toolBarConfig.Pages[i].Lines.size()) + " " +
|
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);
|
maxLogLinesSB->setValue(CurrentConfig.uiConfig.maximumLogLines);
|
||||||
//
|
//
|
||||||
pacListenAddrLabel->setText("http://" + (pacProxyTxt->text().isEmpty() ? "127.0.0.1" : pacProxyTxt->text()) + ":" +
|
setSysProxyCB->setChecked(CurrentConfig.inboundConfig.setSystemProxy);
|
||||||
QSTRN(pacPortSB->value()) + "/pac");
|
|
||||||
//
|
//
|
||||||
finishedLoading = true;
|
finishedLoading = true;
|
||||||
routeSettingsWidget = new RouteSettingsMatrixWidget(CurrentConfig.kernelConfig.AssetsPath(), this);
|
routeSettingsWidget = new RouteSettingsMatrixWidget(CurrentConfig.kernelConfig.AssetsPath(), this);
|
||||||
@ -256,12 +263,6 @@ void PreferencesWindow::on_buttonBox_accepted()
|
|||||||
ports << CurrentConfig.inboundConfig.socks_port;
|
ports << CurrentConfig.inboundConfig.socks_port;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CurrentConfig.inboundConfig.pacConfig.enablePAC)
|
|
||||||
{
|
|
||||||
size++;
|
|
||||||
ports << CurrentConfig.inboundConfig.pacConfig.port;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!StartupOption.noAPI)
|
if (!StartupOption.noAPI)
|
||||||
{
|
{
|
||||||
size++;
|
size++;
|
||||||
@ -932,84 +933,6 @@ void PreferencesWindow::on_darkTrayCB_stateChanged(int arg1)
|
|||||||
CurrentConfig.uiConfig.useDarkTrayIcon = arg1 == Qt::Checked;
|
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)
|
void PreferencesWindow::on_setSysProxyCB_stateChanged(int arg1)
|
||||||
{
|
{
|
||||||
LOADINGCHECK
|
LOADINGCHECK
|
||||||
@ -1017,28 +940,12 @@ void PreferencesWindow::on_setSysProxyCB_stateChanged(int arg1)
|
|||||||
CurrentConfig.inboundConfig.setSystemProxy = arg1 == Qt::Checked;
|
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()
|
void PreferencesWindow::on_pushButton_clicked()
|
||||||
{
|
{
|
||||||
LOADINGCHECK
|
LOADINGCHECK
|
||||||
QDesktopServices::openUrl(QUrl::fromUserInput(QV2RAY_RULES_DIR));
|
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)
|
void PreferencesWindow::on_autoStartSubsCombo_currentIndexChanged(const QString &arg1)
|
||||||
{
|
{
|
||||||
LOADINGCHECK if (arg1.isEmpty())
|
LOADINGCHECK if (arg1.isEmpty())
|
||||||
@ -1147,23 +1054,6 @@ void PreferencesWindow::on_fpPortSB_valueChanged(int arg1)
|
|||||||
CurrentConfig.connectionConfig.forwardProxyConfig.port = 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()
|
void PreferencesWindow::on_checkVCoreSettings_clicked()
|
||||||
{
|
{
|
||||||
auto vcorePath = vCorePathTxt->text();
|
auto vcorePath = vCorePathTxt->text();
|
||||||
@ -1196,20 +1086,6 @@ void PreferencesWindow::on_socksGroupBox_clicked(bool checked)
|
|||||||
CurrentConfig.inboundConfig.useSocks = 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)
|
void PreferencesWindow::on_fpGroupBox_clicked(bool checked)
|
||||||
{
|
{
|
||||||
LOADINGCHECK
|
LOADINGCHECK
|
||||||
@ -1237,3 +1113,81 @@ void PreferencesWindow::on_updateChannelCombo_currentIndexChanged(int index)
|
|||||||
CurrentConfig.updateConfig.updateChannel = index;
|
CurrentConfig.updateConfig.updateChannel = index;
|
||||||
CurrentConfig.updateConfig.ignoredVersion.clear();
|
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_darkTrayCB_stateChanged(int arg1);
|
||||||
|
|
||||||
void on_pacGoBtn_clicked();
|
|
||||||
|
|
||||||
void on_pacPortSB_valueChanged(int arg1);
|
|
||||||
|
|
||||||
void on_setSysProxyCB_stateChanged(int arg1);
|
void on_setSysProxyCB_stateChanged(int arg1);
|
||||||
|
|
||||||
void on_pacProxyCB_currentIndexChanged(int index);
|
|
||||||
|
|
||||||
void on_pushButton_clicked();
|
void on_pushButton_clicked();
|
||||||
|
|
||||||
void on_pacProxyTxt_textEdited(const QString &arg1);
|
|
||||||
|
|
||||||
void on_autoStartSubsCombo_currentIndexChanged(const QString &arg1);
|
void on_autoStartSubsCombo_currentIndexChanged(const QString &arg1);
|
||||||
|
|
||||||
void on_autoStartConnCombo_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_fpPortSB_valueChanged(int arg1);
|
||||||
|
|
||||||
void on_pacProxyTxt_textChanged(const QString &arg1);
|
|
||||||
|
|
||||||
void on_checkVCoreSettings_clicked();
|
void on_checkVCoreSettings_clicked();
|
||||||
|
|
||||||
void on_httpGroupBox_clicked(bool checked);
|
void on_httpGroupBox_clicked(bool checked);
|
||||||
|
|
||||||
void on_socksGroupBox_clicked(bool checked);
|
void on_socksGroupBox_clicked(bool checked);
|
||||||
|
|
||||||
void on_pacGroupBox_clicked(bool checked);
|
|
||||||
|
|
||||||
void on_fpGroupBox_clicked(bool checked);
|
void on_fpGroupBox_clicked(bool checked);
|
||||||
|
|
||||||
void on_maxLogLinesSB_valueChanged(int arg1);
|
void on_maxLogLinesSB_valueChanged(int arg1);
|
||||||
@ -168,6 +156,28 @@ class PreferencesWindow
|
|||||||
|
|
||||||
void on_updateChannelCombo_currentIndexChanged(int index);
|
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:
|
private:
|
||||||
//
|
//
|
||||||
RouteSettingsMatrixWidget *routeSettingsWidget;
|
RouteSettingsMatrixWidget *routeSettingsWidget;
|
||||||
|
@ -181,9 +181,6 @@
|
|||||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QDoubleSpinBox" name="updateIntervalSB">
|
<widget class="QDoubleSpinBox" name="updateIntervalSB">
|
||||||
<property name="minimum">
|
|
||||||
<double>0.500000000000000</double>
|
|
||||||
</property>
|
|
||||||
<property name="maximum">
|
<property name="maximum">
|
||||||
<double>365.000000000000000</double>
|
<double>365.000000000000000</double>
|
||||||
</property>
|
</property>
|
||||||
|
@ -144,6 +144,8 @@ void ConnectionItemWidget::CancelRename()
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ConnectionItemWidget::BeginRename()
|
void ConnectionItemWidget::BeginRename()
|
||||||
|
{
|
||||||
|
if (IsConnection())
|
||||||
{
|
{
|
||||||
stackedWidget->setCurrentIndex(1);
|
stackedWidget->setCurrentIndex(1);
|
||||||
renameTxt->setStyle(QStyleFactory::create("Fusion"));
|
renameTxt->setStyle(QStyleFactory::create("Fusion"));
|
||||||
@ -151,6 +153,7 @@ void ConnectionItemWidget::BeginRename()
|
|||||||
renameTxt->setText(originalItemName);
|
renameTxt->setText(originalItemName);
|
||||||
renameTxt->setFocus();
|
renameTxt->setFocus();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ConnectionItemWidget::~ConnectionItemWidget()
|
ConnectionItemWidget::~ConnectionItemWidget()
|
||||||
{
|
{
|
||||||
|
@ -103,7 +103,7 @@ void RouteSettingsMatrixWidget::on_importSchemeBtn_clicked()
|
|||||||
|
|
||||||
// read the file and parse back to struct.
|
// read the file and parse back to struct.
|
||||||
// if error occurred on parsing, an exception will be thrown.
|
// 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);
|
auto scheme = StructFromJsonString<Qv2rayRouteScheme>(content);
|
||||||
|
|
||||||
// show the information of this scheme to user,
|
// show the information of this scheme to user,
|
||||||
@ -175,7 +175,7 @@ void RouteSettingsMatrixWidget::on_exportSchemeBtn_clicked()
|
|||||||
|
|
||||||
// serialize and write out
|
// serialize and write out
|
||||||
auto content = StructToJsonString(scheme);
|
auto content = StructToJsonString(scheme);
|
||||||
StringToFile(content, savePath.value());
|
StringToFile(content, ACCESS_OPTIONAL_VALUE(savePath));
|
||||||
|
|
||||||
// done
|
// done
|
||||||
// TODO: Give some success as Notification
|
// TODO: Give some success as Notification
|
||||||
|
@ -6,8 +6,8 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>600</width>
|
<width>582</width>
|
||||||
<height>409</height>
|
<height>404</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
@ -44,12 +44,6 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
|
||||||
<widget class="QGroupBox" name="groupBox">
|
|
||||||
<property name="title">
|
|
||||||
<string>Route Settings</string>
|
|
||||||
</property>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="label_3">
|
<widget class="QLabel" name="label_3">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@ -129,9 +123,6 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
<item>
|
<item>
|
||||||
|
@ -22,7 +22,7 @@ QvMessageBusSlotImpl(StreamSettingsWidget)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StreamSettingsObject StreamSettingsWidget::GetStreamSettings()
|
StreamSettingsObject StreamSettingsWidget::GetStreamSettings() const
|
||||||
{
|
{
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
@ -36,6 +36,7 @@ void StreamSettingsWidget::SetStreamObject(const StreamSettingsObject &sso)
|
|||||||
tlsCB->setChecked(stream.security == "tls");
|
tlsCB->setChecked(stream.security == "tls");
|
||||||
serverNameTxt->setText(stream.tlsSettings.serverName);
|
serverNameTxt->setText(stream.tlsSettings.serverName);
|
||||||
allowInsecureCB->setChecked(stream.tlsSettings.allowInsecure);
|
allowInsecureCB->setChecked(stream.tlsSettings.allowInsecure);
|
||||||
|
allowInsecureCiphersCB->setChecked(stream.tlsSettings.allowInsecureCiphers);
|
||||||
alpnTxt->setPlainText(stream.tlsSettings.alpn.join(NEWLINE));
|
alpnTxt->setPlainText(stream.tlsSettings.alpn.join(NEWLINE));
|
||||||
// TCP
|
// TCP
|
||||||
tcpHeaderTypeCB->setCurrentText(stream.tcpSettings.header.type);
|
tcpHeaderTypeCB->setCurrentText(stream.tcpSettings.header.type);
|
||||||
@ -47,9 +48,9 @@ void StreamSettingsWidget::SetStreamObject(const StreamSettingsObject &sso)
|
|||||||
// WS
|
// WS
|
||||||
wsPathTxt->setText(stream.wsSettings.path);
|
wsPathTxt->setText(stream.wsSettings.path);
|
||||||
QString wsHeaders;
|
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);
|
wsHeadersTxt->setPlainText(wsHeaders);
|
||||||
@ -284,3 +285,8 @@ void StreamSettingsWidget::on_alpnTxt_textChanged()
|
|||||||
{
|
{
|
||||||
stream.tlsSettings.alpn = SplitLines(alpnTxt->toPlainText());
|
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:
|
public:
|
||||||
explicit StreamSettingsWidget(QWidget *parent = nullptr);
|
explicit StreamSettingsWidget(QWidget *parent = nullptr);
|
||||||
void SetStreamObject(const StreamSettingsObject &sso);
|
void SetStreamObject(const StreamSettingsObject &sso);
|
||||||
StreamSettingsObject GetStreamSettings();
|
StreamSettingsObject GetStreamSettings() const;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void on_httpPathTxt_textEdited(const QString &arg1);
|
void on_httpPathTxt_textEdited(const QString &arg1);
|
||||||
@ -77,6 +77,8 @@ class StreamSettingsWidget
|
|||||||
|
|
||||||
void on_alpnTxt_textChanged();
|
void on_alpnTxt_textChanged();
|
||||||
|
|
||||||
|
void on_allowInsecureCiphersCB_stateChanged(int arg1);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QvMessageBusSlotDecl;
|
QvMessageBusSlotDecl;
|
||||||
StreamSettingsObject stream;
|
StreamSettingsObject stream;
|
||||||
|
@ -635,37 +635,37 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0">
|
<item row="3" column="0">
|
||||||
<widget class="QLabel" name="label_6">
|
<widget class="QLabel" name="label_6">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Server</string>
|
<string>Server</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="1">
|
<item row="3" column="1">
|
||||||
<widget class="QLineEdit" name="serverNameTxt"/>
|
<widget class="QLineEdit" name="serverNameTxt"/>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0">
|
<item row="4" column="0">
|
||||||
<widget class="QLabel" name="label_14">
|
<widget class="QLabel" name="label_14">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>ALPN</string>
|
<string>ALPN</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="1">
|
<item row="4" column="1">
|
||||||
<widget class="QPlainTextEdit" name="alpnTxt"/>
|
<widget class="QPlainTextEdit" name="alpnTxt"/>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="0">
|
<item row="0" column="0" colspan="2">
|
||||||
<widget class="QLabel" name="label_13">
|
<widget class="QCheckBox" name="tlsCB">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>TLS</string>
|
<string>Enable TLS</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="1">
|
<item row="2" column="0" colspan="2">
|
||||||
<widget class="QCheckBox" name="tlsCB">
|
<widget class="QCheckBox" name="allowInsecureCiphersCB">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Enabled</string>
|
<string>Allow Insecure Ciphers</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|