Merge branch 'dev' into version-v2

Former-commit-id: 6860f7b940
This commit is contained in:
Leroy.H.Y 2019-10-24 23:03:47 +08:00
commit ff994fecee
107 changed files with 5356 additions and 1184 deletions

View File

@ -1,10 +0,0 @@
install:
- git submodule update --init
- set QTDIR=C:\Qt\5.13.0\mingw73_64
- set PATH=%QTDIR%\bin;C:\Qt\Tools\mingw730_64\bin;C:\Windows\;C:\Windows\System32
build_script:
- cmd.exe /c tools\grpc_gen.bat
- mkdir build && cd build
- qmake ..\Qv2ray.pro
- mingw32-make -j4

131
.github/workflows/build-push.yml vendored Normal file
View File

@ -0,0 +1,131 @@
name: Build Cross-platform Qv2ray
on: push
jobs:
Windows:
name: Push build for win64
runs-on: windows-2019
steps:
- name: Checkout Sources
uses: actions/checkout@master
- name: Restore Git Submodules
run: git submodule update --init
# --------------------------------------------------------
- name: Install Qt
uses: jurplel/install-qt-action@v1
with:
arch: win64_mingw73
# --------------------------------------------------------
- name: Extract gRPC and protobuf
run: tools\grpc_gen.bat
# --------------------------------------------------------
- name: Build Qv2ray
run: |
mkdir build
cd build
qmake ..
mingw32-make -j4
- name: Make release package
run: |
cd build
mkdir package
cd package
copy ..\release\Qv2ray.exe .\
copy ..\..\libs\libcrypto-1_1.dll .\
copy ..\..\libs\libssl-1_1.dll .\
windeployqt ./Qv2ray.exe --compiler-runtime
cd ..
..\tools\7z.exe a Qv2ray.zip .\package
- name: Upload Artifact
uses: actions/upload-artifact@master
with:
name: Qv2ray.Win64.zip
path: build/Qv2ray.zip
# =========================================================================================
macOS:
name: Push build for macOS
runs-on: macOS-10.14
steps:
- name: Checkout Sources
uses: actions/checkout@master
- name: Restore Git Submodules
run: git submodule update --init
# --------------------------------------------------------
- name: Install Qt
uses: jurplel/install-qt-action@v1
# --------------------------------------------------------
- name: Extract gRPC and protobuf
run: tools/deps_macOS.sh
# --------------------------------------------------------
- name: Build Qv2ray
run: |
mkdir build
cd build
qmake ..
make -j4
- name: Make Package
run: |
cd build
cd Qv2ray.app
macdeployqt ./
cd ..
tar czf Qv2ray.app.tar.gz Qv2ray.app
- name: Upload Artifact
uses: actions/upload-artifact@master
with:
name: Qv2ray.macOS.app.zip
path: build/Qv2ray.app.tar.gz
# ===================================================================================
Linux:
name: Push build for Linux
runs-on: ubuntu-16.04
steps:
- name: Checkout Sources
uses: actions/checkout@master
- name: Restore Git Submodules
run: git submodule update --init
# --------------------------------------------------------
- name: Install Qt
uses: jurplel/install-qt-action@v1
# --------------------------------------------------------
- name: Install Packages
run: sudo apt install -y libgl-dev and openssl libx11-dev libxkbcommon-x11-dev libgrpc++-dev libprotobuf-dev protobuf-compiler protobuf-c-compiler protobuf-compiler-grpc
# --------------------------------------------------------
- name: Extract gRPC and protobuf
run: tools/grpc_gen.sh
# --------------------------------------------------------
- name: Update Locate DB
run: sudo updatedb
- name: Build Qv2ray
run: |
mkdir build
cd build
qmake ..
make -j4
- name: Generate FS Structure for AppImage
run: |
cd build
make install INSTALL_ROOT=AppDir
cd AppDir
mkdir -p ./usr/lib/
cp /lib/x86_64-linux-gnu/libssl.so.1.1 /lib/x86_64-linux-gnu/libcrypto.so.1.1 ./usr/lib/
cp /lib/x86_64-linux-gnu/libssl.so.1.0.0 /lib/x86_64-linux-gnu/libcrypto.so.1.0.0 ./usr/lib/
- name: Build AppImage using linuxdeployqt
run: |
cd build/AppDir
wget https://github.com/probonopd/linuxdeployqt/releases/download/6/linuxdeployqt-6-x86_64.AppImage
mv ./linuxdeployqt-6-x86_64.AppImage ../
chmod +x ../linuxdeployqt-6-x86_64.AppImage
../linuxdeployqt-6-x86_64.AppImage --appimage-extract
./squashfs-root/AppRun usr/share/applications/Qv2ray.desktop -appimage -no-strip -always-overwrite
mv ./Qv2ray*.AppImage ./Qv2ray.AppImage
- name: Upload Artifact
uses: actions/upload-artifact@master
with:
name: Qv2ray.linux.AppImage
path: build/AppDir/Qv2ray.AppImage

146
.github/workflows/build-release.yml vendored Normal file
View File

@ -0,0 +1,146 @@
name: Build Artifact for Release
on:
release:
types: [prereleased]
jobs:
Windows:
name: Release for Windows x64
runs-on: windows-2019
steps:
- name: Checkout Sources
uses: actions/checkout@master
- name: Restore Git Submodules
run: git submodule update --init
# --------------------------------------------------------
- name: Install Qt
uses: jurplel/install-qt-action@v1
with:
arch: win64_mingw73
# --------------------------------------------------------
- name: Build Qv2ray
run: |
mkdir build
cd build
qmake ..
mingw32-make -j4
- name: Make release package
run: |
cd build
mkdir package
cd package
copy ..\release\Qv2ray.exe .\
copy ..\..\libs\libcrypto-1_1.dll .\
copy ..\..\libs\libssl-1_1.dll .\
windeployqt ./Qv2ray.exe --compiler-runtime
cd ..
..\tools\7z.exe a Qv2ray.zip .\package
- name: Upload Artifact
uses: actions/upload-artifact@master
with:
name: Qv2ray.Win64.zip
path: build/Qv2ray.zip
- name: Upload binaries to release
uses: svenstaro/upload-release-action@v1-release
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: build/Qv2ray.zip
asset_name: Qv2ray-${{ github.ref }}-win64.zip
tag: ${{ github.ref }}
overwrite: true
# =========================================================================================
macOS:
name: Release for macOS
runs-on: macOS-10.14
steps:
- name: Checkout Sources
uses: actions/checkout@master
- name: Restore Git Submodules
run: git submodule update --init
# --------------------------------------------------------
- name: Install Qt
uses: jurplel/install-qt-action@v1
# --------------------------------------------------------
- name: Build Qv2ray
run: |
mkdir build
cd build
qmake ..
make -j4
- name: Make Package
run: |
cd build
cd Qv2ray.app
macdeployqt ./
cd ..
tar czf Qv2ray.app.tar.gz Qv2ray.app
- name: Upload Artifact
uses: actions/upload-artifact@master
with:
name: Qv2ray.macOS.app.zip
path: build/Qv2ray.app.tar.gz
- name: Upload binaries to release
uses: svenstaro/upload-release-action@v1-release
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: build/Qv2ray.app.tar.gz
asset_name: Qv2ray-${{ github.ref }}-macOS.tar.gz
tag: ${{ github.ref }}
overwrite: true
# ===================================================================================
Linux:
name: Release for linux
runs-on: ubuntu-16.04
steps:
- name: Checkout Sources
uses: actions/checkout@master
- name: Restore Git Submodules
run: git submodule update --init
# --------------------------------------------------------
- name: Install Qt
uses: jurplel/install-qt-action@v1
# --------------------------------------------------------
- name: Install libgl-dev and openssl libx11-dev libxkbcommon-x11-dev
run: sudo apt install -y libgl-dev openssl libx11-dev libxkbcommon-x11-dev
- name: Build Qv2ray
run: |
mkdir build
cd build
qmake ..
make -j4
- name: Generate FS Structure for AppImage
run: |
cd build
make install INSTALL_ROOT=AppDir
cd AppDir
mkdir -p ./opt/Qv2ray/lib/
cp /lib/x86_64-linux-gnu/libssl.so.1.0.0 /lib/x86_64-linux-gnu/libcrypto.so.1.0.0 ./opt/Qv2ray/lib/
cp /lib/x86_64-linux-gnu/libssl.so.1.1 /lib/x86_64-linux-gnu/libcrypto.so.1.1 ./opt/Qv2ray/lib/
- name: Build AppImage using linuxdeployqt
run: |
cd build/AppDir
wget https://github.com/probonopd/linuxdeployqt/releases/download/6/linuxdeployqt-6-x86_64.AppImage
mv ./linuxdeployqt-6-x86_64.AppImage ../
chmod +x ../linuxdeployqt-6-x86_64.AppImage
../linuxdeployqt-6-x86_64.AppImage --appimage-extract
./squashfs-root/AppRun opt/Qv2ray/share/applications/Qv2ray.desktop -appimage -no-strip -always-overwrite
mv ./Qv2ray*.AppImage ./Qv2ray.AppImage
- name: Upload Artifact
uses: actions/upload-artifact@master
with:
name: Qv2ray.linux.AppImage
path: build/AppDir/Qv2ray.AppImage
- name: Upload binaries to release
uses: svenstaro/upload-release-action@v1-release
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: build/AppDir/Qv2ray.AppImage
asset_name: Qv2ray-${{ github.ref }}-linux.AppImage
tag: ${{ github.ref }}
overwrite: true

6
.gitignore vendored
View File

@ -4,14 +4,8 @@
*.qm
./.vscode
*.pb.cc
*.pb.h
libs/gen/
libs/gRPC-win32/
libs/gRPC-macOS/
build/
src/build/
OpenBuildService/

View File

@ -1,31 +0,0 @@
language: cpp
matrix:
include:
- os: linux
dist: bionic
sudo: required
env: BADGE=linux
- os: osx
env: BADGE=osx
before_install:
- if [ "$BADGE" = "linux" ]; then sudo apt-get update; fi
install:
- if [ "$BADGE" = "linux" ]; then sudo apt-get install qtchooser libgl-dev qt5-default qttools5-dev-tools libgrpc++-dev libprotobuf-dev protobuf-compiler protobuf-c-compiler protobuf-compiler-grpc; fi
- if [ "$BADGE" = "osx" ]; then brew install qt5 --verbose; fi
before_script:
- if [ "$BADGE" = "osx" ]; then export PATH="/usr/local/Cellar/qt/5.13.1/bin:$PATH"; fi
- if [ "$BADGE" = "linux" ]; then sudo cp ./tools/lrelease.prf /usr/lib/x86_64-linux-gnu/qt5/mkspecs/features/; fi
- if [ "$BADGE" = "linux" ]; then chmod +x ./tools/grpc_gen.sh; fi
script:
- git submodule update --init
- if [ "$BADGE" = "linux" ]; then ./tools/grpc_gen.sh; fi
- if [ "$BADGE" = "osx" ]; then ./tools/deps_macOS.sh; fi
- mkdir build && cd ./build
- qmake ../
- if [ "$BADGE" = "linux" ]; then make compiler_lrelease_make_all; fi
- make -j8

21
.vscode/c_cpp_properties.json vendored Normal file
View File

@ -0,0 +1,21 @@
{
"configurations": [
{
"name": "Qt-c++",
"includePath": [
"${workspaceFolder}/**",
"/usr/include",
"/usr/include/**",
"/usr/local/include",
"${workspaceFolder}/src/**",
"/home/coder/Qt5.12.5/5.12.5/gcc_64/include",
"/home/coder/Qt5.12.5/5.12.5/gcc_64/include/**",
"/usr/include/linux"
],
"defines": [],
"compilerPath": "/usr/bin/gcc-8",
"intelliSenseMode": "gcc-x64"
}
],
"version": 4
}

27
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,27 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) Launch",
"type": "cppdbg",
"request": "launch",
"program": "Qv2ray",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
]
}

12
.vscode/settings.json vendored
View File

@ -1,12 +1,6 @@
{
"files.associations": {
"hash_map": "cpp",
"*.tcc": "cpp",
"deque": "cpp",
"list": "cpp",
"string": "cpp",
"unordered_map": "cpp",
"unordered_set": "cpp",
"vector": "cpp"
}
"qfileinfo": "cpp"
},
"C_Cpp.errorSquiggles": "Disabled"
}

32
BUILDING.md Normal file
View File

@ -0,0 +1,32 @@
# Building Qv2ray
Qv2ray is a cross platform v2ray GUI, so you can run your build on all desktop platforms.
## Obtaining Source Code
-
## Dependencies
- Qt version >= 5
- Qt version 5.12 and 5.13 is recommended
- However, you may build it against 5.11 even 5.9.
- gRPC & protobuf
- It's now required if you want to use the `dev` version. There's no such dependencies in the `version-v1` branch.
- OpenSSL
- The build may **not** fail if you don't have it since it's a **runtime dependency**.
## Build instructions
```bash
#/bin/bash
# Recursively clone the project since we have a submodule.
git clone --recursive https://github.com/lhy0403/Qv2ray
cd Qv2ray
./tools/gen_grpc.sh
mkdir build && cd build
qmake ..
make -j4
```

1
Build.Counter Normal file
View File

@ -0,0 +1 @@
302

View File

@ -4,18 +4,21 @@
#
#-------------------------------------------------
QT += core gui widgets network
QT += core gui widgets network charts
TARGET = Qv2ray
TEMPLATE = app
DEFINES += QT_DEPRECATED_WARNINGS
CONFIG += c++11 openssl openssl-linked lrelease embed_translations
CONFIG += c++11 openssl-linked lrelease embed_translations
win32: QMAKE_TARGET_DESCRIPTION = "Qv2ray, a cross-platform v2ray GUI client."
win32: QMAKE_TARGET_PRODUCT = "Qv2ray"
# Now read build number file.
_BUILD_NUMBER=$$cat(Build.Counter)
_BUILD_NUMBER = $$num_add($$_BUILD_NUMBER, 1)
write_file("Build.Counter", _BUILD_NUMBER)
VERSION = 1.999.999.999
DEFINES += QV_MAJOR_VERSION=\"\\\"$${VERSION}\\\"\"
VERSION = 1.99.0.$$_BUILD_NUMBER
DEFINES += QV2RAY_VERSION_STRING=\"\\\"v$${VERSION}\\\"\"
SOURCES += \
src/main.cpp \
@ -25,15 +28,18 @@ SOURCES += \
src/QvCoreConfigOperations_Verification.cpp \
src/QvCoreInteractions.cpp \
src/QvUtils.cpp \
src/ui/NetSpeedBar/QvNetSpeedBar.cpp \
src/ui/w_InboundEditor.cpp \
src/ui/w_OutboundEditor.cpp \
src/ui/w_RoutesEditor.cpp \
src/ui/w_SubscriptionEditor.cpp \
src/utils/QObjectMessageProxy.cpp \
src/utils/QPingModel.cpp \
src/utils/QvHTTPRequestHelper.cpp \
src/utils/QvRunguard.cpp \
src/utils/QJsonModel.cpp \
src/ui/w_JsonEditor.cpp \
src/ui/w_RouteEditor.cpp \
src/ui/w_SubscribeEditor.cpp \
src/ui/w_MainWindow.cpp \
src/ui/w_ConnectionEditWindow.cpp \
src/ui/w_ImportConfig.cpp \
src/ui/w_PrefrencesWindow.cpp \
libs/gen/v2ray_api_commands.pb.cc \
@ -53,83 +59,138 @@ HEADERS += \
src/QvCoreConfigOperations.h \
src/QvCoreInteractions.h \
src/QvUtils.h \
src/ui/w_InboundEditor.h \
src/ui/w_OutboundEditor.h \
src/ui/w_RoutesEditor.h \
src/ui/w_SubscriptionEditor.h \
src/utils/QJsonObjectInsertMacros.h \
src/utils/QObjectMessageProxy.h \
src/utils/QPingModel.h \
src/utils/QvHTTPRequestHelper.h \
src/utils/QvNetSpeedPlugin.h \
src/utils/QvRunguard.h \
src/utils/QvTinyLog.h \
src/utils/QJsonModel.h \
src/ui/w_JsonEditor.h \
src/ui/w_ConnectionEditWindow.h \
src/ui/w_ImportConfig.h \
src/ui/w_MainWindow.h \
src/ui/w_PrefrencesWindow.h \
src/ui/w_RouteEditor.h \
src/ui/w_SubscribeEditor.h \
libs/gen/v2ray_api_commands.pb.h \
libs/gen/v2ray_api_commands.grpc.pb.h
FORMS += \
src/ui/w_ConnectionEditWindow.ui \
src/ui/w_ImportConfig.ui \
src/ui/w_InboundEditor.ui \
src/ui/w_JsonEditor.ui \
src/ui/w_MainWindow.ui \
src/ui/w_OutboundEditor.ui \
src/ui/w_PrefrencesWindow.ui \
src/ui/w_RouteEditor.ui \
src/ui/w_SubscribeEditor.ui
src/ui/w_RoutesEditor.ui \
src/ui/w_SubscriptionEditor.ui
RESOURCES += \
resources.qrc
# ------------------------------------------ Begin to detect language files.
QM_FILES_RESOURCE_PREFIX = "translations"
for(var, $$list($$files("*.ts", true))) {
LOCALE_FILENAME = $$basename(var)
!equals(LOCALE_FILENAME, "en-US.ts") {
# ONLY USED IN LRELEASE CONTEXT - en-US is not EXTRA...
EXTRA_TRANSLATIONS += translations/$$LOCALE_FILENAME
}
}
TRANSLATIONS += \
translations/en-US.ts
message("CORE Translations:" $$TRANSLATIONS)
message("EXTRA Translations:" $$EXTRA_TRANSLATIONS)
# Fine......
message(" ")
message("Qv2ray Version: $${VERSION}")
message("|-------------------------------------------------|")
message("| Qv2ray, A Cross Platform v2ray Qt GUI Client. |")
message("| Licenced under GPLv3 |")
message("| |")
message("| You may only use this program to the extent |")
message("| permitted by local law. |")
message("| |")
message("| See: https://www.gnu.org/licenses/gpl-3.0.html |")
message("|-------------------------------------------------|")
message(" ")
RC_ICONS += ./icons/Qv2ray.ico
ICON = ./icons/Qv2ray.icns
message(Files will be generated to: $$OUT_PWD)
# ------------------------------------------ Begin checking gRPC and protobuf headers.
!exists(libs/gen/v2ray_api_commands.grpc.pb.h) || !exists(libs/gen/v2ray_api_commands.grpc.pb.cc) || !exists(libs/gen/v2ray_api_commands.pb.h) || !exists(libs/gen/v2ray_api_commands.pb.cc) {
message(" ")
message("-----------------------------------------------")
message("Cannot continue: ")
message(" --> Qv2ray is not properly configured yet: ")
message(" gRPC and protobuf headers for v2ray API is missing.")
message(" --> Please run gen_grpc.sh gen_grpc.bat or deps_macOS.sh located in tools/")
message(" --> Or consider reading https://github.com/lhy0403/Qv2ray/blob/master/BUILDING.md")
message("-----------------------------------------------")
message(" ")
warning("IF YOU THINK IT'S A MISTAKE, PLEASE OPEN AN ISSUE")
error("! NOW THE BUILD WILL ABORT !")
message(" ")
}
# For gRPC and protobuf in linux and macOS
unix: LIBS += -lgrpc++ -lprotobuf
unix: LIBS += -L/usr/local/lib
# ------------------------------------------ Begin to detect language files.
message("Looking for language support.")
QM_FILES_RESOURCE_PREFIX = "translations"
for(var, $$list($$files("translations/*.ts", true))) {
LOCALE_FILENAME = $$basename(var)
message(" --> Found:" $$LOCALE_FILENAME)
!equals(LOCALE_FILENAME, "en-US.ts") {
# ONLY USED IN LRELEASE CONTEXTen-US is not EXTRA...
EXTRA_TRANSLATIONS += translations/$$LOCALE_FILENAME
}
}
message("Qv2ray will build with" $${replace(EXTRA_TRANSLATIONS, "translations/", "")})
TRANSLATIONS += translations/en-US.ts
QMAKE_CXXFLAGS += "-Wno-missing-field-initializers" "-Wno-unused-parameter"
QMAKE_CXXFLAGS += "-Wno-missing-field-initializers" "-Wno-unused-parameter" "-Wno-unused-variable"
win32 {
message("Configuring for win32 environment")
message(" --> Setting up target descriptions")
QMAKE_TARGET_DESCRIPTION = "Qv2ray, a cross-platform v2ray GUI client."
QMAKE_TARGET_PRODUCT = "Qv2ray"
message(" --> Adding Taskbar Toolbox CPP files.")
SOURCES += src/ui/NetSpeedBar/QvNetSpeedBar_win.cpp
# A hack for protobuf header.
message(" --> Applying a hack for protobuf header")
DEFINES += _WIN32_WINNT=0x600
message(" --> Linking against gRPC and protobuf library.")
LIBS += -L$$PWD/libs/gRPC-win32/lib/ -llibgrpc++.dll -llibprotobuf.dll
INCLUDEPATH += $$PWD/libs/gRPC-win32/include
DEPENDPATH += $$PWD/libs/gRPC-win32/include
# Some files issue.
QMAKE_PRE_LINK += forfiles /s /p $${replace(PWD, /, \\)}\libs\ /m "*.dll" /c \"cmd.exe /c copy @file $${replace(OUT_PWD, /, \\)}\\debug\ & copy @file $${replace(OUT_PWD, /, \\)}\\release\\\"
CONFIG(release, debug|release) {
message(" --> Appending scripts for copying gRPC and protobuf dll to RELEASE directory.")
QMAKE_PRE_LINK += forfiles /s /p $${replace(PWD, /, \\)}\libs\ /m "*.dll" /c \"cmd.exe /c copy @file $${replace(OUT_PWD, /, \\)}\\release\\\"
}
CONFIG(debug, debug|release) {
message(" --> Appending scripts for copying gRPC and protobuf dll to DEBUG directory.")
QMAKE_PRE_LINK += forfiles /s /p $${replace(PWD, /, \\)}\libs\ /m "*.dll" /c \"cmd.exe /c copy @file $${replace(OUT_PWD, /, \\)}\\debug\\\"
}
PRE_TARGETDEPS += $$PWD/libs/gRPC-win32/lib/libgrpc++.dll.a $$PWD/libs/gRPC-win32/lib/libprotobuf.dll.a
}
# Installations
qnx: target.path = /tmp/$${TARGET}/bin
unix: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
desktop.files += ./icons/Qv2ray.desktop
desktop.path = /opt/$${TARGET}/share/applications/
icon.files += ./icons/Qv2ray.png
icon.path = /opt/$${TARGET}/share/icons/hicolor/256x256/apps/
unix {
# For Linux and macOS
message("Configuring for unix (macOS and linux) environment")
# For gRPC and protobuf in linux and macOS
message(" --> Linking against gRPC and protobuf library.")
LIBS += -L/usr/local/lib -lgrpc++ -lprotobuf
INSTALLS += desktop
INSTALLS += icon
message(" --> Adding Plasma Toolbox CPP files.")
SOURCES += src/ui/NetSpeedBar/QvNetSpeedBar_linux.cpp
message(" --> Generating desktop dependency.")
desktop.files += ./icons/Qv2ray.desktop
desktop.path = /usr/share/applications/
message(" --> Generating icons dependency.")
icon.files += ./icons/Qv2ray.png
icon.path = /usr/share/icons/hicolor/256x256/apps/
target.path = /usr/local/bin
INSTALLS += target desktop icon
}
message("Done configuring Qv2ray project. Build output will be at:" $$OUT_PWD)
message("Type `make` or `mingw32-make` to start building Qv2ray")

View File

@ -1,16 +1,22 @@
# README in Other Languages
- [English](./README_l10n/en-US.md)
---------------------
***注意Qv2ray 仅能用于 Qt/c++/linux/CI/自动化 等相关技术的学习和在法律允许范围内的使用,任何个人或集体不得使用 Qv2ray 进行任何违反相关法律法规的操作。***
*任何尝试下载或下载 Qv2ray 任意分支或发行版即代表您同意本项目作者不承担任何由于您违反以上准则所带来的任何法律责任。*
# Qv2ray
# Qv2ray - Make v2ray real cross-platform
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/a034dd186c36408c92ffb04449fb6996)](https://app.codacy.com/app/lhy0403/Qv2ray?utm_source=github.com&utm_medium=referral&utm_content=lhy0403/Qv2ray&utm_campaign=Badge_Grade_Dashboard) [![HitCount](http://hits.dwyl.io/lhy0403/Qv2ray.svg)](http://hits.dwyl.io/lhy0403/Qv2ray) ![GitHub Releases](https://img.shields.io/github/downloads/lhy0403/Qv2ray/latest/total?style=flat-square)
[![HitCount](http://hits.dwyl.io/lhy0403/Qv2ray.svg)](http://hits.dwyl.io/lhy0403/Qv2ray) ![GitHub Releases](https://img.shields.io/github/downloads/lhy0403/Qv2ray/latest/total?style=flat-square)
使用 Qt 框架的跨平台 v2ray 客户端. 支持 Windows, Linux, macOS
支持连接编辑,支持导入任意配置和 `vmess://` 分享链接
Special thanks to: [Hv2ray](https://github.com/aliyuchang33/Hv2ray) by [@aliyuchang33](https://github.com/aliyuchang33)
来自于 [Hv2ray](https://github.com/aliyuchang33/Hv2ray),原作者 [@aliyuchang33](https://github.com/aliyuchang33)
## 相关链接:
@ -19,6 +25,7 @@ Special thanks to: [Hv2ray](https://github.com/aliyuchang33/Hv2ray) by [@aliyuch
- Crowdin 翻译平台, Translations are welcome at here: **[Public Translation Platform](https://crwd.in/qv2ray)**
- ArchLinux - AUR: **[qv2ray](https://aur.archlinux.org/packages/qv2ray/)**
- **[qv2ray-dev-git](https://aur.archlinux.org/packages/qv2ray-dev-git)**: `dev` 分支的开发版本,由 **[@axionl](https://github.com/axionl)** 维护
- **注意dev 分支包含不稳定的功能更新和不稳定的bug修复不建议将 dev 分支作为日常使用**
### 首次使用请查看 ➡ [用户手册](https://lhy0403.github.io/Qv2ray)
@ -30,12 +37,12 @@ Special thanks to: [Hv2ray](https://github.com/aliyuchang33/Hv2ray) by [@aliyuch
- 访问链接: [https://jenkins.lhy0403.top/](https://jenkins.lhy0403.top/)
| | [Master](https://github.com/lhy0403/Qv2ray/tree/master) | [Developement](https://github.com/lhy0403/Qv2ray/tree/dev) | [Version v1](https://github.com/lhy0403/Qv2ray/tree/version-v1) | [Version v2](https://github.com/lhy0403/Qv2ray/tree/version-v2) | [Pull Requests](https://github.com/lhy0403/Qv2ray/pulls) |
| | [Master](https://github.com/lhy0403/Qv2ray/tree/master) | [Developement](https://github.com/lhy0403/Qv2ray/tree/dev) | [Version v1](https://github.com/lhy0403/Qv2ray/tree/version-v1) | [Version v2](https://github.com/lhy0403/Qv2ray/tree/version-v2) | Testing |
| -------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
| Linux AppImage | [![Build Status](https://jenkins.lhy0403.top/job/Qv2ray-AppImage-Release/badge/icon)](https://jenkins.lhy0403.top/job/Qv2ray-AppImage-Release/) | [![Build Status](https://jenkins.lhy0403.top/job/Qv2ray-AppImage-Dev/badge/icon)](https://jenkins.lhy0403.top/job/Qv2ray-AppImage-Dev/) | [![Build Status](https://jenkins.lhy0403.top/job/Qv2ray-AppImage-Version1/badge/icon)](https://jenkins.lhy0403.top/job/Qv2ray-AppImage-Version1/) | [![Build Status](https://jenkins.lhy0403.top/job/Qv2ray-AppImage-Version2/badge/icon)](https://jenkins.lhy0403.top/job/Qv2ray-AppImage-Version2/) | [![Build Status](https://jenkins.lhy0403.top/job/Qv2ray-AppImage-PullRequest/badge/icon)](https://jenkins.lhy0403.top/job/Qv2ray-AppImage-PullRequest/) |
| Windows Zip | [![Build Status](https://jenkins.lhy0403.top/job/Qv2ray-Win32-Release/badge/icon)](https://jenkins.lhy0403.top/job/Qv2ray-Win32-Release/) | [![Build Status](https://jenkins.lhy0403.top/job/Qv2ray-Win32-Dev/badge/icon)](https://jenkins.lhy0403.top/job/Qv2ray-Win32-Dev/) | [![Build Status](https://jenkins.lhy0403.top/job/Qv2ray-Win32-Version1/badge/icon)](https://jenkins.lhy0403.top/job/Qv2ray-Win32-Version1/) | [![Build Status](https://jenkins.lhy0403.top/job/Qv2ray-Win32-Version2/badge/icon)](https://jenkins.lhy0403.top/job/Qv2ray-Win32-Version2/) | [![Build Status](https://jenkins.lhy0403.top/job/Qv2ray-Win32-PullRequest/badge/icon)](https://jenkins.lhy0403.top/job/Qv2ray-Win32-PullRequest/) |
| Linux AppImage | [![Build Status](https://jenkins.lhy0403.top/job/Qv2ray-AppImage-Release/badge/icon)](https://jenkins.lhy0403.top/job/Qv2ray-AppImage-Release/) | [![Build Status](https://jenkins.lhy0403.top/job/Qv2ray-AppImage-Dev/badge/icon)](https://jenkins.lhy0403.top/job/Qv2ray-AppImage-Dev/) | [![Build Status](https://jenkins.lhy0403.top/job/Qv2ray-AppImage-Version1/badge/icon)](https://jenkins.lhy0403.top/job/Qv2ray-AppImage-Version1/) | [![Build Status](https://jenkins.lhy0403.top/job/Qv2ray-AppImage-Version2/badge/icon)](https://jenkins.lhy0403.top/job/Qv2ray-AppImage-Version2/) | [![Build Status](https://jenkins.lhy0403.top/job/Qv2ray-AppImage-Testing/badge/icon)](https://jenkins.lhy0403.top/job/Qv2ray-AppImage-Testing/) |
| Windows Zip | [![Build Status](https://jenkins.lhy0403.top/job/Qv2ray-Win32-Release/badge/icon)](https://jenkins.lhy0403.top/job/Qv2ray-Win32-Release/) | [![Build Status](https://jenkins.lhy0403.top/job/Qv2ray-Win32-Dev/badge/icon)](https://jenkins.lhy0403.top/job/Qv2ray-Win32-Dev/) | [![Build Status](https://jenkins.lhy0403.top/job/Qv2ray-Win32-Version1/badge/icon)](https://jenkins.lhy0403.top/job/Qv2ray-Win32-Version1/) | [![Build Status](https://jenkins.lhy0403.top/job/Qv2ray-Win32-Version2/badge/icon)](https://jenkins.lhy0403.top/job/Qv2ray-Win32-Version2/) | [![Build Status](https://jenkins.lhy0403.top/job/Qv2ray-Win32-Testing/badge/icon)](https://jenkins.lhy0403.top/job/Qv2ray-Win32-Testing/) |
### Travis & Appveyor
### Travis & Appveyor 多平台构建状态
*Travis per machine badge provided by: [badge-matrix](https://github.com/exogen/badge-matrix)*
@ -47,82 +54,30 @@ Special thanks to: [Hv2ray](https://github.com/aliyuchang33/Hv2ray) by [@aliyuch
## 编译
## 鸣谢
### 依赖环境
- 框架依赖:`Qt >= 5.12` 5.9 可以编译成功但是不建议使用)
- 编译依赖:`gcc >= 8` 或 `MinGW` (Windows) 或 `clang` (macOS)
- 建议使用 QtCreator以获得最佳编译环境
### Linux & macOS
- 对于 macOS需要额外使用 HomeBrew 安装 Qt 并正确设定 $PATH 环境变量
- 或者使用 Qt 官方包和 XCode
- 对于 Linux请根据不同发行版安装对应的 Qt 开发包
- Arch Linux 用户也可从 **[qv2ray-dev-git](https://aur.archlinux.org/packages/qv2ray-dev-git)** 拉取 `dev` 分支并构建
- 手动构建方法:
```bash
git clone --recursive https://github.com/lhy0403/Qv2ray && cd Qv2ray
# 按需要签出开发分支
# git checkout dev
mkdir build && cd build
qmake ../
make
```
### Windows
- 建议使用 Qt Creator
```batch
REM 首先把 %QTROOT%/bin 和 MinGW/bin 文件夹添加到 PATH 环境变量
REM SET PATH=%PATH%;C:\Qt\5.13.0\mingw73_32\bin\;C:\Qt\Tools\mingw730_32\bin\
SET PATH=%PATH%;Qt安装目录\Qt版本号\编译器类型\bin;Qt安装目录\Tools\编译器类型\bin
git clone --recursive https://github.com/lhy0403/Qv2ray && cd Qv2ray
REM git checkout dev (可选项,用于测试 dev 分支)
mkdir build && cd build
qmake ../
mingw32-make.exe
```
| 姓名 (@Github帐号) | 贡献内容 |
| ------------------------------------------------------------ | ------------------------ |
| Leroy.H.Y [@lhy0403](https://github.com/lhy0403) | Qv2ray 当前维护人员之一 |
| Hork [@aliyuchang33](https://github.com/aliyuchang33) | Hv2ray 灵感与设计 |
| SOneWinstone [@SoneWinstone](https://github.com/SoneWinstone) | 使用 Qt 的 HTTP 交互逻辑 |
| ArielAxionL [@axionl](https://github.com/axionl) | Qv2ray 部分 UI |
| TheBadGateway [@thebadgateway](https://github.com/thebadgateway) | 俄罗斯文翻译 |
## Contributors
| Name (@GithubAccount) | Contributions |
| ------------------------------------------------------------ | ----------------------------------------------------- |
| Leroy.H.Y [@lhy0403](https://github.com/lhy0403) | Qv2ray Current Maintainer |
| Hork [@aliyuchang33](https://github.com/aliyuchang33) | Hv2ray Initial Idea and Designs, gRPC implementations |
| SOneWinstone [@SoneWinstone](https://github.com/SoneWinstone) | HTTP Request Helper |
| ArielAxionL [@axionl](https://github.com/axionl) | Qv2ray Art Work |
| TheBadGateway [@thebadgateway](https://github.com/thebadgateway) | Russian Translation |
## 许可证
Qv2ray 使用 [![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
## License
Qv2ray is licensed under [![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
[X2Struct](https://github.com/xyz347/x2struct) is licensed ![License: GPL v3](https://img.shields.io/badge/License-MIT-blue.svg)
Submodule [X2Struct](https://github.com/xyz347/x2struct) 使用 ![License: GPL v3](https://img.shields.io/badge/License-MIT-blue.svg)
[QJsonModel](https://github.com/dridk/QJsonModel) is licensed under![License: GPL v3](https://img.shields.io/badge/License-MIT-blue.svg) (Copyright (c) 2011 SCHUTZ Sacha)
```
Qv2ray, A Qt frontend for v2ray. Written in c++
Copyright (C) 2019 Leroy.H.Y (@lhy0403) ---> Current Maintainer
Copyright (C) 2019 Hork (@aliyuchang33) ---> Hv2ray Initial Idea and Designs, Current Maintainer
Copyright (C) 2019 SOneWinstone (@SoneWinstone) ---> Hv2ray/Qv2ray HTTP Request Helper
Copyright (C) 2019 ArielAxionL (@axionl) ---> Qv2ray ArtWork
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or

84
README_l10n/en-US.md Normal file
View File

@ -0,0 +1,84 @@
***Note: Qv2ray can only be used for learning related technologies such as Qt/c++/linux/CI/automation and use within the scope permitted by law. Any individual or group may not use Qv2ray for any violation of relevant laws and regulations.***
*Any attempt to download or download any branch or distribution of Qv2ray constitutes your agreement that the author of the project will not be liable for any legal liability arising from your breach of the above guidelines.*
# Qv2ray
[![HitCount](http://hits.dwyl.io/lhy0403/Qv2ray.svg)](http://hits.dwyl.io/lhy0403/Qv2ray) ![GitHub Releases](https://img.shields.io/github/downloads/lhy0403/Qv2ray/latest/total?style=flat-square)
The v2ray GUI client using Qt, supports Windows, Linux and macOS
Supports editing connection config, importing any file and `vmess://` shared link.
Special Thanks to [Hv2ray](https://github.com/aliyuchang33/Hv2ray) by [@aliyuchang33](https://github.com/aliyuchang33)
## Relevant Links
- **Brief Feature Introduction: https://lhy0403.github.io/Qv2ray**
- Latest **[Release Distribution](https://github.com/lhy0403/Qv2ray/releases/latest)**
- Crowdin Translation Platform: **[Public Translation Platform](https://crwd.in/qv2ray)**
- ArchLinux - AUR: **[qv2ray](https://aur.archlinux.org/packages/qv2ray/)**
- **[qv2ray-dev-git](https://aur.archlinux.org/packages/qv2ray-dev-git)**: Development version from branch`dev`, maintained by **[@axionl](https://github.com/axionl)**
- **Note: The development branch contains unstable feature update and (even more) unstable bug fixes, it's not recommended to use a development version in production.**
### First time using Qv2ray please refer to ➡ [User Manual](https://lhy0403.github.io/Qv2ray)
## Qv2ray Project Status
### Jenkins CI
- Server Website: [https://jenkins.lhy0403.top/](https://jenkins.lhy0403.top/)
| | [Master](https://github.com/lhy0403/Qv2ray/tree/master) | [Developement](https://github.com/lhy0403/Qv2ray/tree/dev) | [Version v1](https://github.com/lhy0403/Qv2ray/tree/version-v1) | [Version v2](https://github.com/lhy0403/Qv2ray/tree/version-v2) | Testing |
| -------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
| Linux AppImage | [![Build Status](https://jenkins.lhy0403.top/job/Qv2ray-AppImage-Release/badge/icon)](https://jenkins.lhy0403.top/job/Qv2ray-AppImage-Release/) | [![Build Status](https://jenkins.lhy0403.top/job/Qv2ray-AppImage-Dev/badge/icon)](https://jenkins.lhy0403.top/job/Qv2ray-AppImage-Dev/) | [![Build Status](https://jenkins.lhy0403.top/job/Qv2ray-AppImage-Version1/badge/icon)](https://jenkins.lhy0403.top/job/Qv2ray-AppImage-Version1/) | [![Build Status](https://jenkins.lhy0403.top/job/Qv2ray-AppImage-Version2/badge/icon)](https://jenkins.lhy0403.top/job/Qv2ray-AppImage-Version2/) | [![Build Status](https://jenkins.lhy0403.top/job/Qv2ray-AppImage-Testing/badge/icon)](https://jenkins.lhy0403.top/job/Qv2ray-AppImage-Testing/) |
| Windows Zip | [![Build Status](https://jenkins.lhy0403.top/job/Qv2ray-Win32-Release/badge/icon)](https://jenkins.lhy0403.top/job/Qv2ray-Win32-Release/) | [![Build Status](https://jenkins.lhy0403.top/job/Qv2ray-Win32-Dev/badge/icon)](https://jenkins.lhy0403.top/job/Qv2ray-Win32-Dev/) | [![Build Status](https://jenkins.lhy0403.top/job/Qv2ray-Win32-Version1/badge/icon)](https://jenkins.lhy0403.top/job/Qv2ray-Win32-Version1/) | [![Build Status](https://jenkins.lhy0403.top/job/Qv2ray-Win32-Version2/badge/icon)](https://jenkins.lhy0403.top/job/Qv2ray-Win32-Version2/) | [![Build Status](https://jenkins.lhy0403.top/job/Qv2ray-Win32-Testing/badge/icon)](https://jenkins.lhy0403.top/job/Qv2ray-Win32-Testing/) |
### Travis & Appveyor Cross Platform Build Status
*Travis per machine badge provided by: [badge-matrix](https://github.com/exogen/badge-matrix)*
| OS | [master](https://github.com/lhy0403/Qv2ray/tree/master) | [dev](https://github.com/lhy0403/Qv2ray/tree/dev) | [version-v1](https://github.com/lhy0403/Qv2ray/tree/version-v1) | [version-v2](https://github.com/lhy0403/Qv2ray/tree/version-v2) |
| --------------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
| [Linux](https://travis-ci.com/lhy0403/Qv2ray) | ![Build Status Linux in master](http://badges.herokuapp.com/travis.com/lhy0403/Qv2ray?style=flat-square&env=BADGE=linux&label=Linux-master&branch=master) | ![Build Status Linux in dev](http://badges.herokuapp.com/travis.com/lhy0403/Qv2ray?style=flat-square&env=BADGE=linux&label=Linux-dev&branch=dev) | ![Build Status Linux in v1.0.0](http://badges.herokuapp.com/travis.com/lhy0403/Qv2ray?style=flat-square&env=BADGE=linux&label=Linux-v1&branch=version-v1) | ![Build Status Linux in v2.0.0](http://badges.herokuapp.com/travis.com/lhy0403/Qv2ray?style=flat-square&env=BADGE=linux&label=Linux-v2&branch=version-v2) |
| [macOS](https://travis-ci.com/lhy0403/Qv2ray) | ![Build Status macOS in master](http://badges.herokuapp.com/travis.com/lhy0403/Qv2ray?style=flat-square&env=BADGE=osx&label=macOS-master&branch=master) | ![Build Status macOS in dev](http://badges.herokuapp.com/travis.com/lhy0403/Qv2ray?style=flat-square&env=BADGE=osx&label=macOS-dev&branch=dev) | ![Build Status macOS in v1](http://badges.herokuapp.com/travis.com/lhy0403/Qv2ray?style=flat-square&env=BADGE=osx&label=macOS-v1&branch=version-v1) | ![Build Status macOS in v2](http://badges.herokuapp.com/travis.com/lhy0403/Qv2ray?style=flat-square&env=BADGE=osx&label=macOS-v2&branch=version-v2) |
| [Windows](https://ci.appveyor.com/project/lhy0403/qv2ray) | [![Build status](https://ci.appveyor.com/api/projects/status/i1l524ws0hiitpm4/branch/master?svg=true)](https://ci.appveyor.com/project/lhy0403/qv2ray/branch/master) | [![Build status](https://ci.appveyor.com/api/projects/status/i1l524ws0hiitpm4/branch/dev?svg=true)](https://ci.appveyor.com/project/lhy0403/qv2ray/branch/dev) | [![Build status](https://ci.appveyor.com/api/projects/status/i1l524ws0hiitpm4/branch/version-v1?svg=true)](https://ci.appveyor.com/project/lhy0403/qv2ray/branch/version-v1) | [![Build status](https://ci.appveyor.com/api/projects/status/i1l524ws0hiitpm4/branch/version-v2?svg=true)](https://ci.appveyor.com/project/lhy0403/qv2ray/branch/version-v2) |
## Special Thanks
| NickName (@GithubAccount) | Contributions |
| ------------------------------------------------------------ | ------------------------------ |
| Leroy.H.Y [@lhy0403](https://github.com/lhy0403) | Qv2ray Current Maintainer |
| Hork [@aliyuchang33](https://github.com/aliyuchang33) | Hv2ray Initial Idea and Design |
| SOneWinstone [@SoneWinstone](https://github.com/SoneWinstone) | Qt HTTP Interactive Logics |
| ArielAxionL [@axionl](https://github.com/axionl) | Qv2ray Artworks |
| TheBadGateway [@thebadgateway](https://github.com/thebadgateway) | Translation for Russian |
## Open Source License
Qv2ray is licensed under [![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
Submodule [X2Struct](https://github.com/xyz347/x2struct) is licensed under ![License: GPL v3](https://img.shields.io/badge/License-MIT-blue.svg)
```
Qv2ray, A Qt frontend for v2ray. Written in c++
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
```

View File

@ -1,9 +0,0 @@
# Qv2ray 安装方法 - ArchLinux 及衍生版
## 适用于 使用 AUR 的 ArchLinux 或 ArchLinux 衍生版
1. 从 AUR 安装 `qv2ray`
- *注意,用户需要在 `v2ray` 包的基础上 额外需要 `v2ray-domain-list-community``v2ray-geoip` 进行分流代理*
- *详见Issue: [#23-511384858](https://github.com/lhy0403/Qv2ray/issues/23#issuecomment-511384858)*
2. 打开 Qv2ray导入或添加现有连接即可

View File

@ -1,9 +0,0 @@
# Qv2ray 安装方法 - 一般 Linux
## 适用于包含 v2ray 软件包,但不包含 Qv2ray 的发行版
1. 请安装 `v2ray` 程序包
2. 从最新发布版本下载 Linux 的 AppImage
3. 打开 Qv2ray 的 AppImage
4. Linux 版 Qv2ray 默认使用 `/bin/v2ray``/etc/v2ray` 作为内核与 assets 路径,如果与你的位置不一样,请到首选项中修改
5. 导入或创建新配置即可

View File

@ -1,18 +0,0 @@
# Qv2ray 安装方法 - 任何系统
*此方法为通用操作方法,任何系统都可以使用这种方法安装并使用 Qv2ray*
1. 你需要从官方 [releases](https://github.com/v2ray/v2ray-core/releases/latest) 下载对应系统版本的核心文件,并解压缩
2. 根据系统类别下载 Qv2ray 发布版,解压缩或双击打开 AppImage
3. 打开首选项,设置 vcore 路径和 assets 路径到解压后的文件夹
- 此种情况下,首选项中的 core 路径和 assets 路径通常指向同一文件夹Qv2ray 包含自动补全功能)
4. 点击OK保存配置并开始添加连接你可以选择导入现有链接或者手动输入链接设置
5. 使用托盘图标中键,窗口按钮或右键菜单均可启动当前连接
# *附录*
## *1. Qv2ray 在不同操作系统中生成的配置文件夹位置*
- *Windows: `C:\Users\用户名\.qv2ray`*
- *macOS: `/Users/用户名/.qv2ray`*
- *Linux: `/home/用户名/.qv2ray`*

View File

@ -1,38 +1,8 @@
# Qv2ray 用户手册
# Qv2ray User Manual
- 基于 Qt 的跨平台 v2ray 图形客户端
- Please select a language before continue.
- #### 当前最新版本: [v1.3.7.1](./ReleaseNotes/1.3/v1.3.7.1.md)
请选择一个语言
--------
## 1. 功能简介
见 [简单的功能介绍](./Features.md)
## 2. 首次使用,安装与配置
请根据您的操作系统/发行版,选择对应的使用方法
1. [ArchLinux 及 Arch 衍生版 ](./FirstTime/Arch.md)
2. [其他 Linux 且发行版包含 v2ray 包](./FirstTime/General-Linux.md)
3. [Windows / macOS / 不包含 v2ray 程序包的 Linux 发行版 ](./FirstTime/Windows-macOS.md)
## 3. 更新历史
见 [更新历史记录](./History.md)
## 4. 常见问题 FAQ
见 [FAQ.md](./FAQ.md)
## 5. 共同翻译 Qv2ray
翻译平台使用 Crowdin[https://crowdin.com/project/qv2ray](https://crowdin.com/project/qv2ray)
# 开源协议 Open Source License
GPLv3
- [简体中文](./zh-CN/README.md)
- [English](./en-US/README.md)

32
docs/en-US/BUILDING.md Normal file
View File

@ -0,0 +1,32 @@
# Building Qv2ray
Qv2ray is a cross platform v2ray GUI, so you can run your build on all desktop platforms.
## Obtaining Source Code
-
## Dependencies
- Qt version >= 5
- Qt version 5.12 and 5.13 is recommended
- However, you may build it against 5.11 even 5.9.
- gRPC & protobuf
- It's now required if you want to use the `dev` version. There's no such dependencies in the `version-v1` branch.
- OpenSSL
- The build may **not** fail if you don't have it since it's a **runtime dependency**.
## Build instructions
```bash
#/bin/bash
# Recursively clone the project since we have a submodule.
git clone --recursive https://github.com/lhy0403/Qv2ray
cd Qv2ray
./tools/gen_grpc.sh
mkdir build && cd build
qmake ..
make -j4
```

14
docs/en-US/FAQ.md Normal file
View File

@ -0,0 +1,14 @@
# Frequently Asked Question
## v2ray Failed to start after enabling tProxy
Detail: `Segmentation Fault` occurd after enabling tProxy
It's caused by a limit in `SUID` feature on some Linux OSes. Detailed error analysis please see: [#59](https://github.com/lhy0403/Qv2ray/issues/59)
- Solution:`sudo sysctl fs.suid_dumpable=1`
- The solution will lost on reboot, please refer to [this blog](http://ssdxiao.github.io/linux/2017/03/20/Sysctl-not-applay-on-boot.html) if you want to keep it.
## Ubuntu gives an super ugly UI
- Solution: Append `--style fusion` to the command line arg solves this problem.

58
docs/en-US/Features.md Normal file
View File

@ -0,0 +1,58 @@
# Qv2ray Features
## 1. Linux Friendly
Qv2ray **fully** supports Linux
## 2. Connection Importing
You can import files into Qv2ray from these sources:
- Existing file.
- `vmess://` Shared Link
- Manually.
## 3. Connection Editing
- It seems to be the most powerful v2ray connection editor ever.
- Editor for routing is under development. See [dev](https://github.com/lhy0403/Qv2ray/tree/dev) branch.
- Qv2ray supports editing those outbounds since v1.3.5
- VMess
- ShadowSocks
- Socks
## 4. Automatically Connect
- Connect to a server automatically after start, without showing the Main window.
- Added since [v1.2.0](./ReleaseNotes/1.2/v1.2.0.0.md)
**In order to set an automatic connection, follow these steps:**
1. Open Prefrences Window
2. Select a connection under `Auto Connect To` Section.
3. Click OK to save the config.
## 5. Automatic Update
- Update using Github Release to get the latest feature updates and bug fixes.
## 6. Multi Language Support.
- Qv2ray now supports these languages, since v1.3.7
- Chinese
- English
- Russian (Thanks to @thebadgateway)
You may also help translating Qv2ray at: https://crowdin.com/project/qv2ray
## 7. Display v2ray output in realtime
- For easy understanding what's happening.
## 8. tProxy Support. (Only for Linux)
- Enable tProxy support on Linux using `setcap`

35
docs/en-US/History.md Normal file
View File

@ -0,0 +1,35 @@
# Qv2ray Historical Versions
Latest Release [Github](https://github.com/lhy0403/Qv2ray/releases/latest)
## 1.3.x
- [v1.3.8](./ReleaseNotes/1.3/v1.3.8.0.md) Some bug fixes
- [v1.3.7.1](./ReleaseNotes/1.3/v1.3.7.1.md) Added tProxy support, fixed bugs when importing `vmess://`, this version contains 6 feature updates and 11 bug fixes.
- [v1.3.7](./ReleaseNotes/1.3/v1.3.7.0.md) Not Released
- [v1.3.6.1](./ReleaseNotes/1.3/v1.3.6.1.md) UI Icons update, translations update and a list order fix.
- [v1.3.6.0](./ReleaseNotes/1.3/v1.3.6.0.md) Not Released
- [v1.3.5-3](./ReleaseNotes/1.3/v1.3.5.3.md) Update targeting OpenSSL 1.1.1b-c-d
- [v1.3.5-2](./ReleaseNotes/1.3/v1.3.5.2.md) Fixed a list order issue and connectivity issue in v1.3.5
- [v1.3.5-0](./ReleaseNotes/1.3/v1.3.5.0.md) This version does not work and only roll back method is provided
- [v1.3.3](./ReleaseNotes/1.3/v1.3.3.0.md) This version fixed error in Auto Update module in Windows
- [v1.3.2](./ReleaseNotes/1.3/v1.3.2.0.md) This version contains 7 feature updates and 3 bug fixes.
- [v1.3.0](./ReleaseNotes/1.3/v1.3.0.0.md) Added support for ShadowSocks
## 1.2.x
- [v1.2.0](./ReleaseNotes/1.2/v1.2.0.0.md) This version contains 7 feature updates, 2 bug fixes and 3 minor fixes.
## 1.1.x
- v1.1.x Is canceled due to an internal error.
## 1.0.x
- [v1.0.1](./ReleaseNotes/1.0/v1.0.1.0.md) Fixed a spelling mistake in RC
- [v1.0.0](./ReleaseNotes/1.0/v1.0.0.0.md) Qv2ray v1 Release
## 0.9.x
- [v0.9.9 (BETA)](./ReleaseNotes/1.0/v0.9.9.0.md) - First Public Beta

45
docs/en-US/README.md Normal file
View File

@ -0,0 +1,45 @@
# Qv2ray User Manual
- A cross platform v2ray GUI written in QT
- #### Current Latest Release Version: [v1.3.8](./ReleaseNotes/1.3/v1.3.8.0.md)
--------
## 1. Features
Please see [Brief Feature Introduction](./Features.md)
## 2. Initial Setup and Usage
There are several steps:
1. Download Qv2ray form [Release](https://github.com/lhy0403/Qv2ray/releases/latest) ([AUR](https://aur.archlinux.org/packages/qv2ray/) is available for Arch Linux Users)
2. Download [v2ray kernel](https://github.com/v2ray/v2ray-core/releases/latest) from `v2ray-core` repo.
3. Open Qv2ray and a message box will tell you `Failed to find v2ray kernal`
- Click OK, and extract the kernel to the folder pop up just now.
- The folder contains a file named: `Put your v2ray.exe here.txt` for reference.
4. Close the folder after extracting and you may start importing configs.
```
Notes: Directories that Qv2ray will search for v2ray-kernel
- Windows: C:\Users\USERNAME\.qv2ray\vcore
- macOS: /Users/USERNAME/.qv2ray/vcore
- Linux: ~/.qv2ray/vcore
```
## 3. Update History
See [Update History](./History.md)
## 4. FAQ
Please refer to: [FAQ.md](./FAQ.md)
## 5. Contribute
1. Help translating Qv2ray to more languages.
- Crowdin [https://crowdin.com/project/qv2ray](https://crowdin.com/project/qv2ray)
2. New feature request.
- You can use Github Issues

View File

@ -0,0 +1,16 @@
# Version 1.3.8 Contains Following Fixes
Github Release: [v1.3.8.0](https://github.com/lhy0403/Qv2ray/releases/tag/v1.3.8.0)
## These changes are made in v1.3.8
- Add: Automatically delete the corresponding file when deleting the configuration
- Add: Right click menu to check if there is no option to configure
- Add: The external editor is opened by default when editing the v1 version does not support editing complex connections, and the configuration is automatically reloaded.
- Fixed: Translation issue, changed from MacOS to macOS
- Fixed: Fixed crash in listener port if it was an empty string in the preferences
- Fix: When importing some `vmess://`, there is a bug that the TCP header is set incorrectly and v2ray is not recognised.
- Fixed: Pop-ups causing crashes when no language files are detected
- Fix: Linux platform adds its own Open SSL library support
- Fixed: disable function button by default when no connection configuration is available

32
docs/zh-CN/BUILDING.md Normal file
View File

@ -0,0 +1,32 @@
# Building Qv2ray
Qv2ray is a cross platform v2ray GUI, so you can run your build on all desktop platforms.
## Obtaining Source Code
-
## Dependencies
- Qt version >= 5
- Qt version 5.12 and 5.13 is recommended
- However, you may build it against 5.11 even 5.9.
- gRPC & protobuf
- It's now required if you want to use the `dev` version. There's no such dependencies in the `version-v1` branch.
- OpenSSL
- The build may **not** fail if you don't have it since it's a **runtime dependency**.
## Build instructions
```bash
#/bin/bash
# Recursively clone the project since we have a submodule.
git clone --recursive https://github.com/lhy0403/Qv2ray
cd Qv2ray
./tools/gen_grpc.sh
mkdir build && cd build
qmake ..
make -j4
```

View File

@ -1,8 +1,11 @@
# Qv2ray 功能简介
Qv2ray 是一款跨平台,真正支持 Linux 桌面环境的 v2ray 客户端
## 1. 连接导入
## 1. Linux 友好
Qv2ray 支持 Linux 桌面环境
## 2. 连接导入
Qv2ray 支持以下位置导入配置文件
@ -10,43 +13,44 @@ Qv2ray 支持以下位置导入配置文件
- VMess 连接字符串
- 手动添加
## 2. 连接编辑
## 3. 连接编辑
- 大概会成为是史上最强大的 v2ray 配置文件编辑器了吧
- 路由编辑功能正在开发中
- 大概会成为全平台最强大的 v2ray 配置文件编辑器了吧
- 路由编辑功能正在开发中 见 [dev 分支](https://github.com/lhy0403/Qv2ray/tree/dev) 或 [开发版本 AUR](https://aur.archlinux.org/packages/qv2ray-dev-git)
- 截至到 v1.3.5.3 已经支持以下出站协议的编辑
- VMess
- ShadowSocks
- Socks
## 3. 自动连接
## 4. 自动连接
- 在启动时不显示主窗口,直接连接到设置的服务器
- 该功能自 [v1.2.0](./ReleaseNotes/1.2/ReleaseNote-v1.2.md) 版本添加
- 该功能自 [v1.2.0](./ReleaseNotes/1.2/v1.2.0.0.md) 版本添加
### 如要将某个连接设为自动连接,请打开首选项
1. 从 "自动连接到" 下拉菜单中选择需要自动建立连接的配置
2. 点击确定,保存设置即可
## 4. 自动更新
## 5. 自动更新
- 使用 Github Release 的自动更新机制,保证软件版本为最新,减少 Bug 数量,及时尝试新功能
## 5. 多语言支持
## 6. 多语言支持
- 截止到 v1.3.5.2Qv2ray 现已支持一下两种语言
- 截止到 v1.3.7.1Qv2ray 现已支持一下两种语言
- 英文
- 中文
- 俄罗斯语 (感谢 @thebadgateway
(还希望大家共同翻译啊 https://crowdin.com/project/qv2ray
## 6. 实时显示 v2ray 的输出
## 7. 实时显示 v2ray 的输出
- 查看 bug 更方便
## 7. 自动 tProxy 支持 Linux
## 8. 自动 tProxy 支持 Linux
- 一键对 v2ray 主程序启用透明代理功能 (`setcap`)

View File

@ -4,6 +4,7 @@
## 1.3.x
- [v1.3.8](./ReleaseNotes/1.3/v1.3.8.0.md) 部分 bug 修复
- [v1.3.7.1](./ReleaseNotes/1.3/v1.3.7.1.md)增加 tProxy 支持,修复 vmess:// 导入等多个问题,此版本包含 6 个功能更新和 11 个 bug 修复
- [v1.3.7](./ReleaseNotes/1.3/v1.3.7.0.md) 未发布
- [v1.3.6.1](./ReleaseNotes/1.3/v1.3.6.1.md) UI 图标更新,翻译更新和一个列表顺序修复

45
docs/zh-CN/README.md Normal file
View File

@ -0,0 +1,45 @@
# Qv2ray 用户手册
- 基于 Qt 的跨平台 v2ray 图形客户端
- #### 当前最新版本: [v1.3.8.0](./ReleaseNotes/1.3/v1.3.8.0.md)
--------
## 1. 功能简介
见 [简单的功能介绍](./Features.md)
## 2. 首次使用,安装与配置
使用 Qv2ray 需要以下几个步骤:
1. 在 [Release](https://github.com/lhy0403/Qv2ray/releases/latest) 下载 Qv2ray (ArchLinux 用户可以使用 `qv2ray` 的 [AUR](https://aur.archlinux.org/packages/qv2ray/))
2. 在 `v2ray` 官方下载 [v2ray 内核](https://github.com/v2ray/v2ray-core/releases/latest)
3. 打开 Qv2ray, 首次运行会提示无法找到 v2ray 内核
- 点击确定,将刚刚下载的 v2ray内核 解压到自动打开的文件夹内
- 该文件夹包含一个 `Put your v2ray.exe here.txt` 以供识别
4. 解压完成后,关闭弹出的文件夹,即可开始导入或新建连接配置
```
注: Qv2ray 在不同系统中查找 v2ray 内核的文件路径 (即找不到内核时自动弹出的文件夹路径)
- Windows: C:\Users\用户名\.qv2ray\vcore
- macOS: /Users/用户名/.qv2ray/vcore
- Linux: ~/.qv2ray/vcore
```
## 3. 更新历史
见 [更新历史记录](./History.md)
## 4. 常见问题 FAQ
见 [FAQ.md](./FAQ.md)
## 5. 作出贡献
1. 帮助翻译 Qv2ray 到多个语言
- 翻译平台使用 Crowdin [https://crowdin.com/project/qv2ray](https://crowdin.com/project/qv2ray)
2. 提出新功能建议
- 使用 Github Issues

View File

@ -0,0 +1,22 @@
# [Beta Release] v0.9.9b
Github Release: [v0.9.9b](https://github.com/lhy0403/Qv2ray/releases/tag/v0.9.9b)
- 此版本是 v1.0.0 之前的最后一个测试版修复了许多尤其是连接编辑页面的Bug
- July 09 UPDATE: 这个版本包含部分 Bug ,见 [#21](https://github.com/lhy0403/Qv2ray/issues/21)
- 使用方法:
1. 根据对应系统下载程序包
2. 双击打开 Qv2ray 程序
3. 程序运行后,会在当前目录 (Windows),家目录下 .qv2ray (Linux) 或包内 Contents/Resources (macOS) 生成程序配置文件和一个 generated 文件夹
4. 点击首选项 或 Preference设置 v2ray core 路径 和 v2ray 资源文件夹路径 部分Linux发行版分别使用 /bin/v2ray 和 /etc/v2rayWindows 和 macOS 需要从官方 releases 下载并解压缩,此种情况 core 路径和资源文件夹路径指向同一位置
5. 点击OK保存配置并开始添加连接
6. 使用鼠标中键点击托盘图标或使用按钮即可启动 v2ray

View File

@ -0,0 +1,9 @@
# [v1.0.0 RC1] Qv2ray v1 发布候选版本 RC1
Github Release: [v1.0.0](https://github.com/lhy0403/Qv2ray/releases/tag/v1.0.0)
- 此版本包含了 [#21](https://github.com/lhy0403/Qv2ray/issues/21) 提出的部分修复
- 此版本修改了 macOS 下的配置目录存储路径,避免因版本升级造成数据丢失
** 使用方法见 https://github.com/lhy0403/Qv2ray/releases/tag/v0.9.9b **

View File

@ -0,0 +1,11 @@
# v1.0.1 [Fix] 修复 RC 版本中的拼写错误
Github Release: [v1.0.1](https://github.com/lhy0403/Qv2ray/releases/tag/v1.0.1)
本次更新日志
- 修复了生成配置文件时的一个拼写错误 `genrerated``generated`
(引入自提交 [`0e9b90f`#diff-c3f4a6d32c4ab34067ba5fa647341c6aR12](https://github.com/lhy0403/Qv2ray/commit/0e9b90fb116b790156314a21a6ef1abc8d60fa63#diff-c3f4a6d32c4ab34067ba5fa647341c6aR12))
此次发布不包含任何功能性更新,因此不提供编译后的二进制包。
如果你不关心这个拼写错误,请使用 https://github.com/lhy0403/Qv2ray/releases/tag/v1.0.0 中的版本

View File

@ -0,0 +1,37 @@
# Version 1.2.0 包含以下功能性更新和修复
Github Release: [v1.2.0](https://github.com/lhy0403/Qv2ray/releases/tag/v1.2.0)
## 功能更新
1. 增加使用 Github Release API 的自动检测更新功能
2. 增加了启动时的 GPLv3 版权显示
3. 对于 Windows 版本,默认字体使用了 微软雅黑 而不是 宋体
4. 增加 v2ray 资源目录的自动补全功能
5. 在首选项中添加了 "关于" 选项卡
6. 增加 启动时自动连接到某一配置文件 的功能
7. 实现不需要重启即可重加载语言文件
## 问题修复
1. 修复导入配置时,"放弃"按钮误识别为"确认"的错误
2. 修复输入光标位于日志内容中间时,日志内容错乱问题
## 功能改善
1. 改善修改配置后的重连机制
2. 改善选择 v2ray 传输方式时的界面
3. 增强日志自动滚动算法
------
# Windows 用户注意
### 此版本移动了配置文件路径
- 请遵循以下操作完成升级:
1. 退出旧版 Qv2ray ,并打开旧版 Qv2ray 所在文件夹
2. 移动 `qv2ray.d` 文件夹到 `C:\Users\你的用户名\`,并重命名为 `.qv2ray`
3. 打开新版 Qv2ray 后会自动读取配置,旧版 Qv2ray 文件夹即可删除
#### 新功能 "自动连接" 设置方法,请转到 [用户手册](../README.md#自动连接) 查看

View File

@ -0,0 +1,12 @@
# Version 1.3.0 包含以下功能性更新和修复
Github Release: [v1.3.0](https://github.com/lhy0403/Qv2ray/releases/tag/v1.3.0)
## 功能更新
1. 增加对于 ShadowSocks 的支持
2. 重新设计了连接编辑页面 UI
## 问题修复
1. 部分修复了在 Windows 下无论如何也点不开窗口的问题(需要进一步处理)

View File

@ -0,0 +1,19 @@
# Version 1.3.2 包含以下功能性更新和修复
Github Release: [v1.3.2](https://github.com/lhy0403/Qv2ray/releases/tag/v1.3.2)
## 功能更新
1. 增加 TinyLog 模块,提升了日志详细度,便于反馈问题
2. 配置文件增加 config_version 键,便于向前兼容文件格式
3. 增加对于 Socks 服务器的支持
4. 增加修改连接名的功能,如果要修改的配置文件是当前配置,将会断开并重新连接
5. 主界面连接控制按钮样式更新
6. 主界面连接列表支持右键菜单,可以启动或重命名配置
7. 托盘图标鼠标悬浮时的提示文本更新,增加版本号和当前连接
## 问题修复
1. 完全修复了在 Windows 下无论如何也点不开窗口的问题
2. 在 macOS 环境下使用了 fusion 作为默认主题
3. 修复了在首选项中改变当前语言后,主界面右上角状态文本显示为 “未连接” 的错误

View File

@ -0,0 +1,13 @@
# [v1.3.3] Windows 版自动更新功能修复
Github Release: [v1.3.3](https://github.com/lhy0403/Qv2ray/releases/tag/v1.3.3)
- 修复了之前版本的 Qv2ray for Windows 中由于缺少 OpenSSL 库导致的自动更新检查失败的 bug
- 此版本不包含功能性更新 只提供补丁下载
- Linux 版本不受影响,可以忽略
- 此次更新安装方法:
- 下载并解压 zip 包到 Qv2ray.exe 同文件夹,
- 重新启动 Qv2ray 主程序。Qv2ray 将自动开始检查更新。

View File

@ -0,0 +1,34 @@
# Qv2ray 1.3.5 错误更新回滚方法
Github Release: [v1.3.5](https://github.com/lhy0403/Qv2ray/releases/tag/v1.3.5)
## ROLLBACK METHOD IF YOU HAVE UPDATED TO THE v1.3.4 VERSION
# 如果你已经更新到了 1.3.5,请在使用旧版 Qv2ray 之前进行以下操作
## IF YOU HAVE ALREADY UPDATED TO 1.3.5, PLEASE DO THESE **BEFORE** OPENING OLD VERSION!
- Linux & macOS:
打开 `$HOME/.qv2ray/Qv2ray.conf`
OPEN `$HOME/.qv2ray/Qv2ray.conf`
- Windows:
打开 `C:\Users\你的用户名\.qv2ray\Qv2ray.conf`
OPEN `C:\Users\YOUR_USERNAME\.qv2ray\Qv2ray.conf`
# 把 `config_version` 后的 `2` 修改成 `"1"`
## CHANGE `config_version` FROM `2` TO `"1"`
修改之前 BEFORE CHANGE
![image](https://user-images.githubusercontent.com/18734999/64166267-555c7c80-ce79-11e9-9128-ad932a36c35d.png)
修改之后 AFTER CHANGE
![image](https://user-images.githubusercontent.com/18734999/64166308-63120200-ce79-11e9-9727-323ec3e5c123.png)
# 然后重新启动旧版本,并忽略此次更新
## THEN START THE OLD VERSION AND IGNORE THIS UPDATE IF POSSIBLE

View File

@ -0,0 +1,13 @@
# Version 1.3.5.2 包含以下功能性更新和修复
Github Release: [v1.3.5.2](https://github.com/lhy0403/Qv2ray/releases/tag/v1.3.5.2)
## 此版本修复了以下已知问题
- 手动添加连接配置后,主界面列表顺序与内部顺序不符,会导致错误的连接被选择。
- 修复了在启动 v2ray 时,`config.gen.json` 中 `inbounds` 一直为空数组 ([#36](https://github.com/lhy0403/Qv2ray/issues/36)) ([#35](https://github.com/lhy0403/Qv2ray/issues/35)) ([v1.3.5](https://github.com/lhy0403/Qv2ray/releases/tag/v1.3.5))
## 注意
**<u>更新到此版本以后,配置文件格式将被自动升级,届时旧版本 Qv2ray 将无法使用</u>**

View File

@ -0,0 +1,8 @@
# Version 1.3.5.3 包含以下功能性更新和修复
Github Release: [v1.3.5.3](https://github.com/lhy0403/Qv2ray/releases/tag/v1.3.5.3)
## 此版本修复了以下已知问题
- 部分 Linux 发布版在检测 OpenSSL 库时,出现版本号未能完全匹配而失败的问题,此版本纠正了这个错误并使用官方的函数实现 OpenSSL 检测

View File

@ -0,0 +1,5 @@
# Version 1.3.6.0 由于 macOS 出现问题没有发布
Github Release: NONE
后续版本: [v1.3.6.1](./v1.3.6.1.md)

View File

@ -0,0 +1,9 @@
# Version 1.3.6.1 包含以下功能性更新和修复
Github Release: [v1.3.6.1](https://github.com/lhy0403/Qv2ray/releases/tag/v1.3.6.1)
## 此版本包含以下更改
- 主界面图标更新,来自 [@ArielAxionL](https://github.com/axionl)
- 修复了主界面在选择连接时的错误顺序
- 此版本翻译更新为 Crowdin 服务,不再需要手动翻译

View File

@ -0,0 +1,34 @@
# Version 1.3.7 包含以下功能性更新和修复
Github Release: [v1.3.7](https://github.com/lhy0403/Qv2ray/releases/tag/v1.3.7)
## 此版本包含以下更改
*此版本共包含 4 个功能更新和 8 个 bug 修复*
- 建议所有用户更新到此版本
**功能更新**
1. 使用配置文件夹目录下的 `vcore` 子文件夹作为默认(且唯一的) `v2ray`/`v2ctl` 的搜索路径
2. 停止支持自定义的 `v2ray` 内核路径,现有 `v2ray` 主程序和 `v2ctl` 将被自动复制到新路径,无需进行手动迁移
- 注意:`assets` 路径保持不变,请不要删除原文件夹
3. 移除了首选项中 “使用 root 运行 v2ray” 的选项
4. 增加 Linux 系统的 `tProxy` 功能支持,使用 `pkexec``setcap`,避免了使用 `sudo` 权限过大的问题 [[Issue: #59]](https://github.com/lhy0403/Qv2ray/issues/59)
5. 移除了在每个连接文件中的 `_qv2ray.configSource` 标识符,导入或手动添加的连接将不再有此项内容
6. 增加了在检测不到 `v2ray` 内核时,自动打开搜索路径并提示下载文件的功能
**Bug 修复**
1. 修复了某些情况下 vmess:// 链接导入失败的bug [[Issue: #58]](https://github.com/lhy0403/Qv2ray/issues/58)
2. 修复了某些情况下,导入文件在连接时出现 Tag 缺失的情况
3. (部分修复了)某些情况下,由于 Qv2ray 配置文件版本不一致导致的崩溃问题
4. 修复了在只有一个连接配置时Qv2ray 启动后即使选择了一个连接,仍然会提示“未选择连接”的 bug [[Issue: #57]](https://github.com/lhy0403/Qv2ray/issues/57)
5. 修复了在显示部分未完全翻译的文字时出现单词间空格缺失的 bug
6. 修复了上个版本由于使用云翻译引起的托盘图标处出现两个冒号的问题
7. 修复了在主界面进行添加,重命名,删除连接等复杂列表操作后选择连接,出现 连接配置与选择的配置不一致的 bug
8. 修复了某些极端情况下,在一个连接进行重命名时更换当前连接(即双击其他配置)时出现的重命名失败的 bug

View File

@ -0,0 +1,34 @@
# Version 1.3.7.1 包含以下功能性更新和修复
Github Release: [v1.3.7.1](https://github.com/lhy0403/Qv2ray/releases/tag/v1.3.7.1)
## 此版本包含以下更改
*此版本共包含 6 个功能更新和 11 个 bug 修复*
- 建议所有用户更新到此版本
**功能更新**
1. 使用配置文件夹目录下的 `vcore` 子文件夹作为默认(且唯一的) `v2ray`/`v2ctl` 的搜索路径
2. 停止支持自定义的 `v2ray` 内核路径,现有 `v2ray` 主程序和 `v2ctl` 将被自动复制到新路径,无需进行手动迁移
- 注意:`assets` 路径保持不变,请不要删除原文件夹
3. 移除了首选项中 “使用 root 运行 v2ray” 的选项
4. 增加 Linux 系统的 `tProxy` 功能支持,使用 `pkexec``setcap`,避免了使用 `sudo` 权限过大的问题 [#59](https://github.com/lhy0403/Qv2ray/issues/59)
5. 移除了在每个连接文件中的 `_qv2ray.configSource` 标识符,导入或手动添加的连接将不再有此项内容
6. 增加了在检测不到 `v2ray` 内核时,自动打开路径的功能
**Bug 修复**
1. 修复了某些情况下 vmess:// 链接导入失败的bug [#58](https://github.com/lhy0403/Qv2ray/issues/58)
2. 修复了某些情况下,导入文件在连接时出现 Tag 缺失的情况
3. (部分修复了)某些情况下,由于 Qv2ray 配置文件版本不一致导致的崩溃问题
4. 修复了在只有一个连接配置时Qv2ray 启动后即使选择了一个连接,仍然会提示“未选择连接”的 bug [#57](https://github.com/lhy0403/Qv2ray/issues/57)
5. 修复了在显示部分未完全翻译的文字时出现单词间空格缺失的 bug
6. 修复了上个版本由于使用云翻译引起的托盘图标处出现两个冒号的问题
7. 修复了在主界面进行添加,重命名,删除连接等复杂列表操作后再选择连接,出现连接的配置与选择配置不一致的 bug
8. 修复了某些极端情况下,在一个连接进行重命名时更换当前连接(即双击其他配置)时出现的重命名失败的 bug
9. 修复了在导入包含特定头部伪装的 vmess:// 字符串时TCP Header 被错误设置导致 v2ray 无法运行的 bug
10. 修复了某些情况下,国内网站无法被正确分流到直连的 bug
11. 修复了在尝试编辑尚未支持的复杂连接配置时Qv2ray 闪退的 bug

View File

@ -0,0 +1,15 @@
# Version 1.3.8.0 包含以下功能性更新和修复
Github Release: [v1.3.8.0](https://github.com/lhy0403/Qv2ray/releases/tag/v1.3.8.0)
## 此版本包含以下更改
- 添加:删除配置时自动删除对应文件
- 添加:右键菜单点击时进行检测有无选择配置
- 添加:在编辑 v1 版本不支持编辑的复杂连接时默认打开外部编辑器,并自动重载配置
- 修复:翻译问题,从 MacOS 改为 macOS
- 修复:修复了首选项中监听端口如果是空字符串导致的崩溃问题
- 修复:导入部分 vmess:// 时,出现 TCP 头被乱设置导致 v2ray 无法识别的 bug
- 修复:在检测不到可用语言文件时弹出窗口导致崩溃的 bug
- 修复Linux 平台添加自带 OpenSSL 库支持
- 修复:在没有连接配置的时候默认禁用功能按钮

View File

@ -0,0 +1,38 @@
# Version 2.0
Github Release: [TODO]()
Version 2.0 将会包含以下功能更新
### 复杂连接编辑
- [ ] 路由添加
- [ ] 路由编辑
- [ ] 入站编辑
- [ ] 增加更多出站编辑选项
- [ ] 自动启动对应编辑器(简单出站编辑 / 复杂路由编辑)
- [ ] 自动生成 iptables 的 tProxy 命令(应该挺难的)
- [ ] JSON 编辑器
### 下载模块
- [ ] 自动下载 v2ray core
- [ ] 自动下载更新
- [ ] 自动安装更新(?)
### v2ray API
- [ ] 流量统计
### 订阅功能
- [ ] 订阅添加
- [ ] 订阅编辑,删除
### 分享功能
- [ ] 一键生成 vmess://
- [ ] 生成二维码

View File

@ -3,6 +3,6 @@ Type=Application
Keywords=Internet;VPN;Proxy;v2ray;Qt;
Categories=Network;Qt;
Icon=Qv2ray
Exec=/opt/Qv2ray/bin/Qv2ray
Exec=/usr/local/bin/Qv2ray
Name=Qv2ray
Comment=Cross-platform v2ray GUI Client in Qt.
Comment=Cross platform v2ray Qt GUI Client.

Binary file not shown.

11
icons/netspeed-arrows.svg Normal file
View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="459px" height="459px" viewBox="0 0 459 459" style="enable-background:new 0 0 459 459;" xml:space="preserve">
<g>
<g id="swap-vert">
<path fill="#808080" d="M331.5,357V178.5h-51V357H204l102,102l102-102H331.5z M153,0L51,102h76.5v178.5h51V102H255L153,0z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 658 B

View File

@ -5,5 +5,6 @@
<file>icons/import_connection_btn.png</file>
<file>icons/remove_connection_btn.png</file>
<file>icons/edit_connection_btn.png</file>
<file>icons/netspeed-arrows.svg</file>
</qresource>
</RCC>

View File

@ -1,39 +1,44 @@
#ifndef QV2RAYBASE_H
#define QV2RAYBASE_H
#include <QtCore>
#include "QvTinyLog.h"
#include "QvCoreConfigObjects.h"
#include "QvNetSpeedPlugin.h"
#include "QObjectMessageProxy.h"
#define QV2RAY_VERSION_STRING "v" QV_MAJOR_VERSION
#define QV2RAY_CONFIG_VERSION 8
#define QV2RAY_CONFIG_VERSION 7
// Base folder.
#define QV2RAY_CONFIG_DIR_PATH (Qv2ray::Utils::GetConfigDirPath() + "/")
#define QV2RAY_CONFIG_FILE_PATH (QV2RAY_CONFIG_DIR_PATH + "Qv2ray.conf")
// We need v2ray.exe/v2ray executables here!
#define QV2RAY_V2RAY_CORE_DIR_PATH (QV2RAY_CONFIG_DIR_PATH + "vcore/")
#ifdef __WIN32
// Win32 has .exe
#define QV2RAY_V2RAY_CORE_PATH (QV2RAY_V2RAY_CORE_DIR_PATH + "v2ray.exe")
// Base folder suffix.
#ifdef QT_DEBUG
#define QV2RAY_CONFIG_DIR_SUFFIX "_debug/"
#else
// macOS and Linux....
#define QV2RAY_V2RAY_CORE_PATH (QV2RAY_V2RAY_CORE_DIR_PATH + "v2ray")
#define QV2RAY_CONFIG_DIR_SUFFIX "/"
#endif
#define QV2RAY_CONNECTION_FILE_EXTENSION ".qv2ray.json"
#define QV2RAY_GENERATED_FILE_PATH (QV2RAY_CONFIG_DIR_PATH + "generated/config.gen.json")
// Get Configured Config Dir Path
#define QV2RAY_CONFIG_DIR (Qv2ray::Utils::GetConfigDirPath() + "/")
#define QV2RAY_CONFIG_FILE (QV2RAY_CONFIG_DIR + "Qv2ray.conf")
#define QV2RAY_CONFIG_FILE_EXTENSION ".qv2ray.json"
#define QV2RAY_GENERATED_DIR (QV2RAY_CONFIG_DIR + "generated/")
#define QV2RAY_GENERATED_FILE_PATH (QV2RAY_GENERATED_DIR + "config.gen.json")
#ifndef QV2RAY_DEFAULT_VCORE_PATH
#ifdef _WIN32
#define QV2RAY_DEFAULT_VCORE_PATH (QV2RAY_CONFIG_DIR + "vcore/v2ray.exe")
#else
#define QV2RAY_DEFAULT_VCORE_PATH (QV2RAY_CONFIG_DIR + "vcore/v2ray")
#endif
#endif
#define QV2RAY_VCORE_LOG_DIRNAME "logs/"
#define QV2RAY_VCORE_ACCESS_LOG_FILENAME "access.log"
#define QV2RAY_VCORE_ERROR_LOG_FILENAME "error.log"
// GUI TOOLS
#define RED(obj) \
auto _temp = ui->obj->palette(); \
_temp.setColor(QPalette::Text, Qt::red); \
#define RED(obj) \
auto _temp = ui->obj->palette(); \
_temp.setColor(QPalette::Text, Qt::red); \
ui->obj->setPalette(_temp);
#define BLACK(obj) \
@ -43,11 +48,7 @@
#define QSTRING(std_string) QString::fromStdString(std_string)
#ifdef __WIN32
#define NEWLINE "\r\n"
#else
#define NEWLINE "\r"
#endif
namespace Qv2ray
{
@ -57,24 +58,28 @@ namespace Qv2ray
CONFIGTYPE_CONFIG,
CONFIGTYPE_SUBSCRIPTION
};
struct Qv2rayBasicInboundsConfig {
struct Qv2rayCoreInboundsConfig {
string listenip;
// SOCKS
int socks_port;
bool socks_useAuth;
bool socksUDP;
string socksLocalIP;
AccountObject socksAccount;
// HTTP
int http_port;
bool http_useAuth;
AccountObject httpAccount;
Qv2rayBasicInboundsConfig(): listenip(), socks_port(), socks_useAuth(), socksAccount(), http_port(), http_useAuth(), httpAccount() {}
Qv2rayBasicInboundsConfig(string listen, int socksPort, int httpPort): Qv2rayBasicInboundsConfig()
Qv2rayCoreInboundsConfig(): listenip(), socks_port(), socks_useAuth(), socksAccount(), http_port(), http_useAuth(), httpAccount() {}
Qv2rayCoreInboundsConfig(string listen, int socksPort, int httpPort): Qv2rayCoreInboundsConfig()
{
socks_port = socksPort;
http_port = httpPort;
listenip = listen;
socksLocalIP = "0.0.0.0";
socksUDP = true;
}
XTOSTRUCT(O(listenip, socks_port, socks_useAuth, socksAccount, http_port, http_useAuth, httpAccount))
XTOSTRUCT(O(listenip, socks_port, socks_useAuth, socksAccount, socksUDP, socksLocalIP, http_port, http_useAuth, httpAccount))
};
struct Qv2rayConfig {
@ -83,6 +88,7 @@ namespace Qv2ray
int logLevel;
//
string language;
string v2CorePath;
string v2AssetsPath;
string autoStartConfig;
//
@ -91,42 +97,42 @@ namespace Qv2ray
bool bypassCN;
bool enableProxy;
bool withLocalDNS;
MuxObject mux;
//
bool enableStats;
int statsPort;
//
list<string> dnsList;
//
Qv2rayBasicInboundsConfig inBoundSettings;
Qv2rayCoreInboundsConfig inBoundSettings;
#ifdef newFeature
map<string, QvConfigType> configs;
#else
list<string> configs;
#endif
map<string, string> subscribes;
QvNetSpeedBarConfig speedBarConfig;
Qv2rayConfig():
config_version(QV2RAY_CONFIG_VERSION),
tProxySupport(false),
logLevel(),
language(),
v2CorePath(),
v2AssetsPath(),
autoStartConfig(),
ignoredVersion(),
bypassCN(),
enableProxy(),
withLocalDNS(),
mux(),
enableStats(),
statsPort(15934),
dnsList(),
inBoundSettings(),
configs(),
subscribes()
{
// PLACEHOLDER
}
Qv2rayConfig(string lang, string assetsPath, int log, Qv2rayBasicInboundsConfig _inBoundSettings): Qv2rayConfig()
subscribes(),
speedBarConfig() { }
Qv2rayConfig(string lang, string assetsPath, int log, Qv2rayCoreInboundsConfig _inBoundSettings): Qv2rayConfig()
{
// These settings below are defaults.
ignoredVersion = "";
@ -136,10 +142,9 @@ namespace Qv2ray
inBoundSettings = _inBoundSettings;
logLevel = log;
tProxySupport = false;
mux.enabled = false;
dnsList.push_back("8.8.8.8");
dnsList.push_back("8.8.4.4");
dnsList.push_back("1.1.1.1");
dnsList.push_back("4.4.4.4");
bypassCN = true;
enableProxy = true;
withLocalDNS = true;
@ -154,15 +159,16 @@ namespace Qv2ray
language,
autoStartConfig,
ignoredVersion,
v2CorePath,
v2AssetsPath,
enableProxy,
bypassCN,
withLocalDNS,
dnsList,
inBoundSettings,
mux,
configs,
subscribes))
subscribes,
speedBarConfig))
};
// Extra header for QvConfigUpgrade.cpp

View File

@ -5,18 +5,15 @@
#include "QvUtils.h"
#define UPDATELOG(msg) LOG(MODULE_CONFIG, "[" + to_string(fromVersion) + "-" + to_string(fromVersion + 1) + "] --> " msg)
#define UPDATELOG(msg) LOG(MODULE_CONFIG, " [" + to_string(fromVersion) + "-" + to_string(fromVersion + 1) + "] --> " msg)
namespace Qv2ray
{
namespace QvConfigModels
{
// Secret member
// Private member
QJsonObject UpgradeConfig_Inc(int fromVersion, QJsonObject root)
{
//
//
switch (fromVersion) {
case 1: {
auto v1_oldConfigVersion = root["config_version"].toString();
@ -30,7 +27,7 @@ namespace Qv2ray
case 2 : {
// We copied those files.
auto vCoreFilePath = root["v2CorePath"].toString();
auto vCoreDestPath = QV2RAY_V2RAY_CORE_PATH;
auto vCoreDestPath = QV2RAY_DEFAULT_VCORE_PATH;
// We also need v2ctl
auto v2CtlFilePath = QFileInfo(vCoreFilePath).dir().path() + "/v2ctl";
auto v2CtlDestPath = QFileInfo(vCoreDestPath).dir().path() + "/v2ctl";
@ -75,9 +72,16 @@ namespace Qv2ray
break;
}
case 6: {
root["enableStats"] = true;
UPDATELOG("Default statistics enabled.")
case 7: {
QString path;
#ifdef _WIN32
path = QV2RAY_DEFAULT_VCORE_PATH + ".exe";
#else
path = QV2RAY_DEFAULT_VCORE_PATH;
#endif
root["v2CorePath"] = path;
UPDATELOG("Added ")
break;
}
}

View File

@ -1,11 +1,9 @@
#ifndef V2CONFIG_H
#define V2CONFIG_H
#include <list>
#include <string>
#include <x2struct/x2struct.hpp>
#ifndef V2CONFIG_H
#define V2CONFIG_H
using namespace x2struct;
using namespace std;

View File

@ -58,6 +58,7 @@ namespace Qv2ray
// -------------------------- BEGIN CONFIG CONVERSIONS ---------------------------------------------
// Save Connection Config
bool SaveConnectionConfig(QJsonObject obj, const QString *alias);
bool RemoveConnection(const QString *alias);
bool RenameConnection(QString originalName, QString newName);
// VMess Protocol
QJsonObject ConvertConfigFromVMessString(QString vmess);

View File

@ -6,11 +6,17 @@ namespace Qv2ray
// -------------------------- BEGIN CONFIG CONVERSIONS ----------------------------------------------------------------------------
bool SaveConnectionConfig(QJsonObject obj, const QString *alias)
{
QFile config(QV2RAY_CONFIG_DIR_PATH + *alias + QV2RAY_CONNECTION_FILE_EXTENSION);
QFile config(QV2RAY_CONFIG_DIR + *alias + QV2RAY_CONFIG_FILE_EXTENSION);
auto str = JsonToString(obj);
return StringToFile(&str, &config);
}
bool RemoveConnection(const QString *alias)
{
QFile config(QV2RAY_CONFIG_DIR + *alias + QV2RAY_CONFIG_FILE_EXTENSION);
return config.exists() && config.remove();
}
// This generates global config containing only one outbound....
QJsonObject ConvertConfigFromVMessString(QString str)
{
@ -19,14 +25,15 @@ namespace Qv2ray
auto vmessConf = JsonFromString(Base64Decode(vmessJsonB64.toString()));
string ps, add, id, net, type, host, path, tls;
int port, aid;
ps = vmessConf["ps"].toVariant().toString().toStdString();
ps = vmessConf.contains("ps") ? vmessConf["ps"].toVariant().toString().toStdString()
: (vmessConf["add"].toVariant().toString().toStdString() + ":" + vmessConf["port"].toVariant().toString().toStdString());
add = vmessConf["add"].toVariant().toString().toStdString();
id = vmessConf["id"].toVariant().toString().toStdString();
net = vmessConf["net"].toVariant().toString().toStdString();
type = vmessConf["type"].toVariant().toString().toStdString();
net = vmessConf.contains("net") ? vmessConf["net"].toVariant().toString().toStdString() : "tcp";
type = vmessConf.contains("type") ? vmessConf["type"].toVariant().toString().toStdString() : "none";
host = vmessConf["host"].toVariant().toString().toStdString();
path = vmessConf["path"].toVariant().toString().toStdString();
tls = vmessConf["tls"].toVariant().toString().toStdString();
tls = vmessConf.contains("tls") ? vmessConf["tls"].toVariant().toString().toStdString() : "";
//
port = vmessConf["port"].toVariant().toInt();
aid = vmessConf["aid"].toVariant().toInt();
@ -78,7 +85,8 @@ namespace Qv2ray
// Network type
streaming.network = net;
//
auto outbound = GenerateOutboundEntry("vmess", vConf, GetRootObject(streaming), GetRootObject(GetGlobalConfig().mux), "0.0.0.0", OUTBOUND_TAG_PROXY);
// WARN Mux is missing here.
auto outbound = GenerateOutboundEntry("vmess", vConf, GetRootObject(streaming), QJsonObject() /*GetRootObject(GetGlobalConfig().mux) */, "0.0.0.0", OUTBOUND_TAG_PROXY);
//
QJsonArray outbounds;
outbounds.append(outbound);
@ -107,7 +115,7 @@ namespace Qv2ray
QMap<QString, QJsonObject> list;
foreach (auto conn, connectionNames) {
QString jsonString = StringFromFile(new QFile(QV2RAY_CONFIG_DIR_PATH + QString::fromStdString(conn) + QV2RAY_CONNECTION_FILE_EXTENSION));
QString jsonString = StringFromFile(new QFile(QV2RAY_CONFIG_DIR + QSTRING(conn) + QV2RAY_CONFIG_FILE_EXTENSION));
QJsonObject connectionObject = JsonFromString(jsonString);
list.insert(QString::fromStdString(conn), connectionObject);
}
@ -117,7 +125,7 @@ namespace Qv2ray
bool RenameConnection(QString originalName, QString newName)
{
return QFile(QV2RAY_CONFIG_DIR_PATH + originalName + QV2RAY_CONNECTION_FILE_EXTENSION).rename(QV2RAY_CONFIG_DIR_PATH + newName + QV2RAY_CONNECTION_FILE_EXTENSION);
return QFile(QV2RAY_CONFIG_DIR + originalName + QV2RAY_CONFIG_FILE_EXTENSION).rename(QV2RAY_CONFIG_DIR + newName + QV2RAY_CONFIG_FILE_EXTENSION);
}
int StartPreparation(QJsonObject fullConfig)

View File

@ -119,7 +119,12 @@ namespace Qv2ray
accounts.append(GetRootObject(acc));
}
JADD(auth, accounts, udp, ip, userLevel)
if (udp) {
JADD(auth, accounts, udp, ip, userLevel)
} else {
JADD(auth, accounts, userLevel)
}
RROOT
}
@ -208,7 +213,10 @@ namespace Qv2ray
socksInBoundObject.insert("port", gConf.inBoundSettings.socks_port);
socksInBoundObject.insert("protocol", "socks");
socksInBoundObject.insert("tag", "socks_IN");
auto socksInSettings = GenerateSocksIN(gConf.inBoundSettings.socks_useAuth ? "password" : "noauth", QList<AccountObject>() << gConf.inBoundSettings.socksAccount);
auto socksInSettings = GenerateSocksIN(gConf.inBoundSettings.socks_useAuth ? "password" : "noauth",
QList<AccountObject>() << gConf.inBoundSettings.socksAccount,
gConf.inBoundSettings.socksUDP,
QSTRING(gConf.inBoundSettings.socksLocalIP));
socksInBoundObject.insert("settings", socksInSettings);
inboundsList.append(socksInBoundObject);
}
@ -234,10 +242,9 @@ namespace Qv2ray
root.insert("routing", routeObject);
QJsonArray outbounds = root["outbounds"].toArray();
outbounds.append(GenerateOutboundEntry("freedom", GenerateFreedomOUT("AsIs", ":0", 0), QJsonObject(), QJsonObject(), "0.0.0.0", OUTBOUND_TAG_DIRECT));
// TODO
//
// We don't want to add MUX into the first one in the list.....
// However, this can be added to the Connection Edit Window...
// TODO: However, this can be added to the Connection Edit Window...
//QJsonObject first = outbounds.first().toObject();
//first.insert("mux", GetRootObject(gConf.mux));
//outbounds[0] = first;

View File

@ -19,8 +19,20 @@ namespace Qv2ray
auto vmessConf = JsonFromString(vmessString);
// C is a quick hack...
#define C(k) vmessConf.contains(k)
//string v, ps, add, port, id, aid, net, type, host, path, tls;
bool flag = C("v") && C("ps") && C("add") && C("port") && C("id") && C("aid") && C("net") && C("type") && C("host") && C("path") && C("tls");
bool flag = true;
flag = flag && C("id");
flag = flag && C("aid");
flag = flag && C("port");
flag = flag && C("add");
// Stream Settings
auto net = C("net") ? vmessConf["net"].toString() : "tcp";
if (net == "http" || net == "ws")
flag = flag && C("host") && C("path");
else if (net == "domainsocket")
flag = flag && C("path");
else if (net == "quic")
flag = flag && C("host") && C("type") && C("path");
#undef C
return flag ? 0 : 1;
} catch (exception *e) {

View File

@ -16,7 +16,7 @@ namespace Qv2ray
env.insert("V2RAY_LOCATION_ASSET", QString::fromStdString(GetGlobalConfig().v2AssetsPath));
QProcess process;
process.setProcessEnvironment(env);
process.start(QV2RAY_V2RAY_CORE_PATH, QStringList() << "-test" << "-config" << *path, QIODevice::ReadWrite | QIODevice::Text);
process.start(QSTRING(GetGlobalConfig().v2CorePath), QStringList() << "-test" << "-config" << *path, QIODevice::ReadWrite | QIODevice::Text);
if (!process.waitForFinished()) {
LOG(MODULE_VCORE, "v2ray core failed with exitcode: " << process.exitCode())
@ -60,8 +60,12 @@ namespace Qv2ray
bool Qv2Instance::ValidateKernal()
{
if (!QFile::exists(QV2RAY_V2RAY_CORE_PATH)) {
Utils::QvMessageBox(nullptr, QObject::tr("Cannot start v2ray"), QObject::tr("v2ray core file cannot be found at:") + QV2RAY_V2RAY_CORE_PATH);
if (!QFile::exists(QSTRING(GetGlobalConfig().v2CorePath))) {
Utils::QvMessageBox(nullptr, QObject::tr("Cannot start v2ray"),
QObject::tr("v2ray core file cannot be found at:") + NEWLINE +
QSTRING(GetGlobalConfig().v2CorePath) + NEWLINE + NEWLINE +
QObject::tr("Please go to prefrence window to change the location.") + NEWLINE +
QObject::tr("Or put v2ray core file in the location above."));
return false;
} else return true;
}
@ -79,9 +83,9 @@ namespace Qv2ray
if (ValidateConfig(&filePath)) {
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
env.insert("V2RAY_LOCATION_ASSET", QString::fromStdString(GetGlobalConfig().v2AssetsPath));
env.insert("V2RAY_LOCATION_ASSET", QSTRING(GetGlobalConfig().v2AssetsPath));
vProcess->setProcessEnvironment(env);
vProcess->start(QV2RAY_V2RAY_CORE_PATH, QStringList() << "-config" << filePath, QIODevice::ReadWrite | QIODevice::Text);
vProcess->start(QSTRING(GetGlobalConfig().v2CorePath), QStringList() << "-config" << filePath, QIODevice::ReadWrite | QIODevice::Text);
vProcess->waitForStarted();
VCoreStatus = STARTED;
return true;
@ -98,6 +102,8 @@ namespace Qv2ray
void Qv2Instance::StopVCore()
{
vProcess->close();
totalDataTransfered = QMap<QString, long>();
dataTransferSpeed = QMap<QString, long>();
VCoreStatus = STOPPED;
}
@ -126,26 +132,28 @@ namespace Qv2ray
long Qv2Instance::getTagLastUplink(QString tag)
{
auto val = CallStatsAPIByName("inbound>>>" + tag + ">>>traffic>>>uplink");
auto data = val - lastData[tag + "_up"];
lastData[tag + "_up"] = val;
auto data = val - totalDataTransfered[tag + "_up"];
totalDataTransfered[tag + "_up"] = val;
dataTransferSpeed[tag + "_up"] = data;
return data;
}
long Qv2Instance::getTagLastDownlink(QString tag)
{
auto val = CallStatsAPIByName("inbound>>>" + tag + ">>>traffic>>>downlink");
auto data = val - lastData[tag + "_down"];
lastData[tag + "_down"] = val;
auto data = val - totalDataTransfered[tag + "_down"];
totalDataTransfered[tag + "_down"] = val;
dataTransferSpeed[tag + "_down"] = data;
return data;
}
long Qv2Instance::getTagTotalUplink(QString tag)
{
return lastData[tag + "_up"];
return totalDataTransfered[tag + "_up"];
}
long Qv2Instance::getTagTotalDownlink(QString tag)
{
return lastData[tag + "_down"];
return totalDataTransfered[tag + "_down"];
}
}

View File

@ -41,12 +41,13 @@ namespace Qv2ray
QString ReadProcessOutput();
~Qv2Instance();
QMap<QString, long> totalDataTransfered;
QMap<QString, long> dataTransferSpeed;
private:
long CallStatsAPIByName(QString name);
QProcess *vProcess;
std::shared_ptr<::grpc::Channel> Channel;
std::unique_ptr<StatsService::Stub> Stub;
QMap<QString, long> lastData;
int port;
};
}

View File

@ -10,7 +10,7 @@ namespace Qv2ray
void SetGlobalConfig(Qv2rayConfig conf)
{
GlobalConfig = conf;
QFile config(QV2RAY_CONFIG_FILE_PATH);
QFile config(QV2RAY_CONFIG_FILE);
QString str = StructToJsonString(GetGlobalConfig());
StringToFile(&str, &config);
}
@ -34,7 +34,7 @@ namespace Qv2ray
{
QString out;
foreach (string item, list) {
for (auto item : list) {
out.append(QSTRING(item));
out.append(saperator);
}
@ -49,9 +49,9 @@ namespace Qv2ray
{
QString out;
foreach (QString item, list) {
out.append(saperator);
for (auto item : list) {
out.append(item);
out.append(saperator);
}
if (out.length() >= 1)
@ -60,6 +60,13 @@ namespace Qv2ray
return out;
}
QString StringFromFile(QFile *source)
{
source->open(QIODevice::OpenModeFlag::ReadOnly);
auto str = source->readAll();
source->close();
return str;
}
bool StringToFile(const QString *text, QFile *targetFile)
{
bool override = targetFile->exists();
@ -88,6 +95,7 @@ namespace Qv2ray
{
QJsonParseError error;
QJsonDocument doc = QJsonDocument::fromJson(source->toUtf8(), &error);
Q_UNUSED(doc)
if (error.error == QJsonParseError::NoError) {
return "";
@ -103,15 +111,6 @@ namespace Qv2ray
return doc.object();
}
QString StringFromFile(QFile *sourceFile)
{
sourceFile->open(QFile::ReadOnly);
QTextStream stream(sourceFile);
QString str = stream.readAll();
sourceFile->close();
return str;
}
QString Base64Encode(QString string)
{
QByteArray ba;
@ -128,7 +127,7 @@ namespace Qv2ray
void LoadGlobalConfig()
{
QFile file(QV2RAY_CONFIG_FILE_PATH);
QFile file(QV2RAY_CONFIG_FILE);
file.open(QFile::ReadOnly);
QTextStream stream(&file);
auto str = stream.readAll();
@ -137,12 +136,12 @@ namespace Qv2ray
file.close();
}
QStringList getFileList(QDir *dir)
QStringList getFileList(QDir dir)
{
return dir->entryList(QStringList() << "*" << "*.*", QDir::Hidden | QDir::Files);
return dir.entryList(QStringList() << "*" << "*.*", QDir::Hidden | QDir::Files);
}
bool CheckFile(QDir *dir, QString fileName)
bool CheckFile(QDir dir, QString fileName)
{
return getFileList(dir).indexOf(fileName) >= 0;
}
@ -157,8 +156,9 @@ namespace Qv2ray
return QMessageBox::information(parent, title, text, QMessageBox::Yes | QMessageBox::No | extraButtons);
}
QString FormatBytes(long long bytes, char *str)
QString FormatBytes(long long bytes)
{
char str[64];
const char *sizes[5] = { "B", "KB", "MB", "GB", "TB" };
int i;
double dblByte = bytes;
@ -179,5 +179,3 @@ namespace Qv2ray
}
}
}

View File

@ -11,12 +11,12 @@ namespace Qv2ray
{
QTranslator *getTranslator(const QString *lang);
QStringList getFileList(QDir *dir);
QStringList getFileList(QDir dir);
QString Base64Encode(QString string);
QString Base64Decode(QString string);
bool CheckFile(QDir *dir, QString fileName);
bool CheckFile(QDir dir, QString fileName);
void SetConfigDirPath(const QString *path);
QString GetConfigDirPath();
@ -29,8 +29,8 @@ namespace Qv2ray
void QvMessageBox(QWidget *parent, QString title, QString text);
int QvMessageBoxAsk(QWidget *parent, QString title, QString text, QMessageBox::StandardButton extraButtons = QMessageBox::NoButton);
//
QString StringFromFile(QFile *source);
bool StringToFile(const QString *text, QFile *target);
QString StringFromFile(QFile *sourceFile);
//
QJsonObject JsonFromString(QString string);
QString JsonToString(QJsonObject json);
@ -54,7 +54,18 @@ namespace Qv2ray
X::loadjson(str.toStdString(), v, false);
return v;
}
QString FormatBytes(long long bytes, char *str);
//
template <typename T>
void RemoveItem(std::vector<T> &vec, size_t pos)
{
auto it = vec.begin();
std::advance(it, pos);
vec.erase(it);
}
QString FormatBytes(long long bytes);
}
}

View File

@ -9,110 +9,193 @@
#include "QvRunguard.h"
#include "w_MainWindow.h"
bool initQv()
bool verifyConfigAvaliability(QString path, bool checkExistingConfig)
{
if (!QDir(QV2RAY_CONFIG_DIR_PATH).exists()) {
QDir().mkdir(QV2RAY_CONFIG_DIR_PATH);
LOG(MODULE_INIT, "Created Qv2ray config dir at: " + QV2RAY_CONFIG_DIR_PATH.toStdString())
}
if (!QDir(path).exists()) return false;
if (!QDir(QV2RAY_CONFIG_DIR_PATH + "generated/").exists()) {
QDir().mkdir(QV2RAY_CONFIG_DIR_PATH + "generated/");
LOG(MODULE_INIT, "Created config generation dir.")
}
QFile testFile(path + ".qv2ray_file_write_test_file" + QString::number(QTime::currentTime().msec()));
bool opened = testFile.open(QFile::OpenModeFlag::ReadWrite);
if (!QDir(QV2RAY_V2RAY_CORE_DIR_PATH).exists()) {
QDir().mkdir(QV2RAY_V2RAY_CORE_DIR_PATH);
LOG(MODULE_INIT, "Created dir for v2ray core and assets.")
QFile _readmeFile(QV2RAY_V2RAY_CORE_DIR_PATH + "Put your v2ray.exe here.txt");
_readmeFile.open(QIODevice::WriteOnly);
_readmeFile.write("Please put your v2ray.exe and assets here!");
_readmeFile.close();
LOG(MODULE_INIT, "Done generating readme.")
}
QFile qvConfigFile(QV2RAY_CONFIG_FILE_PATH);
if (!qvConfigFile.exists()) {
// This is first run, even the config file does not exist...
//
// These below genenrated very basic global config.
Qv2rayBasicInboundsConfig inboundSetting = Qv2rayBasicInboundsConfig("127.0.0.1", 1080, 8000);
Qv2rayConfig conf = Qv2rayConfig("zh-CN", QV2RAY_V2RAY_CORE_DIR_PATH.toStdString(), 4, inboundSetting);
//
// Save initial config.
SetGlobalConfig(conf);
//
LOG(MODULE_INIT, "Created initial config file.")
if (!opened) {
LOG(MODULE_CONFIG, "Directory at: " + path.toStdString() + " cannot be used as a valid config file path.")
LOG(MODULE_INIT, "---> Cannot create a new file or openwrite a file.")
return false;
} else {
auto conf = JsonFromString(StringFromFile(&qvConfigFile));
auto confVersion = conf["config_version"].toVariant().toString();
auto newVersion = QSTRING(to_string(QV2RAY_CONFIG_VERSION));
testFile.write("test me");
testFile.close();
bool removed = testFile.remove();
// Some config file upgrades.
// Config version is larger than the current version...
if (stoi(confVersion.toStdString()) > QV2RAY_CONFIG_VERSION) {
QvMessageBox(nullptr, QObject::tr("Qv2ray Cannot Continue"), QObject::tr("You are running a lower version of Qv2ray compared to the current config file.") +
"\r\n" +
QObject::tr("Please report if you think this is an error.") + "\r\n" +
if (!removed) {
LOG(MODULE_CONFIG, "Directory at: " + path.toStdString() + " cannot be used as a valid config file path.")
LOG(MODULE_INIT, "---> Cannot remove a file.")
return false;
}
}
if (checkExistingConfig) {
QFile configFile(path + "Qv2ray.conf");
// No such config file.
if (!configFile.exists()) return false;
bool opened2 = configFile.open(QIODevice::OpenModeFlag::ReadWrite);
try {
if (opened2) {
// Verify if the config can be loaded.
auto conf = StructFromJsonString<Qv2rayConfig>(configFile.readAll());
LOG(MODULE_CONFIG, "Path: " + path.toStdString() + " contains a config file version: " + to_string(conf.config_version))
configFile.close();
return true;
} else {
LOG(MODULE_CONFIG, "File: " + configFile.fileName().toStdString() + " cannot be opened!")
return false;
}
} catch (...) {
LOG(MODULE_CONFIG, "Exception raised when checking path: " + configFile.fileName().toStdString())
return false;
}
} else return true;
}
bool initialiseQv2ray()
{
QStringList configFilePaths;
configFilePaths << QDir::homePath() + "/.qv2ray" QV2RAY_CONFIG_DIR_SUFFIX;
configFilePaths << QDir::homePath() + "/.config/qv2ray" QV2RAY_CONFIG_DIR_SUFFIX;
configFilePaths << QDir::currentPath() + "/config" QV2RAY_CONFIG_DIR_SUFFIX;
QString configPath = "";
//
bool hasExistingConfig = false;
for (auto path : configFilePaths) {
// No such directory.
bool avail = verifyConfigAvaliability(path, true);
if (avail) {
//LOG(MODULE_INIT, "Path: " + path.toStdString() + " is valid.")
configPath = path;
hasExistingConfig = true;
} else {
//LOG(MODULE_INIT, "Path: " + path.toStdString() + " does not contain a valid config file.")
}
}
// If there's no existing config.
if (!hasExistingConfig) {
// Create new config at these dirs.
#ifdef _WIN32
configPath = QDir::currentPath() + "/config" QV2RAY_CONFIG_DIR_SUFFIX;
#elif defined (__linux__)
configPath = QDir::homePath() + "/.config/qv2ray" QV2RAY_CONFIG_DIR_SUFFIX;
#elif defined (__APPLE__)
configPath = QDir::homePath() + "/.qv2ray" QV2RAY_CONFIG_DIR_SUFFIX;
#else
LOG(MODULE_INIT, "CANNOT CONTINUE because Qv2ray cannot determine the OS type.")
static_assert(false, "Qv2ray Cannot understand the enviornment");
#endif
bool mkpathResult = QDir().mkpath(configPath);
// Check if the dirs are writeable
if (!mkpathResult || !verifyConfigAvaliability(configPath, false)) {
// None of the path above can be used as a dir for storing config.
LOG(MODULE_INIT, "FATAL")
LOG(MODULE_INIT, " ---> CANNOT find a proper place to store Qv2ray config files.")
LOG(MODULE_INIT, " ---> Qv2ray will now EXIT")
QString searchPath = Stringify(configFilePaths, NEWLINE);
QvMessageBox(nullptr, QObject::tr("Cannot Start Qv2ray"),
QObject::tr("Cannot find a place to store config files.") + NEWLINE +
QObject::tr("Qv2ray has searched these paths below:") +
NEWLINE + NEWLINE +
searchPath +
NEWLINE + NEWLINE +
QObject::tr("Qv2ray will now exit."));
return false;
} else if (QString::compare(confVersion, newVersion) != 0) {
conf = UpgradeConfig(stoi(confVersion.toStdString()), QV2RAY_CONFIG_VERSION, conf);
} else {
LOG(MODULE_INIT, "Set " + configPath.toStdString() + " as the config path.")
SetConfigDirPath(&configPath);
QFile qvConfigFile(configPath + "/Qv2ray.conf");
// Generate new default config.
Qv2rayCoreInboundsConfig inboundSetting = Qv2rayCoreInboundsConfig("127.0.0.1", 1080, 8000);
Qv2rayConfig conf = Qv2rayConfig("en-US", QV2RAY_DEFAULT_VCORE_PATH.toStdString(), 4, inboundSetting);
//
// Save initial config.
SetGlobalConfig(conf);
//
LOG(MODULE_INIT, "Created initial config file.")
}
auto confObject = StructFromJsonString<Qv2rayConfig>(JsonToString(conf));
SetGlobalConfig(confObject);
LOG(MODULE_INIT, "Loaded config file.")
return true;
} else {
LOG(MODULE_INIT, "Set " + configPath.toStdString() + " as the config path.")
SetConfigDirPath(&configPath);
}
return false;
if (!QDir(QV2RAY_CONFIG_DIR + "generated/").exists()) {
QDir().mkdir(QV2RAY_CONFIG_DIR + "generated/");
LOG(MODULE_INIT, "Created config generation dir under: " + QV2RAY_CONFIG_DIR.toStdString())
}
auto conf = JsonFromString(StringFromFile(new QFile(QV2RAY_CONFIG_FILE)));
//
auto confVersion = conf["config_version"].toVariant().toString();
auto newVersion = QSTRING(to_string(QV2RAY_CONFIG_VERSION));
// Some config file upgrades.
if (confVersion.toInt() > QV2RAY_CONFIG_VERSION) {
// Config version is larger than the current version...
// This is rare but it may happen....
QvMessageBox(nullptr, QObject::tr("Qv2ray Cannot Continue"),
QObject::tr("You are running a lower version of Qv2ray compared to the current config file.") + NEWLINE +
QObject::tr("Please report if you think this is an error.") + NEWLINE +
QObject::tr("Qv2ray will now exit."));
return false;
} else if (confVersion != newVersion) {
conf = UpgradeConfig(confVersion.toInt(), QV2RAY_CONFIG_VERSION, conf);
}
auto confObject = StructFromJsonString<Qv2rayConfig>(JsonToString(conf));
SetGlobalConfig(confObject);
return true;
}
int main(int argc, char *argv[])
{
LOG("LICENCE", "\r\nThis program comes with ABSOLUTELY NO WARRANTY.\r\n"
"This is free software, and you are welcome to redistribute it\r\n"
"under certain conditions.\r\n"
"\r\n"
"Qv2ray Current Developer Copyright (C) 2019 Leroy.H.Y (@lhy0403)\r\n"
"Hv2ray Initial Designs & gRPC implementation Copyright (C) 2019 Hork (@aliyuchang33)\r\n"
"Hv2ray/Qv2ray HTTP Request Helper (partial) Copyright 2019 (C) SOneWinstone (@SoneWinstone)\r\n"
"Qv2ray ArtWork Done By ArielAxionL (@axionl)\r\n"
"Qv2ray Russian Translations By TheBadGateway (@thebadgateway)\r\n"
"\r\n"
// This line must be called before any other ones.
QApplication _qApp(argc, argv);
LOG("LICENCE", NEWLINE "This program comes with ABSOLUTELY NO WARRANTY." NEWLINE
"This is free software, and you are welcome to redistribute it" NEWLINE
"under certain conditions." NEWLINE
NEWLINE
"Qv2ray Current Developer Copyright (C) 2019 Leroy.H.Y (@lhy0403)" NEWLINE
"Hv2ray Initial Designs & gRPC implementation Copyright (C) 2019 Hork (@aliyuchang33)" NEWLINE
"Hv2ray/Qv2ray HTTP Request Helper (partial) Copyright 2019 (C) SOneWinstone (@SoneWinstone)" NEWLINE
"Qv2ray ArtWork Done By ArielAxionL (@axionl)" NEWLINE
"Qv2ray Russian Translations By TheBadGateway (@thebadgateway)" NEWLINE
"Qv2ray patch 8a8c1a By Riko (@rikakomoe)" NEWLINE
NEWLINE
"Qv2ray " QV2RAY_VERSION_STRING " running on " +
QSysInfo::prettyProductName().toStdString() + " " + QSysInfo::currentCpuArchitecture().toStdString() +
"\r\n")
(QSysInfo::prettyProductName() + " " + QSysInfo::currentCpuArchitecture()).toStdString() +
NEWLINE)
//
#ifdef QT_DEBUG
LOG("DEBUG", "============================== This is a debug build, many features are not stable enough. ==============================")
QString configPath = QDir::homePath() + "/.qv2ray_debug";
#else
QString configPath = QDir::homePath() + "/.qv2ray";
#endif
SetConfigDirPath(&configPath);
QDirIterator it(":/translations");
auto langs = getFileList(QDir(":/translations"));
if (!it.hasNext()) {
LOG(MODULE_UI, "FAILED to find any translations, THIS IS A BUILD ERROR.")
if (langs.empty()) {
LOG(MODULE_UI, "FAILED to find any translations. THIS IS A BUILD ERROR.")
QvMessageBox(nullptr, "Cannot load languages", "Qv2ray will run, but you are not able to select languages.");
} else {
for (auto lang : langs) {
LOG(MODULE_UI, "Found Translator: " + lang.toStdString())
}
}
while (it.hasNext()) {
LOG(MODULE_UI, "Found Translator: " + it.next().toStdString())
}
//
QApplication _qApp(argc, argv);
//
// Qv2ray Initialize
if (!initQv())
if (!initialiseQv2ray()) {
return -1;
}
//
#ifdef _WIN32
// Set special font in Windows
QFont font;
@ -123,7 +206,7 @@ int main(int argc, char *argv[])
#ifdef __APPLE__
_qApp.setStyle("fusion");
#endif
LOG(MODULE_UI, "Current Qv2ray Window Style: " + _qApp.style()->objectName().toStdString())
LOG(MODULE_UI, "Current Window Style: " + _qApp.style()->objectName().toStdString())
auto list = QStyleFactory::keys();
LOG(MODULE_UI, Stringify(list).toStdString())
auto lang = GetGlobalConfig().language;
@ -155,7 +238,12 @@ int main(int argc, char *argv[])
QvMessageBox(nullptr, QObject::tr("DependencyMissing"),
QObject::tr("Cannot find openssl libs") + "\r\n" +
QObject::tr("This could be caused by a missing of `openssl` package in your system. Or an AppImage issue.") + "\r\n" +
QObject::tr("If you are using AppImage, please report a bug."));
QObject::tr("If you are using AppImage, please report a bug.") + "\r\n\r\n" +
QObject::tr("Please refer to Github Issue #65 to check for solutions.") + "\r\n" +
QObject::tr("Github Issue Link: ") + "https://github.com/lhy0403/Qv2ray/issues/65" + "\r\n\r\n" +
QObject::tr("Technical Details") + "\r\n" +
"OSsl.Rq.V=" + QSTRING(osslReqVersion) + "\r\n" +
"OSsl.Cr.V=" + QSTRING(osslCurVersion));
return -2;
}

View File

@ -0,0 +1,184 @@
#include <QThread>
#include "QvNetSpeedPlugin.h"
#include "QvUtils.h"
#include "w_MainWindow.h"
namespace Qv2ray
{
namespace Utils
{
namespace NetSpeedPlugin
{
static MainWindow *mainWindow;
static Qv2rayConfig config;
void StopProcessingPlugins()
{
#ifdef __linux__
_linux::StopMessageQThread();
#endif
#ifdef _WIN32
_win::KillNamedPipeThread();
#endif
}
/// Public Function - CALL ONLY ONCE -
/// To start processing plugins' command.
void StartProcessingPlugins(QWidget *_mainWindow)
{
mainWindow = static_cast<MainWindow *>(_mainWindow);
config = GetGlobalConfig();
#ifdef __linux__
_linux::StartMessageQThread();
#endif
#ifdef _WIN32
_win::StartNamedPipeThread();
#endif
}
QString GetAnswerToRequest(const QString &pchRequest)
{
auto req = pchRequest.trimmed();
config = GetGlobalConfig();
QString reply = "{}";
if (req == "START") {
emit mainWindow->Connect();
} else if (req == "STOP") {
emit mainWindow->DisConnect();
} else if (req == "RESTART") {
emit mainWindow->ReConnect();
}
auto BarConfig = config.speedBarConfig;
for (size_t i = 0; i < BarConfig.Pages.size(); i++) {
for (size_t j = 0; j < BarConfig.Pages[i].Lines.size(); j++) {
#define CL BarConfig.Pages[i].Lines[j]
#define STATS_ENABLE_CHECK if(!config.enableStats) { CL.Message = QObject::tr("Stats is not enabled").toStdString(); break;}
switch (CL.ContentType) {
case 0: {
// Custom Text
// We do nothing...
break;
}
case 101: {
// Current Time
CL.Message = QTime().toString("hh:mm:ss").toStdString();
break;
}
case 102: {
// Current Date
CL.Message = QDate().toString("yyyy-MM-dd").toStdString();
break;
}
case 103: {
// Current Qv2ray Version
CL.Message = QV2RAY_VERSION_STRING;
break;
}
case 104: {
// Current Connection Name
CL.Message = mainWindow->CurrentConnectionName.toStdString();
break;
}
case 105: {
// Current Connection Status
switch (mainWindow->vinstance->VCoreStatus) {
case STARTED: {
CL.Message = QObject::tr("Connected").toStdString();
break;
}
case STOPPED: {
CL.Message = QObject::tr("Disconnected").toStdString();
break;
}
case STARTING: {
CL.Message = QObject::tr("Connecting").toStdString();
break;
}
}
break;
}
case 201: {
// Total upload speed;
STATS_ENABLE_CHECK
CL.Message = (mainWindow->totalSpeedUp + "/s").toStdString();
break;
}
case 202: {
// Total download speed;
STATS_ENABLE_CHECK
CL.Message = (mainWindow->totalSpeedDown + "/s").toStdString();
break;
}
case 203: {
// Upload speed for tag
STATS_ENABLE_CHECK
auto data = mainWindow->vinstance->dataTransferSpeed[QSTRING(CL.Message) + "_up"];
CL.Message = FormatBytes(data).toStdString() + "/s";
break;
}
case 204: {
STATS_ENABLE_CHECK
// Download speed for tag
auto data = mainWindow->vinstance->dataTransferSpeed[QSTRING(CL.Message) + "_down"];
CL.Message = FormatBytes(data).toStdString() + "/s";
break;
}
case 301: {
// Total Upload
STATS_ENABLE_CHECK
CL.Message = (mainWindow->totalDataUp).toStdString();
break;
}
case 302: {
// Total download
STATS_ENABLE_CHECK
CL.Message = (mainWindow->totalDataDown).toStdString();
break;
}
case 303: {
// Upload for tag
STATS_ENABLE_CHECK
auto data = mainWindow->vinstance->totalDataTransfered[QSTRING(CL.Message) + "_up"];
CL.Message = FormatBytes(data).toStdString();
break;
}
case 304: {
// Download for tag
STATS_ENABLE_CHECK
auto data = mainWindow->vinstance->totalDataTransfered[QSTRING(CL.Message) + "_down"];
CL.Message = FormatBytes(data).toStdString();
break;
}
default: {
CL.Message = "Not Supported?";
break;
}
}
}
}
reply = StructToJsonString(BarConfig);
return reply;
}
}
}
}

View File

@ -0,0 +1,91 @@
#ifdef __linux__
#include "QvNetSpeedPlugin.h"
#include "QvUtils.h"
#include <QLocalSocket>
#include <QLocalServer>
namespace Qv2ray
{
namespace Utils
{
namespace NetSpeedPlugin
{
namespace _linux
{
static QThread *linuxWorkerThread;
static QLocalServer *server;
static QObjectMessageProxy *messageProxy;
static bool isExiting = false;
void qobject_proxy()
{
QLocalSocket *socket = server->nextPendingConnection();
if (!socket->waitForConnected() || !socket->waitForReadyRead())
return;
try {
while (!isExiting && socket->isOpen() && socket->isValid() && socket->waitForReadyRead()) {
// CANNOT PROPERLY READ...
// Temp-ly fixed (but why and how?)
auto in = QString(socket->readAll());
if (!isExiting && !in.isEmpty()) {
auto out = GetAnswerToRequest(in);
//
socket->write(out.toUtf8());
socket->flush();
} else {
QThread::msleep(200);
}
}
} catch (...) {
LOG(MODULE_PLUGIN, "Closing a broken socket.")
}
}
void DataMessageQThread()
{
server = new QLocalServer();
// BUG Sometimes failed to listen due to improper close of last session.
bool listening = server->listen(QV2RAY_NETSPEED_PLUGIN_PIPE_NAME_LINUX);
while (!isExiting && !listening) {
QThread::msleep(500);
listening = server->listen(QV2RAY_NETSPEED_PLUGIN_PIPE_NAME_LINUX);
}
bool timeOut = false;
server->setSocketOptions(QLocalServer::WorldAccessOption);
messageProxy = new QObjectMessageProxy(&qobject_proxy);
QObject::connect(server, &QLocalServer::newConnection, messageProxy, &QObjectMessageProxy::processMessage);
while (!isExiting) {
bool result = server->waitForNewConnection(200, &timeOut);
LOG(MODULE_PLUGIN, "Plugin thread listening failed: " << server->errorString().toStdString())
LOG(MODULE_PLUGIN, "waitForNewConnection: " << (result ? "true" : "false") << ", " << (timeOut ? "true" : "false"))
}
server->close();
}
void StartMessageQThread()
{
linuxWorkerThread = QThread::create(_linux::DataMessageQThread);
linuxWorkerThread->start();
}
void StopMessageQThread()
{
isExiting = true;
if (linuxWorkerThread->isRunning()) {
LOG(MODULE_PLUGIN, "Waiting for linuxWorkerThread to stop.")
linuxWorkerThread->wait();
}
delete _linux::linuxWorkerThread;
}
}
}
}
}
#endif

View File

@ -0,0 +1,114 @@
#ifdef _WIN32
#include "QvNetSpeedPlugin.h"
#include "QvUtils.h"
namespace Qv2ray
{
namespace Utils
{
namespace NetSpeedPlugin
{
namespace _win
{
// Private Headers
constexpr int BUFSIZE = 10240;
DWORD WINAPI NamedPipeMasterThread(LPVOID lpvParam);
DWORD WINAPI InstanceThread(LPVOID);
static LPVOID ThreadHandle;
static bool isExiting = false;
//
void KillNamedPipeThread()
{
isExiting = true;
TerminateThread(ThreadHandle, 0);
}
//
void StartNamedPipeThread()
{
auto hThread = CreateThread(nullptr, 0, NamedPipeMasterThread, nullptr, 0, nullptr);
if (hThread == nullptr) {
LOG(MODULE_PLUGIN, "CreateThread failed, GLE=" << GetLastError())
return;
} else CloseHandle(hThread);
}
DWORD WINAPI NamedPipeMasterThread(LPVOID lpvParam)
{
Q_UNUSED(lpvParam)
BOOL fConnected = FALSE;
DWORD dwThreadId = 0;
HANDLE hPipe = INVALID_HANDLE_VALUE;
auto lpszPipename = QString().toStdWString();
while (!isExiting) {
//printf("Pipe Server: Main thread awaiting client connection on %s\n", lpszPipename.c_str());
hPipe = CreateNamedPipe(lpszPipename.c_str(), PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, BUFSIZE, BUFSIZE, 0, nullptr);
if (hPipe == INVALID_HANDLE_VALUE) {
LOG(MODULE_PLUGIN, "CreateNamedPipe failed, GLE=" << GetLastError())
return static_cast<DWORD>(-1);
}
fConnected = ConnectNamedPipe(hPipe, nullptr) ? true : (GetLastError() == ERROR_PIPE_CONNECTED);
if (fConnected) {
LOG(MODULE_PLUGIN, "Client connected, creating a processing thread")
ThreadHandle = CreateThread(nullptr, 0, InstanceThread, hPipe, 0, &dwThreadId);
if (ThreadHandle == nullptr) {
LOG(MODULE_PLUGIN, "CreateThread failed, GLE=%d.\n" << GetLastError())
return static_cast<DWORD>(-1);
} else CloseHandle(ThreadHandle);
} else CloseHandle(hPipe);
}
return 0;
}
DWORD WINAPI InstanceThread(LPVOID lpvParam)
{
DWORD cbBytesRead = 0, cbReplyBytes = 0, cbWritten = 0;
BOOL fSuccess = false;
HANDLE hPipe = static_cast<HANDLE>(lpvParam);
TCHAR pchRequest[BUFSIZE] = { 0 };
while (!isExiting) {
fSuccess = ReadFile(hPipe, pchRequest, BUFSIZE * sizeof(TCHAR), &cbBytesRead, nullptr);
if (!fSuccess || cbBytesRead == 0) {
if (GetLastError() == ERROR_BROKEN_PIPE) {
LOG(MODULE_PLUGIN, "InstanceThread: client disconnected.\n" + to_string(GetLastError()))
} else {
LOG(MODULE_PLUGIN, "InstanceThread ReadFile failed, GLE=" + to_string(GetLastError()))
}
break;
}
auto req = QString::fromStdWString(pchRequest);
auto replyQString = isExiting ? "{}" : GetAnswerToRequest(req);
//
// REPLY as std::string
std::string pchReply = replyQString.toUtf8().constData();
cbReplyBytes = static_cast<DWORD>(pchReply.length() + 1) * sizeof(CHAR);
//cbReplyBytes = static_cast<DWORD>(replyQString.length() + 1) * sizeof(TCHAR);
//
fSuccess = WriteFile(hPipe, pchReply.c_str(), cbReplyBytes, &cbWritten, nullptr);
if (!fSuccess || cbReplyBytes != cbWritten) {
LOG(MODULE_PLUGIN, "InstanceThread WriteFile failed, GLE=" + to_string(GetLastError()))
break;
}
}
FlushFileBuffers(hPipe);
DisconnectNamedPipe(hPipe);
CloseHandle(hPipe);
return 1;
}
}
}
}
}
#endif

View File

@ -8,7 +8,7 @@
#include "QvUtils.h"
#include "QvCoreInteractions.h"
#include "QvCoreConfigOperations.h"
#include "w_ConnectionEditWindow.h"
#include "w_OutboundEditor.h"
#include "w_ImportConfig.h"
@ -90,7 +90,7 @@ void ImportConfigWindow::on_buttonBox_accepted()
return;
default:
QvMessageBox(this, tr("VMess String Check"), tr("Some internal error occured."));
QvMessageBox(this, tr("VMess String Check"), tr("VMess config is not valid."));
return;
}
}

View File

@ -92,7 +92,7 @@
<item>
<widget class="QStackedWidget" name="stackedWidget">
<property name="currentIndex">
<number>3</number>
<number>0</number>
</property>
<widget class="QWidget" name="filePage">
<layout class="QFormLayout" name="formLayout">

432
src/ui/w_InboundEditor.cpp Normal file
View File

@ -0,0 +1,432 @@
#include "w_InboundEditor.h"
#include "ui_w_InboundEditor.h"
#include "QvUtils.h"
#include "QvCoreConfigOperations.h"
static bool isLoading = false;
#define PREPARE_RETURN if(isLoading) return;
InboundEditor::InboundEditor(QJsonObject root, QWidget *parent) :
QDialog(parent),
ui(new Ui::InboundEditor)
{
original = root;
this->root = root;
auto inboundType = root["protocol"].toString();
allocate = root["allocate"].toObject();
sniffing = root["sniffing"].toObject();
if (inboundType == "http") {
httpSettings = root["settings"].toObject();
} else if (inboundType == "socks") {
socksSettings = root["settings"].toObject();
} else if (inboundType == "dokodemo-door") {
dokoSettings = root["settings"].toObject();
} else if (inboundType == "mtproto") {
mtSettings = root["settings"].toObject();
} else {
LOG(MODULE_UI, "Unsupported inbound type: " + inboundType.toStdString() + ", decisions should be made if to open JSONEDITOR")
QvMessageBox(this, tr("Inbound type not supported"), tr("The inbound type is not supported by Qv2ray (yet). Please use JsonEditor to change the settings") + "\r\n" +
tr("Inbound: ") + inboundType);
}
ui->setupUi(this);
LoadUIData();
}
QJsonObject InboundEditor::OpenEditor()
{
int resultCode = this->exec();
return resultCode == QDialog::Accepted ? GenerateNewRoot() : original;
}
QJsonObject InboundEditor::GenerateNewRoot()
{
QJsonObject _newRoot = root;
auto inboundType = root["protocol"].toString();
if (inboundType == "http") {
_newRoot["settings"] = httpSettings;
} else if (inboundType == "socks") {
_newRoot["settings"] = socksSettings;
} else if (inboundType == "dokodemo-door") {
_newRoot["settings"] = dokoSettings;
} else if (inboundType == "mtproto") {
_newRoot["settings"] = mtSettings;
}
_newRoot["sniffing"] = sniffing;
_newRoot["allocate"] = allocate;
return _newRoot;
}
void InboundEditor::LoadUIData()
{
isLoading = true;
ui->strategyCombo->setCurrentText(allocate["strategy"].toString());
ui->refreshNumberBox->setValue(allocate["refresh"].toInt());
ui->concurrencyNumberBox->setValue(allocate["concurrency"].toInt());
ui->enableSniffingCB->setChecked(sniffing["enabled"].toBool());
foreach (auto item, sniffing["destOverride"].toArray()) {
if (item.toString().toLower() == "http") ui->destOverrideList->item(0)->setCheckState(Qt::Checked);
if (item.toString().toLower() == "tls") ui->destOverrideList->item(1)->setCheckState(Qt::Checked);
}
//
ui->inboundTagTxt->setText(root["tag"].toString());
ui->inboundHostTxt->setText(root["listen"].toString());
ui->inboundPortTxt->setText(root["port"].toVariant().toString());
ui->inboundProtocolCombo->setCurrentText(root["protocol"].toString());
// HTTP
ui->httpTimeoutSpinBox->setValue(httpSettings["timeout"].toInt());
ui->httpTransparentCB->setChecked(httpSettings["allowTransparent"].toBool());
ui->httpUserLevelSB->setValue(httpSettings["userLevel"].toInt());
ui->httpAccountListBox->clear();
for (auto user : httpSettings["accounts"].toArray()) {
ui->httpAccountListBox->addItem(user.toObject()["user"].toString() + ":" + user.toObject()["pass"].toString());
}
// SOCKS
ui->socksAuthCombo->setCurrentText(socksSettings["auth"].toString());
ui->socksUDPCB->setChecked(socksSettings["udp"].toBool());
ui->socksUDPIPAddrTxt->setText(socksSettings["ip"].toString());
ui->socksUserLevelSB->setValue(socksSettings["userLevel"].toInt());
for (auto user : socksSettings["accounts"].toArray()) {
ui->socksAccountListBox->addItem(user.toObject()["user"].toString() + ":" + user.toObject()["pass"].toString());
}
// Dokodemo-Door
ui->dokoFollowRedirectCB->setChecked(dokoSettings["followRedirect"].toBool());
ui->dokoIPAddrTxt->setText(dokoSettings["address"].toString());
ui->dokoPortSB->setValue(dokoSettings["port"].toInt());
ui->dokoTimeoutSB->setValue(dokoSettings["timeout"].toInt());
ui->dokoUserLevelSB->setValue(dokoSettings["userLevel"].toInt());
ui->dokoTCPCB->setChecked(dokoSettings["network"].toString().contains("tcp"));
ui->dokoUDPCB->setChecked(dokoSettings["network"].toString().contains("udp"));
// MTProto
ui->mtEMailTxt->setText(mtSettings["users"].toArray().first().toObject()["email"].toString());
ui->mtUserLevelSB->setValue(mtSettings["users"].toArray().first().toObject()["level"].toInt());
ui->mtSecretTxt->setText(mtSettings["users"].toArray().first().toObject()["secret"].toString());
isLoading = false;
}
InboundEditor::~InboundEditor()
{
delete ui;
}
void InboundEditor::on_inboundProtocolCombo_currentIndexChanged(const QString &arg1)
{
PREPARE_RETURN
root["protocol"] = arg1.toLower();
}
void InboundEditor::on_inboundProtocolCombo_currentIndexChanged(int index)
{
ui->stackedWidget->setCurrentIndex(index);
}
void InboundEditor::on_inboundTagTxt_textEdited(const QString &arg1)
{
PREPARE_RETURN
root["tag"] = arg1;
}
void InboundEditor::on_httpTimeoutSpinBox_valueChanged(int arg1)
{
PREPARE_RETURN
httpSettings["timtout"] = arg1;
}
void InboundEditor::on_httpTransparentCB_stateChanged(int arg1)
{
PREPARE_RETURN
httpSettings["allowTransparent"] = arg1 == Qt::Checked;
}
void InboundEditor::on_httpUserLevelSB_valueChanged(int arg1)
{
PREPARE_RETURN
httpSettings["userLevel"] = arg1;
}
void InboundEditor::on_httpRemoveUserBtn_clicked()
{
PREPARE_RETURN
if (ui->httpAccountListBox->currentRow() != -1) {
auto item = ui->httpAccountListBox->currentItem();
auto list = httpSettings["accounts"].toArray();
for (int i = 0 ; i < list.count(); i++) {
auto user = list[i].toObject();
auto entry = user["user"].toString() + ":" + user["pass"].toString();
if (entry == item->text().trimmed()) {
list.removeAt(i);
httpSettings["accounts"] = list;
LOG(MODULE_UI, "Removed http inbound user " + entry.toStdString())
ui->httpAccountListBox->takeItem(ui->httpAccountListBox->currentRow());
}
}
//QvMessageBox(this, tr("Removing a user"), tr("No user has been removed. Why?"));
} else {
QvMessageBox(this, tr("Removing a user"), tr("You haven't selected a user yet."));
}
}
void InboundEditor::on_httpAddUserBtn_clicked()
{
PREPARE_RETURN
auto user = ui->httpAddUserTxt->text();
auto pass = ui->httpAddPasswordTxt->text();
//
auto list = httpSettings["accounts"].toArray();
for (int i = 0 ; i < list.count(); i++) {
auto _user = list[i].toObject();
if (_user["user"].toString() == user) {
QvMessageBox(this, tr("Add a user"), tr("This user exists already."));
return;
}
}
ui->httpAddUserTxt->clear();
ui->httpAddPasswordTxt->clear();
QJsonObject entry;
entry["user"] = user;
entry["pass"] = pass;
list.append(entry);
ui->httpAccountListBox->addItem(user + ":" + pass);
httpSettings["accounts"] = list;
}
void InboundEditor::on_socksRemoveUserBtn_clicked()
{
PREPARE_RETURN
if (ui->socksAccountListBox->currentRow() != -1) {
auto item = ui->socksAccountListBox->currentItem();
auto list = socksSettings["accounts"].toArray();
for (int i = 0 ; i < list.count(); i++) {
auto user = list[i].toObject();
auto entry = user["user"].toString() + ":" + user["pass"].toString();
if (entry == item->text().trimmed()) {
list.removeAt(i);
socksSettings["accounts"] = list;
LOG(MODULE_UI, "Removed http inbound user " + entry.toStdString())
ui->socksAccountListBox->takeItem(ui->socksAccountListBox->currentRow());
return;
}
}
} else {
QvMessageBox(this, tr("Removing a user"), tr("You haven't selected a user yet."));
}
}
void InboundEditor::on_socksAddUserBtn_clicked()
{
PREPARE_RETURN
auto user = ui->socksAddUserTxt->text();
auto pass = ui->socksAddPasswordTxt->text();
//
auto list = socksSettings["accounts"].toArray();
for (int i = 0 ; i < list.count(); i++) {
auto _user = list[i].toObject();
if (_user["user"].toString() == user) {
QvMessageBox(this, tr("Add a user"), tr("This user exists already."));
return;
}
}
ui->socksAddUserTxt->clear();
ui->socksAddPasswordTxt->clear();
QJsonObject entry;
entry["user"] = user;
entry["pass"] = pass;
list.append(entry);
ui->socksAccountListBox->addItem(user + ":" + pass);
socksSettings["accounts"] = list;
}
void InboundEditor::on_strategyCombo_currentIndexChanged(const QString &arg1)
{
PREPARE_RETURN
allocate["strategy"] = arg1.toLower();
}
void InboundEditor::on_refreshNumberBox_valueChanged(int arg1)
{
PREPARE_RETURN
allocate["refresh"] = arg1;
}
void InboundEditor::on_concurrencyNumberBox_valueChanged(int arg1)
{
PREPARE_RETURN
allocate["concurrency"] = arg1;
}
void InboundEditor::on_enableSniffingCB_stateChanged(int arg1)
{
PREPARE_RETURN
sniffing["enabled"] = arg1 == Qt::Checked;
}
void InboundEditor::on_destOverrideList_itemChanged(QListWidgetItem *item)
{
PREPARE_RETURN
Q_UNUSED(item)
QJsonArray list;
for (int i = 0; i < ui->destOverrideList->count(); i++) {
auto _item = ui->destOverrideList->item(i);
if (item->checkState() == Qt::Checked) {
list.append(_item->text().toLower());
}
}
sniffing["destOverride"] = list;
}
void InboundEditor::on_socksUDPCB_stateChanged(int arg1)
{
PREPARE_RETURN
socksSettings["udp"] = arg1 == Qt::Checked;
}
void InboundEditor::on_socksUDPIPAddrTxt_textEdited(const QString &arg1)
{
PREPARE_RETURN
socksSettings["ip"] = arg1;
}
void InboundEditor::on_socksUserLevelSB_valueChanged(int arg1)
{
PREPARE_RETURN
socksSettings["userLevel"] = arg1;
}
void InboundEditor::on_dokoIPAddrTxt_textEdited(const QString &arg1)
{
PREPARE_RETURN
dokoSettings["address"] = arg1;
}
void InboundEditor::on_dokoPortSB_valueChanged(int arg1)
{
PREPARE_RETURN
dokoSettings["port"] = arg1;
}
void InboundEditor::on_dokoTCPCB_stateChanged(int arg1)
{
PREPARE_RETURN
Q_UNUSED(arg1)
bool hasTCP = ui->dokoTCPCB->checkState() == Qt::Checked;
bool hasUDP = ui->dokoUDPCB->checkState() == Qt::Checked;
QString str = "";
str += hasTCP ? "tcp" : "";
str += (hasTCP && hasUDP) ? "," : "";
str += hasUDP ? "udp" : "";
dokoSettings["network"] = str;
}
void InboundEditor::on_dokoUDPCB_stateChanged(int arg1)
{
PREPARE_RETURN
Q_UNUSED(arg1)
bool hasTCP = ui->dokoTCPCB->checkState() == Qt::Checked;
bool hasUDP = ui->dokoUDPCB->checkState() == Qt::Checked;
QString str = "";
str += hasTCP ? "tcp" : "";
str += (hasTCP && hasUDP) ? "," : "";
str += hasUDP ? "udp" : "";
dokoSettings["network"] = str;
}
void InboundEditor::on_dokoTimeoutSB_valueChanged(int arg1)
{
PREPARE_RETURN
dokoSettings["timeout"] = arg1;
}
void InboundEditor::on_dokoFollowRedirectCB_stateChanged(int arg1)
{
PREPARE_RETURN
dokoSettings["followRedirect"] = arg1 == Qt::Checked;
}
void InboundEditor::on_dokoUserLevelSB_valueChanged(int arg1)
{
PREPARE_RETURN
dokoSettings["userLevel"] = arg1;
}
void InboundEditor::on_mtEMailTxt_textEdited(const QString &arg1)
{
PREPARE_RETURN
if (!mtSettings.contains("users")) mtSettings["users"] = QJsonArray();
QJsonObject user = mtSettings["users"].toArray().empty() ? QJsonObject() : mtSettings["users"].toArray().first().toObject();
user["email"] = arg1;
QJsonArray list;
list.append(user);
mtSettings["users"] = list;
}
void InboundEditor::on_mtSecretTxt_textEdited(const QString &arg1)
{
PREPARE_RETURN
if (!mtSettings.contains("users")) mtSettings["users"] = QJsonArray();
QJsonObject user = mtSettings["users"].toArray().empty() ? QJsonObject() : mtSettings["users"].toArray().first().toObject();
user["secret"] = arg1;
QJsonArray list;
list.append(user);
mtSettings["users"] = list;
}
void InboundEditor::on_mtUserLevelSB_valueChanged(int arg1)
{
PREPARE_RETURN
if (!mtSettings.contains("users")) mtSettings["users"] = QJsonArray();
QJsonObject user = mtSettings["users"].toArray().empty() ? QJsonObject() : mtSettings["users"].toArray().first().toObject();
user["userLevel"] = arg1;
QJsonArray list;
list.append(user);
mtSettings["users"] = list;
}
void InboundEditor::on_inboundHostTxt_textEdited(const QString &arg1)
{
PREPARE_RETURN
root["listen"] = arg1;
}
void InboundEditor::on_inboundPortTxt_textEdited(const QString &arg1)
{
PREPARE_RETURN
root["port"] = arg1;
}
void InboundEditor::on_socksAuthCombo_currentIndexChanged(const QString &arg1)
{
PREPARE_RETURN
socksSettings["auth"] = arg1.toLower();
}

101
src/ui/w_InboundEditor.h Normal file
View File

@ -0,0 +1,101 @@
#ifndef W_INBOUNDEDITOR_H
#define W_INBOUNDEDITOR_H
#include <QDialog>
#include <QJsonObject>
#include <QListWidgetItem>
namespace Ui
{
class InboundEditor;
}
class InboundEditor : public QDialog
{
Q_OBJECT
public:
explicit InboundEditor(QJsonObject root, QWidget *parent = nullptr);
~InboundEditor();
QJsonObject OpenEditor();
private slots:
void on_inboundProtocolCombo_currentIndexChanged(const QString &arg1);
void on_inboundProtocolCombo_currentIndexChanged(int index);
void on_inboundTagTxt_textEdited(const QString &arg1);
void on_httpTimeoutSpinBox_valueChanged(int arg1);
void on_httpTransparentCB_stateChanged(int arg1);
void on_httpUserLevelSB_valueChanged(int arg1);
void on_httpRemoveUserBtn_clicked();
void on_httpAddUserBtn_clicked();
void on_strategyCombo_currentIndexChanged(const QString &arg1);
void on_refreshNumberBox_valueChanged(int arg1);
void on_concurrencyNumberBox_valueChanged(int arg1);
void on_enableSniffingCB_stateChanged(int arg1);
void on_destOverrideList_itemChanged(QListWidgetItem *item);
void on_socksUDPCB_stateChanged(int arg1);
void on_socksUDPIPAddrTxt_textEdited(const QString &arg1);
void on_socksUserLevelSB_valueChanged(int arg1);
void on_socksRemoveUserBtn_clicked();
void on_socksAddUserBtn_clicked();
void on_dokoIPAddrTxt_textEdited(const QString &arg1);
void on_dokoPortSB_valueChanged(int arg1);
void on_dokoTCPCB_stateChanged(int arg1);
void on_dokoUDPCB_stateChanged(int arg1);
void on_dokoTimeoutSB_valueChanged(int arg1);
void on_dokoFollowRedirectCB_stateChanged(int arg1);
void on_dokoUserLevelSB_valueChanged(int arg1);
void on_mtEMailTxt_textEdited(const QString &arg1);
void on_mtSecretTxt_textEdited(const QString &arg1);
void on_mtUserLevelSB_valueChanged(int arg1);
void on_inboundHostTxt_textEdited(const QString &arg1);
void on_inboundPortTxt_textEdited(const QString &arg1);
void on_socksAuthCombo_currentIndexChanged(const QString &arg1);
private:
QJsonObject GenerateNewRoot();
void LoadUIData();
Ui::InboundEditor *ui;
QJsonObject original;
QJsonObject root;
//
QJsonObject httpSettings;
QJsonObject socksSettings;
QJsonObject mtSettings;
QJsonObject dokoSettings;
//
QJsonObject sniffing;
QJsonObject allocate;
};
#endif // W_INBOUNDEDITOR_H

846
src/ui/w_InboundEditor.ui Normal file
View File

@ -0,0 +1,846 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>InboundEditor</class>
<widget class="QDialog" name="InboundEditor">
<property name="windowModality">
<enum>Qt::ApplicationModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>815</width>
<height>500</height>
</rect>
</property>
<property name="windowTitle">
<string>Inbound Editor</string>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QGridLayout" name="gridLayout" columnstretch="7,3">
<item row="2" column="0">
<layout class="QFormLayout" name="formLayout_4">
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Tag</string>
</property>
<property name="buddy">
<cstring>inboundTagTxt</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="inboundTagTxt">
<property name="placeholderText">
<string>Tag of this inbound entry</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Listening</string>
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="2,0,1">
<item>
<widget class="QLineEdit" name="inboundHostTxt">
<property name="placeholderText">
<string>Hostname or IP Address</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="inboundPortTxt">
<property name="placeholderText">
<string>Port: 1080|80-85</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Protocol</string>
</property>
<property name="buddy">
<cstring>inboundProtocolCombo</cstring>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="inboundProtocolCombo">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item>
<property name="text">
<string notr="true">http</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">socks</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">dokodemo-door</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">mtproto</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item row="2" column="1">
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Allocation Settings</string>
</property>
<layout class="QFormLayout" name="formLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Strategy</string>
</property>
<property name="buddy">
<cstring>strategyCombo</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="strategyCombo">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item>
<property name="text">
<string>always</string>
</property>
</item>
<item>
<property name="text">
<string>random</string>
</property>
</item>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Refresh</string>
</property>
<property name="buddy">
<cstring>refreshNumberBox</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="refreshNumberBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimum">
<number>2</number>
</property>
<property name="value">
<number>5</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Concurrency</string>
</property>
<property name="buddy">
<cstring>concurrencyNumberBox</cstring>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="concurrencyNumberBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="value">
<number>3</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="4" column="1">
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Sniffing Settings</string>
</property>
<layout class="QFormLayout" name="formLayout_2">
<item row="1" column="0">
<widget class="QLabel" name="label_5">
<property name="minimumSize">
<size>
<width>0</width>
<height>32</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>80</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Destination Override</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="scaledContents">
<bool>false</bool>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="buddy">
<cstring>destOverrideList</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QListWidget" name="destOverrideList">
<item>
<property name="text">
<string notr="true">HTTP</string>
</property>
<property name="checkState">
<enum>Unchecked</enum>
</property>
</item>
<item>
<property name="text">
<string notr="true">TLS</string>
</property>
<property name="checkState">
<enum>Unchecked</enum>
</property>
</item>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="enableSniffingCB">
<property name="text">
<string>Enabled</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_29">
<property name="text">
<string>Enabled</string>
</property>
<property name="buddy">
<cstring>enableSniffingCB</cstring>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="5" column="1">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QStackedWidget" name="stackedWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="HTTPPage">
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>HTTP Inbound Settings</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="4,6">
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_13">
<property name="text">
<string>Timeout</string>
</property>
<property name="buddy">
<cstring>httpTimeoutSpinBox</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="httpTimeoutSpinBox"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Allow Transparent</string>
</property>
<property name="buddy">
<cstring>httpTransparentCB</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="httpTransparentCB">
<property name="text">
<string>Enabled</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_15">
<property name="text">
<string>User Level</string>
</property>
<property name="buddy">
<cstring>httpUserLevelSB</cstring>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="httpUserLevelSB"/>
</item>
</layout>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_3" rowstretch="0,1,1,0">
<item row="1" column="3">
<widget class="QToolButton" name="httpRemoveUserBtn">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../resources.qrc">
<normaloff>:/icons/remove_connection_btn.png</normaloff>:/icons/remove_connection_btn.png</iconset>
</property>
</widget>
</item>
<item row="1" column="0" rowspan="2" colspan="3">
<widget class="QListWidget" name="httpAccountListBox"/>
</item>
<item row="3" column="2">
<widget class="QLineEdit" name="httpAddPasswordTxt">
<property name="placeholderText">
<string>Password</string>
</property>
</widget>
</item>
<item row="3" column="3">
<widget class="QToolButton" name="httpAddUserBtn">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../resources.qrc">
<normaloff>:/icons/add_connection_btn.png</normaloff>:/icons/add_connection_btn.png</iconset>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_16">
<property name="text">
<string>Add</string>
</property>
<property name="buddy">
<cstring>httpAddUserTxt</cstring>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="httpAddUserTxt">
<property name="placeholderText">
<string>Username</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="5">
<widget class="QLabel" name="label_14">
<property name="text">
<string>Accounts</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="SOCKSPage">
<layout class="QGridLayout" name="gridLayout_5">
<item row="0" column="0">
<widget class="QGroupBox" name="groupBox_4">
<property name="title">
<string>SOCKS Inbound Settings</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3" stretch="4,6">
<item>
<layout class="QFormLayout" name="formLayout_5">
<item row="0" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Auth</string>
</property>
<property name="buddy">
<cstring>socksAuthCombo</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="socksAuthCombo">
<item>
<property name="text">
<string notr="true">noauth</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">password</string>
</property>
</item>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_20">
<property name="text">
<string>Enable UDP</string>
</property>
<property name="buddy">
<cstring>socksUDPCB</cstring>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_19">
<property name="text">
<string>Local UDP IP</string>
</property>
<property name="buddy">
<cstring>socksUDPIPAddrTxt</cstring>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_21">
<property name="text">
<string>User Level</string>
</property>
<property name="buddy">
<cstring>socksUserLevelSB</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="socksUDPCB">
<property name="text">
<string>Enabled</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="socksUDPIPAddrTxt">
<property name="placeholderText">
<string>127.0.0.1</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="socksUserLevelSB"/>
</item>
</layout>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_4" rowstretch="0,1,1,0">
<item row="1" column="3">
<widget class="QToolButton" name="socksRemoveUserBtn">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../resources.qrc">
<normaloff>:/icons/remove_connection_btn.png</normaloff>:/icons/remove_connection_btn.png</iconset>
</property>
</widget>
</item>
<item row="1" column="0" rowspan="2" colspan="3">
<widget class="QListWidget" name="socksAccountListBox"/>
</item>
<item row="3" column="2">
<widget class="QLineEdit" name="socksAddPasswordTxt">
<property name="placeholderText">
<string>Password</string>
</property>
</widget>
</item>
<item row="3" column="3">
<widget class="QToolButton" name="socksAddUserBtn">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../resources.qrc">
<normaloff>:/icons/add_connection_btn.png</normaloff>:/icons/add_connection_btn.png</iconset>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_17">
<property name="text">
<string>Add</string>
</property>
<property name="buddy">
<cstring>socksAddUserTxt</cstring>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="socksAddUserTxt">
<property name="placeholderText">
<string>Username</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="5">
<widget class="QLabel" name="label_18">
<property name="text">
<string>Accounts</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="DokodemoPage">
<layout class="QGridLayout" name="gridLayout_6">
<item row="0" column="0">
<widget class="QGroupBox" name="groupBox_5">
<property name="title">
<string>Dokodemo-Door Inbound Settings</string>
</property>
<layout class="QFormLayout" name="formLayout_6">
<item row="0" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>IP Address</string>
</property>
<property name="buddy">
<cstring>dokoIPAddrTxt</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="dokoIPAddrTxt">
<property name="placeholderText">
<string>Not necessary when setting &quot;Follow Redirect&quot;</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_22">
<property name="text">
<string>Port</string>
</property>
<property name="buddy">
<cstring>dokoPortSB</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="dokoPortSB">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>65535</number>
</property>
<property name="value">
<number>10086</number>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_23">
<property name="text">
<string>Network</string>
</property>
<property name="buddy">
<cstring>dokoTCPCB</cstring>
</property>
</widget>
</item>
<item row="3" column="1">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QCheckBox" name="dokoTCPCB">
<property name="text">
<string>TCP</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="dokoUDPCB">
<property name="text">
<string>UDP</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_24">
<property name="text">
<string>Timeout</string>
</property>
<property name="buddy">
<cstring>dokoTimeoutSB</cstring>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_25">
<property name="text">
<string>Follow Redirect</string>
</property>
<property name="buddy">
<cstring>dokoFollowRedirectCB</cstring>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_26">
<property name="text">
<string>User Level</string>
</property>
<property name="buddy">
<cstring>dokoUserLevelSB</cstring>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QSpinBox" name="dokoTimeoutSB">
<property name="minimum">
<number>-1</number>
</property>
<property name="maximum">
<number>8192</number>
</property>
<property name="value">
<number>300</number>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QCheckBox" name="dokoFollowRedirectCB">
<property name="text">
<string>Enabled</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QSpinBox" name="dokoUserLevelSB"/>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="MTProtoPage">
<layout class="QGridLayout" name="gridLayout_7">
<item row="0" column="0">
<widget class="QGroupBox" name="groupBox_6">
<property name="title">
<string>MTProto Inbound Settings</string>
</property>
<layout class="QFormLayout" name="formLayout_7">
<item row="0" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>EMail Address</string>
</property>
<property name="buddy">
<cstring>mtEMailTxt</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="mtEMailTxt">
<property name="placeholderText">
<string notr="true">your.name@domain.com</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_27">
<property name="text">
<string>Secret</string>
</property>
<property name="buddy">
<cstring>mtSecretTxt</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="mtSecretTxt">
<property name="placeholderText">
<string>SECRET</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_28">
<property name="text">
<string>User Level</string>
</property>
<property name="buddy">
<cstring>mtUserLevelSB</cstring>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="mtUserLevelSB"/>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>inboundTagTxt</tabstop>
<tabstop>inboundHostTxt</tabstop>
<tabstop>inboundPortTxt</tabstop>
<tabstop>inboundProtocolCombo</tabstop>
<tabstop>httpTimeoutSpinBox</tabstop>
<tabstop>httpTransparentCB</tabstop>
<tabstop>httpUserLevelSB</tabstop>
<tabstop>httpAccountListBox</tabstop>
<tabstop>httpRemoveUserBtn</tabstop>
<tabstop>httpAddUserTxt</tabstop>
<tabstop>httpAddPasswordTxt</tabstop>
<tabstop>httpAddUserBtn</tabstop>
<tabstop>socksAuthCombo</tabstop>
<tabstop>socksUDPCB</tabstop>
<tabstop>socksUDPIPAddrTxt</tabstop>
<tabstop>socksUserLevelSB</tabstop>
<tabstop>socksAccountListBox</tabstop>
<tabstop>socksRemoveUserBtn</tabstop>
<tabstop>socksAddUserTxt</tabstop>
<tabstop>socksAddPasswordTxt</tabstop>
<tabstop>socksAddUserBtn</tabstop>
<tabstop>dokoIPAddrTxt</tabstop>
<tabstop>dokoPortSB</tabstop>
<tabstop>dokoTCPCB</tabstop>
<tabstop>dokoUDPCB</tabstop>
<tabstop>dokoTimeoutSB</tabstop>
<tabstop>dokoFollowRedirectCB</tabstop>
<tabstop>dokoUserLevelSB</tabstop>
<tabstop>mtEMailTxt</tabstop>
<tabstop>mtSecretTxt</tabstop>
<tabstop>mtUserLevelSB</tabstop>
<tabstop>strategyCombo</tabstop>
<tabstop>refreshNumberBox</tabstop>
<tabstop>concurrencyNumberBox</tabstop>
<tabstop>enableSniffingCB</tabstop>
<tabstop>destOverrideList</tabstop>
</tabstops>
<resources>
<include location="../../resources.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>InboundEditor</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>841</x>
<y>493</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>InboundEditor</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>841</x>
<y>493</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -3,24 +3,24 @@
#include "QvUtils.h"
JsonEditor::JsonEditor(QJsonObject document, QWidget *parent) :
JsonEditor::JsonEditor(QJsonObject rootObject, QWidget *parent) :
QDialog(parent),
ui(new Ui::JsonEditor)
{
ui->setupUi(this);
original = document;
result = document;
QString jsonString = JsonToString(document);
original = rootObject;
final = rootObject;
QString jsonString = JsonToString(rootObject);
if (VerifyJsonString(&jsonString).isEmpty()) {
LOG(MODULE_UI, "Begin loading Json Model")
ui->jsonTree->setModel(&model);
model.loadJson(QJsonDocument(document).toJson());
model.loadJson(QJsonDocument(rootObject).toJson());
} else {
QvMessageBox(this, tr("Json Contains Syntax Errors"), tr("Original Json may contain syntax errors. Json tree is disabled."));
}
ui->jsonEditor->setText(JsonToString(document));
ui->jsonEditor->setText(JsonToString(rootObject));
ui->jsonTree->expandAll();
ui->jsonTree->resizeColumnToContents(0);
}
@ -36,7 +36,7 @@ QJsonObject JsonEditor::OpenEditor()
string = ui->jsonEditor->toPlainText();
}
return resultCode == QDialog::Accepted ? result : original;
return resultCode == QDialog::Accepted ? final : original;
}
JsonEditor::~JsonEditor()
@ -52,8 +52,8 @@ void JsonEditor::on_jsonEditor_textChanged()
if (VerifyResult.isEmpty()) {
BLACK(jsonEditor)
result = JsonFromString(string);
model.loadJson(QJsonDocument(result).toJson());
final = JsonFromString(string);
model.loadJson(QJsonDocument(final).toJson());
ui->jsonTree->expandAll();
ui->jsonTree->resizeColumnToContents(0);
} else {

View File

@ -15,7 +15,7 @@ class JsonEditor : public QDialog
Q_OBJECT
public:
explicit JsonEditor(QJsonObject document, QWidget *parent = nullptr);
explicit JsonEditor(QJsonObject rootObject, QWidget *parent = nullptr);
~JsonEditor();
QJsonObject OpenEditor();
@ -27,7 +27,7 @@ class JsonEditor : public QDialog
private:
QJsonModel model;
QJsonObject original;
QJsonObject result;
QJsonObject final;
Ui::JsonEditor *ui;
};

View File

@ -19,9 +19,13 @@
<property name="modal">
<bool>true</bool>
</property>
<layout class="QGridLayout" name="gridLayout" columnstretch="11,9">
<item row="1" column="0">
<widget class="QTextEdit" name="jsonEditor"/>
<layout class="QGridLayout" name="gridLayout" columnstretch="6,5">
<item row="3" column="0">
<widget class="QLabel" name="jsonValidateStatus">
<property name="text">
<string>OK</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_2">
@ -30,20 +34,6 @@
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Json Editor</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="jsonValidateStatus">
<property name="text">
<string>OK</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
@ -54,6 +44,9 @@
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QTextEdit" name="jsonEditor"/>
</item>
<item row="2" column="0">
<widget class="QPushButton" name="formatJsonBtn">
<property name="text">
@ -61,6 +54,13 @@
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Json Editor</string>
</property>
</widget>
</item>
<item row="1" column="1" rowspan="2">
<widget class="QTreeView" name="jsonTree">
<property name="sizeAdjustPolicy">

View File

@ -12,27 +12,26 @@
#include <QVersionNumber>
#include <QKeyEvent>
#ifdef _WIN32
#include <Windows.h>
#endif
#include "w_ConnectionEditWindow.h"
#include "w_OutboundEditor.h"
#include "w_ImportConfig.h"
#include "w_MainWindow.h"
#include "w_RouteEditor.h"
#include "w_RoutesEditor.h"
#include "w_PrefrencesWindow.h"
#include "w_SubscribeEditor.h"
#include "w_SubscriptionEditor.h"
#include "w_JsonEditor.h"
#include "QvNetSpeedPlugin.h"
#define TRAY_TOOLTIP_PREFIX "Qv2ray " QV2RAY_VERSION_STRING
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent),
ui(new Ui::MainWindow),
HTTPRequestHelper(),
hTray(new QSystemTrayIcon(this)),
vinstance(),
speedTimer(this)
ui(new Ui::MainWindow),
uploadList(),
downloadList(),
HTTPRequestHelper(),
hTray(new QSystemTrayIcon(this))
{
vinstance = new Qv2Instance(this);
ui->setupUi(this);
@ -72,12 +71,15 @@ MainWindow::MainWindow(QWidget *parent)
connect(action_RCM_RenameConnection, &QAction::triggered, this, &MainWindow::on_action_RenameConnection_triggered);
connect(action_RCM_StartThis, &QAction::triggered, this, &MainWindow::on_action_StartThis_triggered);
connect(action_RCM_EditJson, &QAction::triggered, this, &MainWindow::on_action_RCM_EditJson_triggered);
connect(&speedTimer, &QTimer::timeout, this, &MainWindow::on_speedTimer_Ticked);
// TODO: UNCOMMENT THIS....
LOG(MODULE_UI, "SHARE OPTION TODO...")
//connect(action_RCM_ShareLink, &QAction::triggered, this, &MainWindow::on_action_RCM_ShareLink_triggered);
//connect(action_RCM_ShareQR, &QAction::triggered, this, &MainWindow::on_action_RCM_ShareQR_triggered);
//
connect(this, &MainWindow::Connect, this, &MainWindow::on_startButton_clicked);
connect(this, &MainWindow::DisConnect, this, &MainWindow::on_stopButton_clicked);
connect(this, &MainWindow::ReConnect, this, &MainWindow::on_reconnectButton_clicked);
//
hTray->setContextMenu(trayMenu);
hTray->show();
//
@ -90,11 +92,41 @@ MainWindow::MainWindow(QWidget *parent)
LoadConnections();
QObject::connect(&HTTPRequestHelper, &QvHttpRequestHelper::httpRequestFinished, this, &MainWindow::VersionUpdate);
HTTPRequestHelper.get("https://api.github.com/repos/lhy0403/Qv2ray/releases/latest");
bool hasAutoStart = false;
//
if (!vinstance->ValidateKernal()) {
QDesktopServices::openUrl(QUrl::fromLocalFile(QV2RAY_V2RAY_CORE_DIR_PATH));
} else {
uploadSerie = new QSplineSeries(this);
downloadSerie = new QSplineSeries(this);
uploadSerie->setName("Upload");
downloadSerie->setName("Download");
for (int i = 0; i < 30 ; i++) {
uploadList.append(0);
downloadList.append(0);
uploadSerie->append(i, 0);
downloadSerie->append(i, 0);
}
speedChart = new QChart();
speedChartView = new QChartView(speedChart, this);
speedChartView->setRenderHint(QPainter::RenderHint::HighQualityAntialiasing, true);
speedChart->setTitle("Qv2ray Speed Chart");
speedChart->legend()->hide();
speedChart->createDefaultAxes();
speedChart->addSeries(uploadSerie);
speedChart->addSeries(downloadSerie);
speedChart->createDefaultAxes();
speedChart->axes(Qt::Vertical).first()->setRange(0, 512);
static_cast<QValueAxis>(speedChart->axes(Qt::Horizontal).first()).setLabelFormat("dd.dd");
speedChart->axes(Qt::Horizontal).first()->setRange(0, 30);
speedChart->setContentsMargins(-20, -45, -20, -25);
auto layout = new QHBoxLayout(ui->speedChart);
layout->addWidget(speedChartView);
ui->speedChart->setLayout(layout);
ui->speedChart->layout()->addWidget(speedChartView);
//
//
if (vinstance->ValidateKernal()) {
auto conf = GetGlobalConfig();
if (conf.autoStartConfig != "" && QList<string>::fromStdList(conf.configs).contains(conf.autoStartConfig)) {
@ -104,12 +136,9 @@ MainWindow::MainWindow(QWidget *parent)
ui->connectionListWidget->setCurrentItem(item);
on_connectionListWidget_itemClicked(item);
on_startButton_clicked();
//ToggleVisibility();
this->hide();
hasAutoStart = true;
trayMenu->actions()[0]->setText(tr("Show"));
} else {
this->show();
if (ui->connectionListWidget->count() != 0) {
// The first one is default.
ui->connectionListWidget->setCurrentRow(0);
@ -117,10 +146,16 @@ MainWindow::MainWindow(QWidget *parent)
}
}
}
if (hasAutoStart) {
this->hide();
} else {
this->show();
}
Utils::NetSpeedPlugin::StartProcessingPlugins(this);
}
void MainWindow::on_action_StartThis_triggered()
{
if (ui->connectionListWidget->selectedItems().empty()) {
@ -216,45 +251,55 @@ void MainWindow::UpdateLog()
void MainWindow::on_startButton_clicked()
{
if (CurrentConnectionName == "") {
QvMessageBox(this, tr("No connection selected!"), tr("Please select a config from the list."));
return;
}
LOG(MODULE_VCORE, ("Connecting to: " + CurrentConnectionName).toStdString())
ui->logText->clear();
CurrentFullConfig = GenerateRuntimeConfig(connections[CurrentConnectionName]);
StartPreparation(CurrentFullConfig);
bool startFlag = this->vinstance->StartVCore();
if (startFlag) {
this->hTray->showMessage("Qv2ray", tr("Connected To Server: ") + CurrentConnectionName);
hTray->setToolTip(TRAY_TOOLTIP_PREFIX "\r\n" + tr("Connected To Server: ") + CurrentConnectionName);
ui->statusLabel->setText(tr("Connected: ") + CurrentConnectionName);
if (GetGlobalConfig().enableStats) {
vinstance->SetAPIPort(GetGlobalConfig().statsPort);
speedTimer.start(1000);
if (vinstance->VCoreStatus != STARTED) {
// Reset the graph
for (int i = 0; i < 30 ; i++) {
uploadList[i] = 0;
downloadList[i] = 0;
uploadSerie->replace(i, 0, 0);
downloadSerie->replace(i, 0, 0);
}
}
trayMenu->actions()[2]->setEnabled(!startFlag);
trayMenu->actions()[3]->setEnabled(startFlag);
trayMenu->actions()[4]->setEnabled(startFlag);
//
ui->startButton->setEnabled(!startFlag);
ui->stopButton->setEnabled(startFlag);
if (CurrentConnectionName == "") {
QvMessageBox(this, tr("No connection selected!"), tr("Please select a config from the list."));
return;
}
LOG(MODULE_VCORE, ("Connecting to: " + CurrentConnectionName).toStdString())
ui->logText->clear();
CurrentFullConfig = GenerateRuntimeConfig(connections[CurrentConnectionName]);
StartPreparation(CurrentFullConfig);
bool startFlag = this->vinstance->StartVCore();
if (startFlag) {
this->hTray->showMessage("Qv2ray", tr("Connected To Server: ") + CurrentConnectionName);
hTray->setToolTip(TRAY_TOOLTIP_PREFIX "\r\n" + tr("Connected To Server: ") + CurrentConnectionName);
ui->statusLabel->setText(tr("Connected") + ": " + CurrentConnectionName);
if (GetGlobalConfig().enableStats) {
vinstance->SetAPIPort(GetGlobalConfig().statsPort);
speedTimerId = startTimer(1000);
}
}
trayMenu->actions()[2]->setEnabled(!startFlag);
trayMenu->actions()[3]->setEnabled(startFlag);
trayMenu->actions()[4]->setEnabled(startFlag);
//
ui->startButton->setEnabled(!startFlag);
ui->stopButton->setEnabled(startFlag);
}
}
void MainWindow::on_stopButton_clicked()
{
if (vinstance->VCoreStatus != STOPPED) {
this->vinstance->StopVCore();
speedTimer.stop();
killTimer(speedTimerId);
hTray->setToolTip(TRAY_TOOLTIP_PREFIX);
QFile(QV2RAY_GENERATED_FILE_PATH).remove();
ui->statusLabel->setText(tr("Disconnected"));
ui->logText->clear();
ui->logText->setText("");
trayMenu->actions()[2]->setEnabled(true);
trayMenu->actions()[3]->setEnabled(false);
trayMenu->actions()[4]->setEnabled(false);
@ -262,10 +307,8 @@ void MainWindow::on_stopButton_clicked()
ui->startButton->setEnabled(true);
ui->stopButton->setEnabled(false);
//
ui->speedUpLabel->setText("");
ui->speedDownLabel->setText("");
ui->totalDataUpLabel->setText("");
ui->totalDataDownLabel->setText("");
ui->netspeedLabel->setText("0.00 B/s\r\n0.00 B/s");
ui->dataamountLabel->setText("0.00 B\r\n0.00 B");
}
}
@ -326,6 +369,7 @@ void MainWindow::ToggleVisibility()
void MainWindow::quit()
{
Utils::NetSpeedPlugin::StopProcessingPlugins();
on_stopButton_clicked();
QApplication::quit();
}
@ -359,29 +403,16 @@ void MainWindow::ShowAndSetConnection(QString guiConnectionName, bool SetConnect
auto Server = StructFromJsonString<VMessServerObject>(JsonToString(outBoundRoot["settings"].toObject()["vnext"].toArray().first().toObject()));
ui->_hostLabel->setText(QSTRING(Server.address));
ui->_portLabel->setText(QSTRING(to_string(Server.port)));
auto user = QList<VMessServerObject::UserObject>::fromStdList(Server.users).first();
auto _configString = tr("UUID") + ": " + QSTRING(user.id)
+ "\r\n"
+ tr("AlterID") + ": " + QSTRING(to_string(user.alterId))
+ "\r\n"
+ tr("Transport") + ": " + outBoundRoot["streamSettings"].toObject()["network"].toString();
ui->detailInfoTxt->setPlainText(_configString);
} else if (outboundType == "shadowsocks") {
auto x = JsonToString(outBoundRoot["settings"].toObject()["servers"].toArray().first().toObject());
auto Server = StructFromJsonString<ShadowSocksServer>(x);
ui->_hostLabel->setText(QSTRING(Server.address));
ui->_portLabel->setText(QSTRING(to_string(Server.port)));
auto _configString = tr("Email") + ": " + QSTRING(Server.email)
+ "\r\n"
+ tr("Encryption") + ": " + QSTRING(Server.method);
ui->detailInfoTxt->setPlainText(_configString);
} else if (outboundType == "socks") {
auto x = JsonToString(outBoundRoot["settings"].toObject()["servers"].toArray().first().toObject());
auto Server = StructFromJsonString<SocksServerObject>(x);
ui->_hostLabel->setText(QSTRING(Server.address));
ui->_portLabel->setText(QSTRING(to_string(Server.port)));
auto _configString = tr("Username") + ": " + QSTRING(Server.users.front().user);
ui->detailInfoTxt->setPlainText(_configString);
}
// --------- END Show Connection
@ -502,23 +533,27 @@ void MainWindow::on_connectionListWidget_itemChanged(QListWidgetItem *item)
void MainWindow::on_removeConfigButton_clicked()
{
if (ui->connectionListWidget->currentIndex().row() < 0) return;
if (QvMessageBoxAsk(this, tr("Removing this Connection"), tr("Are you sure to remove this connection?")) == QMessageBox::Yes) {
auto conf = GetGlobalConfig();
QList<string> list = QList<string>::fromStdList(conf.configs);
auto currentSelected = ui->connectionListWidget->currentIndex().row();
auto connectionName = ui->connectionListWidget->currentItem()->text();
if (currentSelected < 0) return;
bool isRemovingItemRunning = ui->connectionListWidget->item(currentSelected)->text() == CurrentConnectionName;
if (isRemovingItemRunning) {
CurrentConnectionName = "";
if (connectionName == CurrentConnectionName) {
on_stopButton_clicked();
CurrentConnectionName = "";
}
list.removeOne(ui->connectionListWidget->item(currentSelected)->text().toStdString());
auto conf = GetGlobalConfig();
QList<string> list = QList<string>::fromStdList(conf.configs);
list.removeOne(connectionName.toStdString());
conf.configs = list.toStdList();
if (!RemoveConnection(&connectionName)) {
QvMessageBox(this, tr("Removing this Connection"), tr("Failed to delete connection file, please delete manually."));
}
SetGlobalConfig(conf);
OnConfigListChanged(isRemovingItemRunning);
OnConfigListChanged(false);
ShowAndSetConnection(CurrentConnectionName, false, false);
}
}
@ -533,23 +568,28 @@ void MainWindow::on_importConfigButton_clicked()
void MainWindow::on_addConfigButton_clicked()
{
ConnectionEditWindow *w = new ConnectionEditWindow(this);
connect(w, &ConnectionEditWindow::s_reload_config, this, &MainWindow::OnConfigListChanged);
OutboundEditor *w = new OutboundEditor(this);
connect(w, &OutboundEditor::s_reload_config, this, &MainWindow::OnConfigListChanged);
auto outboundEntry = w->OpenEditor();
QJsonArray outboundsList;
outboundsList.push_back(outboundEntry);
QJsonObject root;
root.insert("outbounds", outboundsList);
bool isChanged = w->result() == QDialog::Accepted;
auto alias = w->Alias;
delete w;
auto conf = GetGlobalConfig();
auto connectionList = conf.configs;
connectionList.push_back(alias.toStdString());
conf.configs = connectionList;
SetGlobalConfig(conf);
OnConfigListChanged(false);
SaveConnectionConfig(root, &alias);
ShowAndSetConnection(CurrentConnectionName, false, false);
if (isChanged) {
QJsonArray outboundsList;
outboundsList.push_back(outboundEntry);
QJsonObject root;
root.insert("outbounds", outboundsList);
//
auto conf = GetGlobalConfig();
auto connectionList = conf.configs;
connectionList.push_back(alias.toStdString());
conf.configs = connectionList;
SetGlobalConfig(conf);
OnConfigListChanged(false);
SaveConnectionConfig(root, &alias);
ShowAndSetConnection(CurrentConnectionName, false, false);
}
}
void MainWindow::on_editConfigButton_clicked()
@ -563,24 +603,29 @@ void MainWindow::on_editConfigButton_clicked()
auto alias = ui->connectionListWidget->currentItem()->text();
auto outBoundRoot = connections[alias];
QJsonObject root;
bool isChanged = false;
if (outBoundRoot["outbounds"].toArray().count() > 1) {
LOG(MODULE_UI, "INFO: Opening route editor.")
RouteEditor *routeWindow = new RouteEditor(outBoundRoot, alias, this);
root = routeWindow->OpenEditor();
isChanged = routeWindow->result() == QDialog::Accepted;
} else {
LOG(MODULE_UI, "INFO: Opening single connection edit window.")
ConnectionEditWindow *w = new ConnectionEditWindow(outBoundRoot["outbounds"].toArray().first().toObject(), &alias, this);
OutboundEditor *w = new OutboundEditor(outBoundRoot["outbounds"].toArray().first().toObject(), &alias, this);
auto outboundEntry = w->OpenEditor();
isChanged = w->result() == QDialog::Accepted;
QJsonArray outboundsList;
outboundsList.push_back(outboundEntry);
root.insert("outbounds", outboundsList);
}
connections[alias] = root;
SaveConnectionConfig(root, &alias);
OnConfigListChanged(alias == CurrentConnectionName);
ShowAndSetConnection(CurrentConnectionName, false, false);
if (isChanged) {
connections[alias] = root;
SaveConnectionConfig(root, &alias);
OnConfigListChanged(alias == CurrentConnectionName);
ShowAndSetConnection(CurrentConnectionName, false, false);
}
}
void MainWindow::on_reconnectButton_clicked()
@ -600,14 +645,19 @@ void MainWindow::on_action_RCM_EditJson_triggered()
auto alias = ui->connectionListWidget->currentItem()->text();
JsonEditor *w = new JsonEditor(connections[alias], this);
auto root = w->OpenEditor();
bool isChanged = w->result() == QDialog::Accepted;
delete w;
connections[alias] = root;
SaveConnectionConfig(root, &alias);
ShowAndSetConnection(CurrentConnectionName, false, false);
if (isChanged) {
connections[alias] = root;
SaveConnectionConfig(root, &alias);
ShowAndSetConnection(CurrentConnectionName, false, false);
}
}
void MainWindow::on_editJsonBtn_clicked()
{
// See above.
on_action_RCM_EditJson_triggered();
}
@ -626,31 +676,56 @@ void MainWindow::on_shareVMessButton_clicked()
// Share vmess://
}
void MainWindow::on_speedTimer_Ticked()
void MainWindow::timerEvent(QTimerEvent *event)
{
Q_UNUSED(event)
auto inbounds = CurrentFullConfig["inbounds"].toArray();
long totalSpeedUp = 0, totalSpeedDown = 0, totalDataUp = 0, totalDataDown = 0;
long _totalSpeedUp = 0, _totalSpeedDown = 0, _totalDataUp = 0, _totalDataDown = 0;
foreach (auto inbound, inbounds) {
auto tag = inbound.toObject()["tag"].toString();
totalSpeedUp += vinstance->getTagLastUplink(tag);
totalSpeedDown += vinstance->getTagLastDownlink(tag);
totalDataUp += vinstance->getTagTotalUplink(tag);
totalDataDown += vinstance->getTagTotalDownlink(tag);
// TODO: A proper scheme...
if (tag == API_TAG_INBOUND) {
continue;
}
_totalSpeedUp += vinstance->getTagLastUplink(tag);
_totalSpeedDown += vinstance->getTagLastDownlink(tag);
_totalDataUp += vinstance->getTagTotalUplink(tag);
_totalDataDown += vinstance->getTagTotalDownlink(tag);
}
char s[32] = "";
auto speedUp = FormatBytes(totalSpeedUp, s);
auto speedDown = FormatBytes(totalSpeedDown, s);
auto dataUp = FormatBytes(totalDataUp, s);
auto dataDown = FormatBytes(totalDataDown, s);
double max = 0;
double historyMax = 0;
auto graphVUp = _totalSpeedUp / 1024;
auto graphVDown = _totalSpeedDown / 1024;
for (auto i = 0; i < 29; i++) {
historyMax = MAX(historyMax, MAX(uploadList[i + 1], downloadList[i + 1]));
uploadList[i] = uploadList[i + 1];
downloadList[i] = downloadList[i + 1];
uploadSerie->replace(i, i, uploadList[i + 1]);
downloadSerie->replace(i, i, downloadList[i + 1]);
}
uploadList[uploadList.count() - 1] = graphVUp;
downloadList[uploadList.count() - 1] = graphVDown;
uploadSerie->replace(29, 29, graphVUp);
downloadSerie->replace(29, 29, graphVDown);
//
ui->speedUpLabel->setText("Upload: " + speedUp + "/s");
ui->speedDownLabel->setText("Download: " + speedDown + "/s");
ui->totalDataUpLabel->setText("Total Up: " + dataUp);
ui->totalDataDownLabel->setText("Total Down: " + dataDown);
max = MAX(MAX(graphVUp, graphVDown), historyMax);
speedChart->axes(Qt::Vertical).first()->setRange(0, max * 1.2);
//
hTray->setToolTip(TRAY_TOOLTIP_PREFIX "\r\n" + tr("Connected To Server: ") + CurrentConnectionName + "\r\n"
"Up: " + speedUp + "/s Down: " + speedDown + "/s");
//
totalSpeedUp = FormatBytes(_totalSpeedUp);
totalSpeedDown = FormatBytes(_totalSpeedDown);
totalDataUp = FormatBytes(_totalDataUp);
totalDataDown = FormatBytes(_totalDataDown);
//
ui->netspeedLabel->setText(totalSpeedUp + "/s\r\n" + totalSpeedDown + "/s");
ui->dataamountLabel->setText(totalDataUp + "\r\n" + totalDataDown);
//
hTray->setToolTip(TRAY_TOOLTIP_PREFIX "\r\n" + tr("Connected To Server: ") + CurrentConnectionName + "\r\nUp: " + totalSpeedUp + "/s Down: " + totalSpeedDown + "/s");
}

View File

@ -3,6 +3,7 @@
#include <QMainWindow>
#include <QMenu>
#include <QScrollBar>
#include <QtCharts>
#include <QSystemTrayIcon>
#include "QvUtils.h"
@ -11,6 +12,7 @@
#include "QvHTTPRequestHelper.h"
#include "ui_w_MainWindow.h"
namespace Ui
{
class MainWindow;
@ -22,14 +24,18 @@ class MainWindow : public QMainWindow
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
signals:
void Connect();
void DisConnect();
void ReConnect();
public slots:
void UpdateLog();
void OnConfigListChanged(bool need_restart);
private slots:
void on_speedTimer_Ticked();
void VersionUpdate(QByteArray &data);
void on_startButton_clicked();
void on_stopButton_clicked();
void on_reconnectButton_clicked();
void VersionUpdate(QByteArray &data);
void on_activatedTray(QSystemTrayIcon::ActivationReason reason);
void ToggleVisibility();
void quit();
@ -58,8 +64,6 @@ class MainWindow : public QMainWindow
void on_editConfigButton_clicked();
void on_reconnectButton_clicked();
void on_editJsonBtn_clicked();
void on_pingTestBtn_clicked();
@ -67,25 +71,41 @@ class MainWindow : public QMainWindow
void on_shareQRButton_clicked();
void on_shareVMessButton_clicked();
public:
QJsonObject CurrentFullConfig;
QString CurrentConnectionName = "";
Qv2Instance *vinstance;
QString totalDataUp;
QString totalDataDown;
QString totalSpeedUp;
QString totalSpeedDown;
protected:
void timerEvent(QTimerEvent *event);
private:
Ui::MainWindow *ui;
//
QChartView *speedChartView;
QChart *speedChart;
QSplineSeries *uploadSerie;
QSplineSeries *downloadSerie;
QList<double> uploadList;
QList<double> downloadList;
//
void on_action_StartThis_triggered();
void on_action_RCM_EditJson_triggered();
void on_action_RenameConnection_triggered();
Ui::MainWindow *ui;
QvHttpRequestHelper HTTPRequestHelper;
QSystemTrayIcon *hTray;
QMenu *trayMenu = new QMenu(this);
Qv2Instance *vinstance;
QMenu listMenu;
QMap<QString, QJsonObject> connections;
QString CurrentConnectionName;
//
QString originalName;
bool isRenamingInProgress;
//
QTimer speedTimer;
QJsonObject CurrentFullConfig;
int speedTimerId;
//
void ShowAndSetConnection(QString currentText, bool SetConnection, bool Apply);
void LoadConnections();

View File

@ -10,7 +10,7 @@
<x>0</x>
<y>0</y>
<width>680</width>
<height>555</height>
<height>585</height>
</rect>
</property>
<property name="sizePolicy">
@ -19,6 +19,12 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>680</width>
<height>585</height>
</size>
</property>
<property name="windowTitle">
<string>Qv2ray</string>
</property>
@ -35,144 +41,7 @@
<height>530</height>
</size>
</property>
<layout class="QGridLayout" name="gridLayout" columnstretch="4,5">
<item row="4" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Log</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QListWidget" name="connectionListWidget">
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<property name="lineWidth">
<number>1</number>
</property>
<property name="midLineWidth">
<number>0</number>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAsNeeded</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAsNeeded</enum>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="defaultDropAction">
<enum>Qt::CopyAction</enum>
</property>
<property name="verticalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
<property name="horizontalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
<property name="viewMode">
<enum>QListView::ListMode</enum>
</property>
</widget>
</item>
<item row="3" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QToolButton" name="addConfigButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>#AddConnection</string>
</property>
<property name="text">
<string>A</string>
</property>
<property name="icon">
<iconset resource="../../resources.qrc">
<normaloff>:/icons/add_connection_btn.png</normaloff>:/icons/add_connection_btn.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="importConfigButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>#ImportConnection</string>
</property>
<property name="text">
<string>I</string>
</property>
<property name="icon">
<iconset resource="../../resources.qrc">
<normaloff>:/icons/import_connection_btn.png</normaloff>:/icons/import_connection_btn.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="removeConfigButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>#RemoveConnection</string>
</property>
<property name="text">
<string>R</string>
</property>
<property name="icon">
<iconset resource="../../resources.qrc">
<normaloff>:/icons/remove_connection_btn.png</normaloff>:/icons/remove_connection_btn.png</iconset>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<layout class="QGridLayout" name="gridLayout_2" rowstretch="0,5,4,0" columnstretch="4,3">
<item row="0" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,1,0,1,0,0,0">
<property name="spacing">
@ -241,72 +110,252 @@
</item>
</layout>
</item>
<item row="1" column="1" rowspan="3">
<item row="1" column="0">
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QLabel" name="label_14">
<property name="text">
<string>Host List</string>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="connectionListWidget">
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<property name="lineWidth">
<number>1</number>
</property>
<property name="midLineWidth">
<number>0</number>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAsNeeded</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAsNeeded</enum>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="defaultDropAction">
<enum>Qt::CopyAction</enum>
</property>
<property name="verticalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
<property name="horizontalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
<property name="viewMode">
<enum>QListView::ListMode</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QToolButton" name="addConfigButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>#AddConnection</string>
</property>
<property name="text">
<string>A</string>
</property>
<property name="icon">
<iconset resource="../../resources.qrc">
<normaloff>:/icons/add_connection_btn.png</normaloff>:/icons/add_connection_btn.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="importConfigButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>#ImportConnection</string>
</property>
<property name="text">
<string>I</string>
</property>
<property name="icon">
<iconset resource="../../resources.qrc">
<normaloff>:/icons/import_connection_btn.png</normaloff>:/icons/import_connection_btn.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="removeConfigButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>#RemoveConnection</string>
</property>
<property name="text">
<string>R</string>
</property>
<property name="icon">
<iconset resource="../../resources.qrc">
<normaloff>:/icons/remove_connection_btn.png</normaloff>:/icons/remove_connection_btn.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="editConfigButton">
<property name="toolTip">
<string>#EditConnection</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../resources.qrc">
<normaloff>:/icons/edit_connection_btn.png</normaloff>:/icons/edit_connection_btn.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="editJsonBtn">
<property name="text">
<string>{ }</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
<item row="1" column="1">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Config Details</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Type</string>
</property>
</widget>
<layout class="QVBoxLayout" name="verticalLayout_2" stretch="1,0">
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Type</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="_OutBoundTypeLabel">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Address</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="_hostLabel">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Port Number</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="_portLabel">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Routes Count</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="label_7">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Mux</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="label_9">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="1">
<widget class="QLabel" name="_OutBoundTypeLabel">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Host</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="_hostLabel">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Port</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="_portLabel">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Detail</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QTextBrowser" name="detailInfoTxt">
<property name="lineWrapMode">
<enum>QTextEdit::NoWrap</enum>
</property>
</widget>
</item>
<item row="4" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<spacer name="horizontalSpacer_5">
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
@ -318,20 +367,6 @@
</property>
</spacer>
</item>
<item>
<widget class="QToolButton" name="editConfigButton">
<property name="toolTip">
<string>#EditConnection</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../resources.qrc">
<normaloff>:/icons/edit_connection_btn.png</normaloff>:/icons/edit_connection_btn.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="pingTestBtn">
<property name="text">
@ -342,26 +377,19 @@
<item>
<widget class="QToolButton" name="shareQRButton">
<property name="text">
<string>QR</string>
<string>QR Code</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="shareVMessButton">
<property name="text">
<string>://</string>
<string>VMess</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="editJsonBtn">
<property name="text">
<string>Json</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<spacer name="horizontalSpacer_9">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
@ -378,56 +406,180 @@
</layout>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Host List</string>
</property>
</widget>
<item row="2" column="0" colspan="2">
<layout class="QGridLayout" name="gridLayout" columnstretch="5,2">
<item row="0" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Log</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QTextBrowser" name="logText">
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOn</enum>
</property>
<property name="lineWrapMode">
<enum>QTextEdit::NoWrap</enum>
</property>
<property name="openLinks">
<bool>false</bool>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Speed Graph</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QWidget" name="speedChart" native="true">
<property name="minimumSize">
<size>
<width>300</width>
<height>0</height>
</size>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
</widget>
</item>
</layout>
</item>
<item row="5" column="0" colspan="2">
<widget class="QTextBrowser" name="logText">
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOn</enum>
</property>
<property name="lineWrapMode">
<enum>QTextEdit::NoWrap</enum>
</property>
<property name="openLinks">
<bool>false</bool>
</property>
</widget>
</item>
<item row="6" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item row="3" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="speedUpLabel">
<widget class="QLabel" name="label_5">
<property name="text">
<string/>
<string>Speed</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="speedDownLabel">
<property name="text">
<string/>
<widget class="QFrame" name="updownImageBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>32</horstretch>
<verstretch>32</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>25</width>
<height>25</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">border-image: url(:/icons/netspeed-arrows.svg) 0 0 0 0 stretch stretch;background-repeat: no-repeat;</string>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="totalDataUpLabel">
<widget class="QLabel" name="netspeedLabel">
<property name="font">
<font>
<pointsize>8</pointsize>
</font>
</property>
<property name="text">
<string/>
<string>0.00 B/s
0.00 B/s</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="totalDataDownLabel">
<spacer name="horizontalSpacer_6">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_6">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string/>
<string>Data</string>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="updownImageBox_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>32</horstretch>
<verstretch>32</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>25</width>
<height>25</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">border-image: url(:/icons/netspeed-arrows.svg) 0 0 0 0 stretch stretch;background-repeat: no-repeat;</string>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="dataamountLabel">
<property name="font">
<font>
<pointsize>8</pointsize>
</font>
</property>
<property name="text">
<string>0.00 B
0.00 B</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_7">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>

View File

@ -3,15 +3,15 @@
#include <QIntValidator>
#include <iostream>
#include "w_ConnectionEditWindow.h"
#include "w_OutboundEditor.h"
#include "w_MainWindow.h"
#include "w_JsonEditor.h"
ConnectionEditWindow::ConnectionEditWindow(QWidget *parent)
OutboundEditor::OutboundEditor(QWidget *parent)
: QDialog(parent),
Tag(OUTBOUND_TAG_PROXY),
Alias(),
ui(new Ui::ConnectionEditWindow),
ui(new Ui::OutboundEditor),
stream(),
vmess(),
shadowsocks()
@ -31,8 +31,8 @@ ConnectionEditWindow::ConnectionEditWindow(QWidget *parent)
Result = GenerateConnectionJson();
}
ConnectionEditWindow::ConnectionEditWindow(QJsonObject outboundEntry, QString *alias, QWidget *parent)
: ConnectionEditWindow(parent)
OutboundEditor::OutboundEditor(QJsonObject outboundEntry, QString *alias, QWidget *parent)
: OutboundEditor(parent)
{
Original = outboundEntry;
Alias = alias == nullptr ? "" : *alias;
@ -65,18 +65,18 @@ ConnectionEditWindow::ConnectionEditWindow(QJsonObject outboundEntry, QString *a
}
ConnectionEditWindow::~ConnectionEditWindow()
OutboundEditor::~OutboundEditor()
{
delete ui;
}
QJsonObject ConnectionEditWindow::OpenEditor()
QJsonObject OutboundEditor::OpenEditor()
{
int resultCode = this->exec();
return resultCode == QDialog::Accepted ? Result : Original;
}
void ConnectionEditWindow::ReLoad_GUI_JSON_ModelContent()
void OutboundEditor::ReLoad_GUI_JSON_ModelContent()
{
if (OutboundType == "vmess") {
ui->outBoundTypeCombo->setCurrentIndex(0);
@ -151,7 +151,7 @@ void ConnectionEditWindow::ReLoad_GUI_JSON_ModelContent()
}
void ConnectionEditWindow::on_buttonBox_accepted()
void OutboundEditor::on_buttonBox_accepted()
{
// TODO : NAMING THE CONNECTION
auto alias = Alias == "" ? (ui->ipLineEdit->text() + "_" + ui->portLineEdit->text()) : Alias;
@ -179,7 +179,7 @@ void ConnectionEditWindow::on_buttonBox_accepted()
//emit s_reload_config(!is_new_config);
}
void ConnectionEditWindow::on_ipLineEdit_textEdited(const QString &arg1)
void OutboundEditor::on_ipLineEdit_textEdited(const QString &arg1)
{
vmess.address = arg1.toStdString();
shadowsocks.address = arg1.toStdString();
@ -197,7 +197,7 @@ void ConnectionEditWindow::on_ipLineEdit_textEdited(const QString &arg1)
//}
}
void ConnectionEditWindow::on_portLineEdit_textEdited(const QString &arg1)
void OutboundEditor::on_portLineEdit_textEdited(const QString &arg1)
{
if (arg1 != "") {
vmess.port = stoi(arg1.toStdString());
@ -206,38 +206,38 @@ void ConnectionEditWindow::on_portLineEdit_textEdited(const QString &arg1)
}
}
void ConnectionEditWindow::on_idLineEdit_textEdited(const QString &arg1)
void OutboundEditor::on_idLineEdit_textEdited(const QString &arg1)
{
if (vmess.users.empty()) vmess.users.push_back(VMessServerObject::UserObject());
vmess.users.front().id = arg1.toStdString();
}
void ConnectionEditWindow::on_alterLineEdit_textEdited(const QString &arg1)
void OutboundEditor::on_alterLineEdit_textEdited(const QString &arg1)
{
if (vmess.users.empty()) vmess.users.push_back(VMessServerObject::UserObject());
vmess.users.front().alterId = stoi(arg1.toStdString());
}
void ConnectionEditWindow::on_securityCombo_currentIndexChanged(const QString &arg1)
void OutboundEditor::on_securityCombo_currentIndexChanged(const QString &arg1)
{
if (vmess.users.empty()) vmess.users.push_back(VMessServerObject::UserObject());
vmess.users.front().security = arg1.toStdString();
}
void ConnectionEditWindow::on_tranportCombo_currentIndexChanged(const QString &arg1)
void OutboundEditor::on_tranportCombo_currentIndexChanged(const QString &arg1)
{
stream.network = arg1.toStdString();
}
void ConnectionEditWindow::on_httpPathTxt_textEdited(const QString &arg1)
void OutboundEditor::on_httpPathTxt_textEdited(const QString &arg1)
{
stream.httpSettings.path = arg1.toStdString();
}
void ConnectionEditWindow::on_httpHostTxt_textChanged()
void OutboundEditor::on_httpHostTxt_textChanged()
{
try {
QStringList hosts = ui->httpHostTxt->toPlainText().replace("\r", "").split("\n");
@ -254,16 +254,18 @@ void ConnectionEditWindow::on_httpHostTxt_textChanged()
}
}
void ConnectionEditWindow::on_wsHeadersTxt_textChanged()
void OutboundEditor::on_wsHeadersTxt_textChanged()
{
try {
QStringList headers = ui->wsHeadersTxt->toPlainText().replace("\r", "").split("\n");
stream.wsSettings.headers.clear();
foreach (auto header, headers) {
if (header.isEmpty()) continue;
auto content = header.split("|");
if (content.length() < 2) throw "fast fail.";
if (content.length() < 2) throw "fast fail to set RED color";
stream.wsSettings.headers.insert(make_pair(content[0].toStdString(), content[1].toStdString()));
}
@ -275,7 +277,7 @@ void ConnectionEditWindow::on_wsHeadersTxt_textChanged()
}
void ConnectionEditWindow::on_tcpRequestDefBtn_clicked()
void OutboundEditor::on_tcpRequestDefBtn_clicked()
{
ui->tcpRequestTxt->clear();
ui->tcpRequestTxt->insertPlainText("{\"version\":\"1.1\",\"method\":\"GET\",\"path\":[\"/\"],\"headers\":"
@ -288,25 +290,26 @@ void ConnectionEditWindow::on_tcpRequestDefBtn_clicked()
"\"Connection\":[\"keep-alive\"],\"Pragma\":\"no-cache\"}}");
}
void ConnectionEditWindow::on_tcpRespDefBtn_clicked()
void OutboundEditor::on_tcpRespDefBtn_clicked()
{
ui->tcpRespTxt->clear();
ui->tcpRespTxt->insertPlainText("{\"version\":\"1.1\",\"status\":\"200\",\"reason\":\"OK\",\"headers\":{\"Content-Type\":[\"application/octet-stream\",\"video/mpeg\"],\"Transfer-Encoding\":[\"chunked\"],\"Connection\":[\"keep-alive\"],\"Pragma\":\"no-cache\"}}");
}
void ConnectionEditWindow::on_genJsonBtn_clicked()
void OutboundEditor::on_genJsonBtn_clicked()
{
auto json = GenerateConnectionJson();
}
QJsonObject ConnectionEditWindow::GenerateConnectionJson()
QJsonObject OutboundEditor::GenerateConnectionJson()
{
// VMess is only a ServerObject, and we need an array { "vnext": [] }
QJsonObject settings;
auto mux = JsonFromString(StructToJsonString(GetGlobalConfig().mux));
// BUG !!!
auto mux = QJsonObject();// JsonFromString(StructToJsonString(GetGlobalConfig().mux));
auto streaming = JsonFromString(StructToJsonString(stream));
if (OutboundType == "vmess") {
// VMess is only a ServerObject, and we need an array { "vnext": [] }
QJsonArray vnext;
vnext.append(GetRootObject(vmess));
settings.insert("vnext", vnext);
@ -326,124 +329,124 @@ QJsonObject ConnectionEditWindow::GenerateConnectionJson()
return root;
}
void ConnectionEditWindow::on_tlsCB_stateChanged(int arg1)
void OutboundEditor::on_tlsCB_stateChanged(int arg1)
{
stream.security = arg1 == Qt::Checked ? "tls" : "none";
}
void ConnectionEditWindow::on_soMarkSpinBox_valueChanged(int arg1)
void OutboundEditor::on_soMarkSpinBox_valueChanged(int arg1)
{
stream.sockopt.mark = arg1;
}
void ConnectionEditWindow::on_tcpFastOpenCB_stateChanged(int arg1)
void OutboundEditor::on_tcpFastOpenCB_stateChanged(int arg1)
{
stream.sockopt.tcpFastOpen = arg1 == Qt::Checked;
}
void ConnectionEditWindow::on_tProxyCB_currentIndexChanged(const QString &arg1)
void OutboundEditor::on_tProxyCB_currentIndexChanged(const QString &arg1)
{
stream.sockopt.tproxy = arg1.toStdString();
}
void ConnectionEditWindow::on_quicSecurityCB_currentTextChanged(const QString &arg1)
void OutboundEditor::on_quicSecurityCB_currentTextChanged(const QString &arg1)
{
stream.quicSettings.security = arg1.toStdString();
}
void ConnectionEditWindow::on_quicKeyTxt_textEdited(const QString &arg1)
void OutboundEditor::on_quicKeyTxt_textEdited(const QString &arg1)
{
stream.quicSettings.key = arg1.toStdString();
}
void ConnectionEditWindow::on_quicHeaderTypeCB_currentIndexChanged(const QString &arg1)
void OutboundEditor::on_quicHeaderTypeCB_currentIndexChanged(const QString &arg1)
{
stream.quicSettings.header.type = arg1.toStdString();
}
void ConnectionEditWindow::on_tcpHeaderTypeCB_currentIndexChanged(const QString &arg1)
void OutboundEditor::on_tcpHeaderTypeCB_currentIndexChanged(const QString &arg1)
{
stream.tcpSettings.header.type = arg1.toStdString();
}
void ConnectionEditWindow::on_wsPathTxt_textEdited(const QString &arg1)
void OutboundEditor::on_wsPathTxt_textEdited(const QString &arg1)
{
stream.wsSettings.path = arg1.toStdString();
}
void ConnectionEditWindow::on_kcpMTU_valueChanged(int arg1)
void OutboundEditor::on_kcpMTU_valueChanged(int arg1)
{
stream.kcpSettings.mtu = arg1;
}
void ConnectionEditWindow::on_kcpTTI_valueChanged(int arg1)
void OutboundEditor::on_kcpTTI_valueChanged(int arg1)
{
stream.kcpSettings.tti = arg1;
}
void ConnectionEditWindow::on_kcpUploadCapacSB_valueChanged(int arg1)
void OutboundEditor::on_kcpUploadCapacSB_valueChanged(int arg1)
{
stream.kcpSettings.uplinkCapacity = arg1;
}
void ConnectionEditWindow::on_kcpCongestionCB_stateChanged(int arg1)
void OutboundEditor::on_kcpCongestionCB_stateChanged(int arg1)
{
stream.kcpSettings.congestion = arg1 == Qt::Checked;
}
void ConnectionEditWindow::on_kcpDownCapacitySB_valueChanged(int arg1)
void OutboundEditor::on_kcpDownCapacitySB_valueChanged(int arg1)
{
stream.kcpSettings.downlinkCapacity = arg1;
}
void ConnectionEditWindow::on_kcpReadBufferSB_valueChanged(int arg1)
void OutboundEditor::on_kcpReadBufferSB_valueChanged(int arg1)
{
stream.kcpSettings.readBufferSize = arg1;
}
void ConnectionEditWindow::on_kcpWriteBufferSB_valueChanged(int arg1)
void OutboundEditor::on_kcpWriteBufferSB_valueChanged(int arg1)
{
stream.kcpSettings.writeBufferSize = arg1;
}
void ConnectionEditWindow::on_kcpHeaderType_currentTextChanged(const QString &arg1)
void OutboundEditor::on_kcpHeaderType_currentTextChanged(const QString &arg1)
{
stream.kcpSettings.header.type = arg1.toStdString();
}
void ConnectionEditWindow::on_tranportCombo_currentIndexChanged(int index)
void OutboundEditor::on_tranportCombo_currentIndexChanged(int index)
{
ui->v2rayStackView->setCurrentIndex(index);
}
void ConnectionEditWindow::on_dsPathTxt_textEdited(const QString &arg1)
void OutboundEditor::on_dsPathTxt_textEdited(const QString &arg1)
{
stream.dsSettings.path = arg1.toStdString();
}
void ConnectionEditWindow::on_outBoundTypeCombo_currentIndexChanged(int index)
void OutboundEditor::on_outBoundTypeCombo_currentIndexChanged(int index)
{
ui->outboundTypeStackView->setCurrentIndex(index);
OutboundType = ui->outBoundTypeCombo->currentText().toLower();
}
void ConnectionEditWindow::on_ss_emailTxt_textEdited(const QString &arg1)
void OutboundEditor::on_ss_emailTxt_textEdited(const QString &arg1)
{
shadowsocks.email = arg1.toStdString();
}
void ConnectionEditWindow::on_ss_passwordTxt_textEdited(const QString &arg1)
void OutboundEditor::on_ss_passwordTxt_textEdited(const QString &arg1)
{
shadowsocks.password = arg1.toStdString();
}
void ConnectionEditWindow::on_ss_encryptionMethod_currentIndexChanged(const QString &arg1)
void OutboundEditor::on_ss_encryptionMethod_currentIndexChanged(const QString &arg1)
{
shadowsocks.method = arg1.toStdString();
}
void ConnectionEditWindow::on_ss_levelSpin_valueChanged(int arg1)
void OutboundEditor::on_ss_levelSpin_valueChanged(int arg1)
{
shadowsocks.level = arg1;
}
void ConnectionEditWindow::on_ss_otaCheckBox_stateChanged(int arg1)
void OutboundEditor::on_ss_otaCheckBox_stateChanged(int arg1)
{
shadowsocks.ota = arg1 == Qt::Checked;
}
void ConnectionEditWindow::on_socks_UserNameTxt_textEdited(const QString &arg1)
void OutboundEditor::on_socks_UserNameTxt_textEdited(const QString &arg1)
{
socks.users.front().user = arg1.toStdString();
}
void ConnectionEditWindow::on_socks_PasswordTxt_textEdited(const QString &arg1)
void OutboundEditor::on_socks_PasswordTxt_textEdited(const QString &arg1)
{
socks.users.front().pass = arg1.toStdString();
}
void ConnectionEditWindow::on_tcpRequestEditBtn_clicked()
void OutboundEditor::on_tcpRequestEditBtn_clicked()
{
JsonEditor *w = new JsonEditor(JsonFromString(ui->tcpRequestTxt->toPlainText()), this);
auto rString = JsonToString(w->OpenEditor());
@ -453,7 +456,7 @@ void ConnectionEditWindow::on_tcpRequestEditBtn_clicked()
delete w;
}
void ConnectionEditWindow::on_tcpResponseEditBtn_clicked()
void OutboundEditor::on_tcpResponseEditBtn_clicked()
{
JsonEditor *w = new JsonEditor(JsonFromString(ui->tcpRespTxt->toPlainText()), this);
auto rString = JsonToString(w->OpenEditor());

View File

@ -4,24 +4,24 @@
#include <QtCore>
#include <QDialog>
#include "QvCoreConfigObjects.h"
#include "ui_w_ConnectionEditWindow.h"
#include "ui_w_OutboundEditor.h"
namespace Ui
{
class ConnectionEditWindow;
class OutboundEditor;
}
class ConnectionEditWindow : public QDialog
class OutboundEditor : public QDialog
{
Q_OBJECT
public:
explicit ConnectionEditWindow(QWidget *parent = nullptr);
explicit OutboundEditor(QWidget *parent = nullptr);
QString Tag;
QString Alias;
QJsonObject OpenEditor();
explicit ConnectionEditWindow(QJsonObject outboundEntry, QString *alias, QWidget *parent = nullptr);
~ConnectionEditWindow();
explicit OutboundEditor(QJsonObject outboundEntry, QString *alias, QWidget *parent = nullptr);
~OutboundEditor();
signals:
void s_reload_config(bool need_restart);
private:
@ -115,7 +115,7 @@ class ConnectionEditWindow : public QDialog
QJsonObject Original;
QJsonObject Result;
QJsonObject GenerateConnectionJson();
Ui::ConnectionEditWindow *ui;
Ui::OutboundEditor *ui;
//
// Connection Configs
QString OutboundType;

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ConnectionEditWindow</class>
<widget class="QDialog" name="ConnectionEditWindow">
<class>OutboundEditor</class>
<widget class="QDialog" name="OutboundEditor">
<property name="geometry">
<rect>
<x>0</x>
@ -39,7 +39,11 @@
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="ipLineEdit"/>
<widget class="QLineEdit" name="ipLineEdit">
<property name="placeholderText">
<string>Hostname or IP Address</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="portLabel">
@ -53,6 +57,9 @@
<property name="maxLength">
<number>5</number>
</property>
<property name="placeholderText">
<string>Port</string>
</property>
</widget>
</item>
<item row="3" column="0">
@ -192,6 +199,9 @@
<property name="maxLength">
<number>36</number>
</property>
<property name="placeholderText">
<string notr="true">xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx</string>
</property>
</widget>
</item>
<item row="1" column="0">
@ -206,6 +216,9 @@
<property name="maxLength">
<number>5</number>
</property>
<property name="placeholderText">
<string>64</string>
</property>
</widget>
</item>
<item row="2" column="0">
@ -441,7 +454,11 @@
</widget>
</item>
<item row="1" column="0">
<widget class="QLineEdit" name="httpPathTxt"/>
<widget class="QLineEdit" name="httpPathTxt">
<property name="placeholderText">
<string notr="true">/</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_4">
@ -451,7 +468,11 @@
</widget>
</item>
<item row="3" column="0">
<widget class="QPlainTextEdit" name="httpHostTxt"/>
<widget class="QPlainTextEdit" name="httpHostTxt">
<property name="placeholderText">
<string notr="true">myhost.mydomain.com</string>
</property>
</widget>
</item>
</layout>
</widget>
@ -471,10 +492,20 @@
<property name="text">
<string/>
</property>
<property name="placeholderText">
<string>/wsPath</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QPlainTextEdit" name="wsHeadersTxt">
<property name="placeholderText">
<string notr="true">Key|Value</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_26">
<property name="text">
@ -482,16 +513,6 @@
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_23">
<property name="text">
<string>Format: Key|Value</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QPlainTextEdit" name="wsHeadersTxt"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="mKCPStackPage">
@ -719,6 +740,9 @@
<property name="text">
<string/>
</property>
<property name="placeholderText">
<string notr="true">/dsPath</string>
</property>
</widget>
</item>
</layout>
@ -771,6 +795,9 @@
<property name="text">
<string/>
</property>
<property name="placeholderText">
<string>keys</string>
</property>
</widget>
</item>
<item row="3" column="0">
@ -976,17 +1003,29 @@
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="ss_emailTxt"/>
<widget class="QLineEdit" name="ss_emailTxt">
<property name="placeholderText">
<string notr="true">my.name@domain.com</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="ss_passwordTxt"/>
<widget class="QLineEdit" name="ss_passwordTxt">
<property name="placeholderText">
<string notr="true">p@ssw0rd</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="page_3">
<layout class="QFormLayout" name="formLayout">
<item row="1" column="1">
<widget class="QLineEdit" name="socks_UserNameTxt"/>
<widget class="QLineEdit" name="socks_UserNameTxt">
<property name="placeholderText">
<string notr="true">Username</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_31">
@ -996,7 +1035,11 @@
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="socks_PasswordTxt"/>
<widget class="QLineEdit" name="socks_PasswordTxt">
<property name="placeholderText">
<string notr="true">p@ssw0rd</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_21">
@ -1029,7 +1072,7 @@
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>ConnectionEditWindow</receiver>
<receiver>OutboundEditor</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
@ -1045,7 +1088,7 @@
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>ConnectionEditWindow</receiver>
<receiver>OutboundEditor</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">

View File

@ -2,9 +2,11 @@
#include "QvCoreInteractions.h"
#include "w_PrefrencesWindow.h"
#include <QFileDialog>
#include <QColorDialog>
#include <iostream>
#define LOADINGCHECK if(!finishedLoading) return;
#define NEEDRESTART if(finishedLoading) IsConnectionPropertyChanged = true;
PrefrencesWindow::PrefrencesWindow(QWidget *parent) : QDialog(parent),
@ -20,6 +22,10 @@ PrefrencesWindow::PrefrencesWindow(QWidget *parent) : QDialog(parent),
ui->languageComboBox->addItem(it.next().split("/").last().split(".").first());
}
for (auto item : NetSpeedPluginMessages.values()) {
ui->nsBarContentCombo->addItem(item);
}
//
ui->qvVersion->setText(QV2RAY_VERSION_STRING);
CurrentConfig = GetGlobalConfig();
@ -33,7 +39,7 @@ PrefrencesWindow::PrefrencesWindow(QWidget *parent) : QDialog(parent),
//
bool have_http = CurrentConfig.inBoundSettings.http_port != 0;
ui->httpCB->setChecked(have_http);
ui->httpPortLE->setText(QSTRING(to_string(CurrentConfig.inBoundSettings.http_port)));
ui->httpPortLE->setValue(CurrentConfig.inBoundSettings.http_port);
ui->httpAuthCB->setChecked(CurrentConfig.inBoundSettings.http_useAuth);
//
ui->httpAuthCB->setEnabled(have_http);
@ -42,12 +48,11 @@ PrefrencesWindow::PrefrencesWindow(QWidget *parent) : QDialog(parent),
ui->httpAuthPasswordTxt->setEnabled(have_http && CurrentConfig.inBoundSettings.http_useAuth);
ui->httpAuthUsernameTxt->setText(QSTRING(CurrentConfig.inBoundSettings.httpAccount.user));
ui->httpAuthPasswordTxt->setText(QSTRING(CurrentConfig.inBoundSettings.httpAccount.pass));
ui->httpPortLE->setValidator(new QIntValidator());
//
//
bool have_socks = CurrentConfig.inBoundSettings.socks_port != 0;
ui->socksCB->setChecked(have_socks);
ui->socksPortLE->setText(QSTRING(to_string(CurrentConfig.inBoundSettings.socks_port)));
ui->socksPortLE->setValue(CurrentConfig.inBoundSettings.socks_port);
ui->socksAuthCB->setChecked(CurrentConfig.inBoundSettings.socks_useAuth);
//
ui->socksAuthCB->setEnabled(have_socks);
@ -56,20 +61,18 @@ PrefrencesWindow::PrefrencesWindow(QWidget *parent) : QDialog(parent),
ui->socksAuthPasswordTxt->setEnabled(have_socks && CurrentConfig.inBoundSettings.socks_useAuth);
ui->socksAuthUsernameTxt->setText(QSTRING(CurrentConfig.inBoundSettings.socksAccount.user));
ui->socksAuthPasswordTxt->setText(QSTRING(CurrentConfig.inBoundSettings.socksAccount.pass));
ui->socksPortLE->setValidator(new QIntValidator());
// Socks UDP Options
ui->socksUDPCB->setChecked(CurrentConfig.inBoundSettings.socksUDP);
ui->socksUDPIP->setEnabled(CurrentConfig.inBoundSettings.socksUDP);
ui->socksUDPIP->setText(QSTRING(CurrentConfig.inBoundSettings.socksLocalIP));
//
//
ui->vCorePathTxt->setText(QSTRING(CurrentConfig.v2CorePath));
ui->vCoreAssetsPathTxt->setText(QSTRING(CurrentConfig.v2AssetsPath));
ui->statsCheckbox->setChecked(CurrentConfig.enableStats);
ui->statsPortBox->setValue(CurrentConfig.statsPort);
//
//
#if false
ui->muxEnabledCB->setChecked(CurrentConfig.mux.enabled);
ui->muxConcurrencyTxt->setValue(CurrentConfig.mux.concurrency);
#endif
//
//
ui->bypassCNCb->setChecked(CurrentConfig.bypassCN);
ui->proxyDefaultCb->setChecked(CurrentConfig.enableProxy);
//
@ -92,6 +95,20 @@ PrefrencesWindow::PrefrencesWindow(QWidget *parent) : QDialog(parent),
ui->autoStartCombo->setCurrentText(QSTRING(CurrentConfig.autoStartConfig));
ui->cancelIgnoreVersionBtn->setEnabled(CurrentConfig.ignoredVersion != "");
ui->ignoredNextVersion->setText(QSTRING(CurrentConfig.ignoredVersion));
//
// TODO : Show nsBarPageList content.
//
for (size_t i = 0; i < CurrentConfig.speedBarConfig.Pages.size(); i++) {
ui->nsBarPagesList->addItem(tr("Page") + QString::number(i));
}
if (CurrentConfig.speedBarConfig.Pages.size() > 0) {
ui->nsBarPagesList->setCurrentRow(0);
on_nsBarPagesList_currentRowChanged(0);
}
CurrentBarPageId = 0;
finishedLoading = true;
}
@ -124,7 +141,7 @@ void PrefrencesWindow::on_httpCB_stateChanged(int checked)
CurrentConfig.inBoundSettings.http_port = checked == Qt::Checked ? CurrentConfig.inBoundSettings.http_port : 0;
if (checked != Qt::Checked) {
ui->httpPortLE->setText("0");
ui->httpPortLE->setValue(0);
}
}
@ -138,7 +155,7 @@ void PrefrencesWindow::on_socksCB_stateChanged(int checked)
CurrentConfig.inBoundSettings.socks_port = checked == Qt::Checked ? CurrentConfig.inBoundSettings.socks_port : 0;
if (checked != Qt::Checked) {
ui->socksPortLE->setText("0");
ui->socksPortLE->setValue(0);
}
}
@ -185,36 +202,12 @@ void PrefrencesWindow::on_vCoreAssetsPathTxt_textEdited(const QString &arg1)
CurrentConfig.v2AssetsPath = arg1.toStdString();
}
void PrefrencesWindow::on_muxEnabledCB_stateChanged(int arg1)
{
NEEDRESTART
CurrentConfig.mux.enabled = arg1 == Qt::Checked;
}
void PrefrencesWindow::on_muxConcurrencyTxt_valueChanged(int arg1)
{
NEEDRESTART
CurrentConfig.mux.concurrency = arg1;
}
void PrefrencesWindow::on_listenIPTxt_textEdited(const QString &arg1)
{
NEEDRESTART
CurrentConfig.inBoundSettings.listenip = arg1.toStdString();
}
void PrefrencesWindow::on_socksPortLE_textEdited(const QString &arg1)
{
NEEDRESTART
CurrentConfig.inBoundSettings.socks_port = stoi(arg1.toStdString());
}
void PrefrencesWindow::on_httpPortLE_textEdited(const QString &arg1)
{
NEEDRESTART
CurrentConfig.inBoundSettings.http_port = stoi(arg1.toStdString());
}
void PrefrencesWindow::on_httpAuthUsernameTxt_textEdited(const QString &arg1)
{
NEEDRESTART
@ -259,6 +252,19 @@ void PrefrencesWindow::on_selectVAssetBtn_clicked()
on_vCoreAssetsPathTxt_textEdited(dir);
}
void PrefrencesWindow::on_selectVCoreBtn_clicked()
{
QString core = QFileDialog::getOpenFileName(this, tr("Open v2ray core file"), QDir::currentPath());
ui->vCorePathTxt->setText(core);
on_vCorePathTxt_textEdited(core);
}
void PrefrencesWindow::on_vCorePathTxt_textEdited(const QString &arg1)
{
NEEDRESTART
CurrentConfig.v2CorePath = arg1.toStdString();
}
void PrefrencesWindow::on_DNSListTxt_textChanged()
{
if (finishedLoading) {
@ -319,7 +325,7 @@ void PrefrencesWindow::on_tProxyCheckBox_stateChanged(int arg1)
LOG(MODULE_UI, "Canceled enabling tProxy feature.")
}
int ret = QProcess::execute("pkexec setcap CAP_NET_ADMIN,CAP_NET_RAW,CAP_NET_BIND_SERVICE=eip " + QV2RAY_V2RAY_CORE_PATH);
int ret = QProcess::execute("pkexec setcap CAP_NET_ADMIN,CAP_NET_RAW,CAP_NET_BIND_SERVICE=eip " + QSTRING(CurrentConfig.v2CorePath));
if (ret != 0) {
LOG(MODULE_UI, "WARN: setcap exits with code: " + to_string(ret))
@ -329,7 +335,7 @@ void PrefrencesWindow::on_tProxyCheckBox_stateChanged(int arg1)
CurrentConfig.tProxySupport = true;
NEEDRESTART
} else {
int ret = QProcess::execute("pkexec setcap -r " + QV2RAY_V2RAY_CORE_PATH);
int ret = QProcess::execute("pkexec setcap -r " + QSTRING(CurrentConfig.v2CorePath));
if (ret != 0) {
LOG(MODULE_UI, "WARN: setcap exits with code: " + to_string(ret))
@ -365,3 +371,249 @@ void PrefrencesWindow::on_statsPortBox_valueChanged(int arg1)
NEEDRESTART
CurrentConfig.statsPort = arg1;
}
void PrefrencesWindow::on_socksPortLE_valueChanged(int arg1)
{
NEEDRESTART
CurrentConfig.inBoundSettings.socks_port = arg1;
}
void PrefrencesWindow::on_httpPortLE_valueChanged(int arg1)
{
NEEDRESTART
CurrentConfig.inBoundSettings.http_port = arg1;
}
void PrefrencesWindow::on_socksUDPCB_stateChanged(int arg1)
{
NEEDRESTART
CurrentConfig.inBoundSettings.socksUDP = arg1 == Qt::Checked;
ui->socksUDPIP->setEnabled(arg1 == Qt::Checked);
}
void PrefrencesWindow::on_socksUDPIP_textEdited(const QString &arg1)
{
NEEDRESTART
CurrentConfig.inBoundSettings.socksLocalIP = arg1.toStdString();
}
// ------------------- NET SPEED PLUGIN OPERATIONS -----------------------------------------------------------------
#define CurrentBarPage CurrentConfig.speedBarConfig.Pages[this->CurrentBarPageId]
#define CurrentBarLine CurrentBarPage.Lines[this->CurrentBarLineId]
#define SET_LINE_LIST_TEXT ui->nsBarLinesList->currentItem()->setText(GetBarLineDescription(CurrentBarLine));
void PrefrencesWindow::on_nsBarPageAddBTN_clicked()
{
QvBarPage page;
CurrentConfig.speedBarConfig.Pages.push_back(page);
CurrentBarPageId = CurrentConfig.speedBarConfig.Pages.size() - 1 ;
// Add default line.
QvBarLine line;
CurrentBarPage.Lines.push_back(line);
CurrentBarLineId = 0;
ui->nsBarPagesList->addItem(QString::number(CurrentBarPageId));
ShowLineParameters(CurrentBarLine);
LOG(MODULE_UI, "Adding new page Id: " + to_string(CurrentBarPageId))
}
void PrefrencesWindow::on_nsBarPageDelBTN_clicked()
{
RemoveItem(CurrentConfig.speedBarConfig.Pages, static_cast<size_t>(ui->nsBarPagesList->currentRow()));
ui->nsBarPagesList->takeItem(ui->nsBarPagesList->currentRow());
}
void PrefrencesWindow::on_nsBarPageYOffset_valueChanged(int arg1)
{
LOADINGCHECK
CurrentBarPage.OffsetYpx = arg1;
}
void PrefrencesWindow::on_nsBarLineAddBTN_clicked()
{
// WARNING Is it really just this simple?
QvBarLine line;
CurrentBarPage.Lines.push_back(line);
CurrentBarLineId = CurrentBarPage.Lines.size() - 1 ;
ui->nsBarLinesList->addItem(QString::number(CurrentBarLineId));
ShowLineParameters(CurrentBarLine);
LOG(MODULE_UI, "Adding new line Id: " + to_string(CurrentBarLineId))
// TODO Some UI Works such as enabling ui.
}
void PrefrencesWindow::on_nsBarLineDelBTN_clicked()
{
RemoveItem(CurrentBarPage.Lines, static_cast<size_t>(ui->nsBarLinesList->currentRow()));
ui->nsBarLinesList->takeItem(ui->nsBarLinesList->currentRow());
CurrentBarLineId = 0;
// TODO Disabling some UI;
}
void PrefrencesWindow::on_nsBarPagesList_currentRowChanged(int currentRow)
{
if (currentRow < 0) return;
// Change page.
// We reload the lines
// Set all parameters item to the property of the first line.
CurrentBarPageId = static_cast<size_t>(currentRow);
CurrentBarLineId = 0;
ui->nsBarPageYOffset->setValue(CurrentBarPage.OffsetYpx);
ui->nsBarLinesList->clear();
if (!CurrentBarPage.Lines.empty()) {
for (auto line : CurrentBarPage.Lines) {
auto description = GetBarLineDescription(line);
ui->nsBarLinesList->addItem(description);
}
ui->nsBarLinesList->setCurrentRow(0);
ShowLineParameters(CurrentBarLine);
}
}
void PrefrencesWindow::on_nsBarLinesList_currentRowChanged(int currentRow)
{
if (currentRow < 0) return;
CurrentBarLineId = static_cast<size_t>(currentRow);
ShowLineParameters(CurrentBarLine);
}
void PrefrencesWindow::on_fontComboBox_currentFontChanged(const QFont &f)
{
LOADINGCHECK
CurrentBarLine.Family = f.family().toStdString();
SET_LINE_LIST_TEXT
}
void PrefrencesWindow::on_nsBarFontBoldCB_stateChanged(int arg1)
{
LOADINGCHECK
CurrentBarLine.Bold = arg1 == Qt::Checked;
SET_LINE_LIST_TEXT
}
void PrefrencesWindow::on_nsBarFontItalicCB_stateChanged(int arg1)
{
LOADINGCHECK
CurrentBarLine.Italic = arg1 == Qt::Checked;
SET_LINE_LIST_TEXT
}
void PrefrencesWindow::on_nsBarFontASB_valueChanged(int arg1)
{
LOADINGCHECK
CurrentBarLine.ColorA = arg1;
ShowLineParameters(CurrentBarLine);
SET_LINE_LIST_TEXT
}
void PrefrencesWindow::on_nsBarFontRSB_valueChanged(int arg1)
{
LOADINGCHECK
CurrentBarLine.ColorR = arg1;
ShowLineParameters(CurrentBarLine);
SET_LINE_LIST_TEXT
}
void PrefrencesWindow::on_nsBarFontGSB_valueChanged(int arg1)
{
LOADINGCHECK
CurrentBarLine.ColorG = arg1;
ShowLineParameters(CurrentBarLine);
SET_LINE_LIST_TEXT
}
void PrefrencesWindow::on_nsBarFontBSB_valueChanged(int arg1)
{
LOADINGCHECK
CurrentBarLine.ColorB = arg1;
ShowLineParameters(CurrentBarLine);
SET_LINE_LIST_TEXT
}
void PrefrencesWindow::on_nsBarFontSizeSB_valueChanged(double arg1)
{
LOADINGCHECK
CurrentBarLine.Size = arg1;
SET_LINE_LIST_TEXT
}
QString PrefrencesWindow::GetBarLineDescription(QvBarLine line)
{
QString result = "Empty";
result = NetSpeedPluginMessages[line.ContentType];
// BUG Content type is null, then set empty;
if (line.ContentType == 0) {
result += "(" + QSTRING(line.Message) + ")";
}
result = result.append(line.Bold ? ", " + tr("Bold") : "");
result = result.append(line.Italic ? ", " + tr("Italic") : "");
// TODO : Set more descriptions
return result;
}
void PrefrencesWindow::ShowLineParameters(QvBarLine &line)
{
finishedLoading = false;
if (!line.Family.empty()) {
ui->fontComboBox->setCurrentFont(QFont(QSTRING(line.Family)));
}
// Colors
ui->nsBarFontASB->setValue(line.ColorA);
ui->nsBarFontBSB->setValue(line.ColorB);
ui->nsBarFontGSB->setValue(line.ColorG);
ui->nsBarFontRSB->setValue(line.ColorR);
//
QColor color = QColor::fromRgb(line.ColorR, line.ColorG, line.ColorB, line.ColorA);
QString s("background: #"
+ QString(color.red() < 16 ? "0" : "") + QString::number(color.red(), 16)
+ QString(color.green() < 16 ? "0" : "") + QString::number(color.green(), 16)
+ QString(color.blue() < 16 ? "0" : "") + QString::number(color.blue(), 16) + ";");
ui->chooseColorBtn->setStyleSheet(s);
ui->nsBarFontSizeSB->setValue(line.Size);
ui->nsBarFontBoldCB->setChecked(line.Bold);
ui->nsBarFontItalicCB->setChecked(line.Italic);
ui->nsBarContentCombo->setCurrentText(NetSpeedPluginMessages[line.ContentType]);
ui->nsBarTagTxt->setText(QSTRING(line.Message));
finishedLoading = true;
}
void PrefrencesWindow::on_chooseColorBtn_clicked()
{
LOADINGCHECK
QColorDialog d(QColor::fromRgb(CurrentBarLine.ColorR, CurrentBarLine.ColorG, CurrentBarLine.ColorB, CurrentBarLine.ColorA), this);
d.exec();
if (d.result() == QDialog::DialogCode::Accepted) {
d.selectedColor().getRgb(&CurrentBarLine.ColorR, &CurrentBarLine.ColorG, &CurrentBarLine.ColorB, &CurrentBarLine.ColorA);
ShowLineParameters(CurrentBarLine);
SET_LINE_LIST_TEXT
}
}
void PrefrencesWindow::on_nsBarTagTxt_textEdited(const QString &arg1)
{
LOADINGCHECK
CurrentBarLine.Message = arg1.toStdString();
SET_LINE_LIST_TEXT
}
void PrefrencesWindow::on_nsBarContentCombo_currentIndexChanged(const QString &arg1)
{
LOADINGCHECK
CurrentBarLine.ContentType = NetSpeedPluginMessages.key(arg1);
SET_LINE_LIST_TEXT
}
void PrefrencesWindow::on_applyNSBarSettingsBtn_clicked()
{
auto conf = GetGlobalConfig();
conf.speedBarConfig = CurrentConfig.speedBarConfig;
SetGlobalConfig(conf);
}

View File

@ -37,15 +37,11 @@ class PrefrencesWindow : public QDialog
void on_vCoreAssetsPathTxt_textEdited(const QString &arg1);
void on_muxEnabledCB_stateChanged(int arg1);
void on_muxConcurrencyTxt_valueChanged(int arg1);
void on_listenIPTxt_textEdited(const QString &arg1);
void on_socksPortLE_textEdited(const QString &arg1);
void on_socksPortLE_valueChanged(int arg1);
void on_httpPortLE_textEdited(const QString &arg1);
void on_httpPortLE_valueChanged(int arg1);
void on_httpAuthUsernameTxt_textEdited(const QString &arg1);
@ -77,7 +73,60 @@ class PrefrencesWindow : public QDialog
void on_statsPortBox_valueChanged(int arg1);
void on_socksUDPCB_stateChanged(int arg1);
void on_socksUDPIP_textEdited(const QString &arg1);
void on_nsBarPageAddBTN_clicked();
void on_nsBarPageDelBTN_clicked();
void on_nsBarPageYOffset_valueChanged(int arg1);
void on_nsBarLineAddBTN_clicked();
void on_nsBarLineDelBTN_clicked();
void on_nsBarPagesList_currentRowChanged(int currentRow);
void on_nsBarLinesList_currentRowChanged(int currentRow);
void on_fontComboBox_currentFontChanged(const QFont &f);
void on_nsBarFontBoldCB_stateChanged(int arg1);
void on_nsBarFontItalicCB_stateChanged(int arg1);
void on_nsBarFontASB_valueChanged(int arg1);
void on_nsBarFontRSB_valueChanged(int arg1);
void on_nsBarFontGSB_valueChanged(int arg1);
void on_nsBarFontBSB_valueChanged(int arg1);
void on_nsBarFontSizeSB_valueChanged(double arg1);
void on_chooseColorBtn_clicked();
void on_nsBarTagTxt_textEdited(const QString &arg1);
void on_nsBarContentCombo_currentIndexChanged(const QString &arg1);
void on_applyNSBarSettingsBtn_clicked();
void on_selectVCoreBtn_clicked();
void on_vCorePathTxt_textEdited(const QString &arg1);
private:
// Set ui parameters for a line;
void ShowLineParameters(QvBarLine &line);
QString GetBarLineDescription(QvBarLine line);
//
size_t CurrentBarLineId;
size_t CurrentBarPageId;
//
bool IsConnectionPropertyChanged = false;
bool finishedLoading = false;
Qv2ray::QvConfigModels::Qv2rayConfig CurrentConfig;

View File

@ -6,14 +6,20 @@
<rect>
<x>0</x>
<y>0</y>
<width>709</width>
<height>370</height>
<width>780</width>
<height>500</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>700</width>
<height>370</height>
<width>780</width>
<height>500</height>
</size>
</property>
<property name="windowTitle">
@ -22,8 +28,8 @@
<property name="modal">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QTabWidget" name="tabWidget">
<property name="tabShape">
<enum>QTabWidget::Rounded</enum>
@ -141,14 +147,14 @@
</property>
</widget>
</item>
<item row="6" column="0">
<item row="7" column="0">
<widget class="QLabel" name="label_15">
<property name="text">
<string>v2ray Assets Path</string>
</property>
</widget>
</item>
<item row="6" column="1">
<item row="7" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLineEdit" name="vCoreAssetsPathTxt"/>
@ -156,7 +162,7 @@
<item>
<widget class="QPushButton" name="selectVAssetBtn">
<property name="text">
<string>#Select</string>
<string>Select</string>
</property>
</widget>
</item>
@ -196,24 +202,49 @@
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_46">
<property name="text">
<string>v2ray Core Path</string>
</property>
</widget>
</item>
<item row="6" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_12">
<item>
<widget class="QLineEdit" name="vCorePathTxt"/>
</item>
<item>
<widget class="QPushButton" name="selectVCoreBtn">
<property name="text">
<string>Select</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_3">
<attribute name="title">
<string>InBound Settings</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,1">
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,1">
<item>
<layout class="QFormLayout" name="formLayout_4">
<item row="0" column="0">
<widget class="QLabel" name="label_22">
<property name="text">
<string>Listen IP</string>
<string>Listening Address</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="listenIPTxt"/>
<widget class="QLineEdit" name="listenIPTxt">
<property name="placeholderText">
<string notr="true">0.0.0.0</string>
</property>
</widget>
</item>
</layout>
</item>
@ -222,7 +253,7 @@
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>SOCKS InBound Settings</string>
<string>SOCKS Inbound Settings</string>
</property>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="1">
@ -240,9 +271,12 @@
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="socksPortLE">
<property name="text">
<string/>
<widget class="QSpinBox" name="socksPortLE">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>65535</number>
</property>
</widget>
</item>
@ -268,7 +302,11 @@
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="socksAuthUsernameTxt"/>
<widget class="QLineEdit" name="socksAuthUsernameTxt">
<property name="placeholderText">
<string notr="true">user</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_12">
@ -278,7 +316,39 @@
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="socksAuthPasswordTxt"/>
<widget class="QLineEdit" name="socksAuthPasswordTxt">
<property name="placeholderText">
<string notr="true">pass</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_14">
<property name="text">
<string>SOCKS UDP</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_25">
<property name="text">
<string>Local IP</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QCheckBox" name="socksUDPCB">
<property name="text">
<string>Enabled</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QLineEdit" name="socksUDPIP">
<property name="placeholderText">
<string notr="true">127.0.0.1</string>
</property>
</widget>
</item>
</layout>
</widget>
@ -286,7 +356,7 @@
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>HTTP InBound Settings</string>
<string>HTTP Inbound Settings</string>
</property>
<layout class="QFormLayout" name="formLayout_7">
<item row="0" column="1">
@ -307,15 +377,12 @@
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="httpPortLE">
<property name="text">
<string/>
<widget class="QSpinBox" name="httpPortLE">
<property name="minimum">
<number>1</number>
</property>
<property name="dragEnabled">
<bool>false</bool>
</property>
<property name="clearButtonEnabled">
<bool>false</bool>
<property name="maximum">
<number>65535</number>
</property>
</widget>
</item>
@ -341,7 +408,11 @@
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="httpAuthUsernameTxt"/>
<widget class="QLineEdit" name="httpAuthUsernameTxt">
<property name="placeholderText">
<string notr="true">user</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_13">
@ -351,26 +422,17 @@
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="httpAuthPasswordTxt"/>
<widget class="QLineEdit" name="httpAuthPasswordTxt">
<property name="placeholderText">
<string notr="true">pass</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</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>
</widget>
<widget class="QWidget" name="tab_4">
@ -442,6 +504,310 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>NetSpeed Bar Settings</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_3" rowstretch="0,1,0" columnstretch="1,0">
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="label_33">
<property name="text">
<string>You can config how the network speed toolbar looks like in this panel</string>
</property>
</widget>
</item>
<item row="1" column="0" rowspan="2">
<widget class="QGroupBox" name="groupBox_6">
<property name="title">
<string>Items</string>
</property>
<layout class="QGridLayout" name="gridLayout_2" rowstretch="2,2,3,2,1,1,1" columnstretch="0,0,1,0">
<item row="1" column="3">
<widget class="QToolButton" name="nsBarPageDelBTN">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../resources.qrc">
<normaloff>:/icons/remove_connection_btn.png</normaloff>:/icons/remove_connection_btn.png</iconset>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QToolButton" name="nsBarPageAddBTN">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../resources.qrc">
<normaloff>:/icons/add_connection_btn.png</normaloff>:/icons/add_connection_btn.png</iconset>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="label_39">
<property name="text">
<string>Page Y Offset</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_32">
<property name="text">
<string>Pages</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_45">
<property name="text">
<string>Lines</string>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QSpinBox" name="nsBarPageYOffset">
<property name="maximum">
<number>1024</number>
</property>
</widget>
</item>
<item row="2" column="3" rowspan="2">
<spacer name="verticalSpacer_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>38</height>
</size>
</property>
</spacer>
</item>
<item row="5" column="3">
<widget class="QToolButton" name="nsBarLineDelBTN">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../resources.qrc">
<normaloff>:/icons/remove_connection_btn.png</normaloff>:/icons/remove_connection_btn.png</iconset>
</property>
</widget>
</item>
<item row="4" column="3">
<widget class="QToolButton" name="nsBarLineAddBTN">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../resources.qrc">
<normaloff>:/icons/add_connection_btn.png</normaloff>:/icons/add_connection_btn.png</iconset>
</property>
</widget>
</item>
<item row="6" column="3">
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="1" rowspan="3" colspan="2">
<widget class="QListWidget" name="nsBarPagesList"/>
</item>
<item row="4" column="1" rowspan="3" colspan="2">
<widget class="QListWidget" name="nsBarLinesList"/>
</item>
</layout>
</widget>
</item>
<item row="1" column="1">
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Text Style</string>
</property>
<layout class="QFormLayout" name="formLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="label_34">
<property name="text">
<string>Font</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QFontComboBox" name="fontComboBox"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_35">
<property name="text">
<string>Bold</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="nsBarFontBoldCB">
<property name="text">
<string>Enabled</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_36">
<property name="text">
<string>Italic</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="nsBarFontItalicCB">
<property name="text">
<string>Enabled</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_37">
<property name="text">
<string>Color</string>
</property>
</widget>
</item>
<item row="3" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_11" stretch="0,1,0,1,0,1,0,1,0">
<item>
<widget class="QLabel" name="label_40">
<property name="text">
<string>A:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="nsBarFontASB">
<property name="maximum">
<number>255</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_43">
<property name="text">
<string>R:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="nsBarFontRSB">
<property name="maximum">
<number>255</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_42">
<property name="text">
<string>G:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="nsBarFontGSB">
<property name="maximum">
<number>255</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_41">
<property name="text">
<string>B:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="nsBarFontBSB">
<property name="maximum">
<number>255</number>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="chooseColorBtn">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_38">
<property name="text">
<string>Size</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QDoubleSpinBox" name="nsBarFontSizeSB"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_5">
<property name="title">
<string>Content</string>
</property>
<layout class="QFormLayout" name="formLayout_9">
<item row="0" column="0">
<widget class="QLabel" name="label_44">
<property name="text">
<string>Content Type</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="nsBarContentCombo"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_31">
<property name="text">
<string>Text/Tag</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="nsBarTagTxt"/>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item row="2" column="1">
<widget class="QPushButton" name="applyNSBarSettingsBtn">
<property name="text">
<string>Apply Network Speed Bar UI Settings</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_5">
<attribute name="title">
<string>About</string>
@ -496,7 +862,7 @@
</property>
</widget>
</item>
<item row="1" column="0">
<item row="2" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<spacer name="horizontalSpacer">
@ -529,7 +895,7 @@
</item>
</layout>
</item>
<item row="2" column="0">
<item row="4" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item>
<spacer name="horizontalSpacer_3">
@ -580,7 +946,7 @@
</item>
</layout>
</item>
<item row="3" column="0">
<item row="5" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_9">
<item>
<spacer name="horizontalSpacer_5">
@ -631,7 +997,7 @@
</item>
</layout>
</item>
<item row="7" column="0">
<item row="9" column="0">
<spacer name="verticalSpacer1">
<property name="orientation">
<enum>Qt::Vertical</enum>
@ -644,7 +1010,7 @@
</property>
</spacer>
</item>
<item row="7" column="1">
<item row="9" column="1">
<widget class="QPushButton" name="aboutQt">
<property name="minimumSize">
<size>
@ -657,7 +1023,7 @@
</property>
</widget>
</item>
<item row="5" column="0">
<item row="7" column="0">
<spacer name="verticalSpacer1_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
@ -670,7 +1036,7 @@
</property>
</spacer>
</item>
<item row="6" column="0">
<item row="8" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_10">
<item>
<spacer name="horizontalSpacer_4">
@ -708,6 +1074,39 @@
</item>
</layout>
</item>
<item row="3" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_26">
<property name="text">
<string>Built Time</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="qvBuildTime">
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
@ -715,7 +1114,7 @@
</widget>
</widget>
</item>
<item>
<item row="1" column="0">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
@ -727,7 +1126,9 @@
</item>
</layout>
</widget>
<resources/>
<resources>
<include location="../../resources.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonBox</sender>

View File

@ -1,7 +1,9 @@
#include "w_RouteEditor.h"
#include "w_RoutesEditor.h"
#include "QvCoreConfigOperations.h"
#include "ui_w_RouteEditor.h"
#include "w_ConnectionEditWindow.h"
#include "ui_w_RoutesEditor.h"
#include "w_OutboundEditor.h"
#include "w_JsonEditor.h"
#include "w_InboundEditor.h"
RouteEditor::RouteEditor(QJsonObject connection, const QString alias, QWidget *parent) :
QDialog(parent),
@ -14,6 +16,7 @@ RouteEditor::RouteEditor(QJsonObject connection, const QString alias, QWidget *p
routes = StructFromJsonString<RoutingObject>(JsonToString(root["routing"].toObject()));
ui->setupUi(this);
ui->outboundsList->clear();
ui->inboundsList->clear();
foreach (auto out, outbounds) {
bool hasTag = out.toObject().contains("tag");
@ -27,7 +30,7 @@ RouteEditor::RouteEditor(QJsonObject connection, const QString alias, QWidget *p
foreach (auto in, inbounds) {
bool hasTag = in.toObject().contains("tag");
//
auto tag = hasTag ? in.toObject()["tag"].toString() : tr("NoTag");
auto tag = hasTag ? in.toObject()["tag"].toString() : tr("NoTag");
auto protocol = in.toObject()["protocol"].toString();
auto port = in.toObject()["port"].toVariant().toString();
//
@ -54,6 +57,9 @@ RouteEditor::RouteEditor(QJsonObject connection, const QString alias, QWidget *p
QJsonObject RouteEditor::OpenEditor()
{
this->exec();
root["inbounds"] = inbounds;
root["outbounds"] = outbounds;
root["routing"] = GetRootObject(routes);
return root;
}
@ -139,19 +145,23 @@ void RouteEditor::on_routesTable_cellClicked(int row, int column)
void RouteEditor::on_editOutboundBtn_clicked()
{
QJsonObject result;
int row = ui->outboundsList->currentRow();
auto currentOutbound = outbounds[row].toObject();
auto protocol = currentOutbound["protocol"].toString();
if (protocol != "vmess" && protocol != "shadowsocks" && protocol != "socks") {
QvMessageBox(this, tr("Cannot Edit"), tr("Currently, this type of outbound is not supported by the editor."));
return;
QvMessageBox(this, tr("Cannot Edit"), tr("Currently, this type of outbound is not supported by the editor.") + "\r\n" +
tr("We will launch Json Editor instead."));
JsonEditor *w = new JsonEditor(currentOutbound, this);
result = w->OpenEditor();
delete w;
} else {
OutboundEditor *w = new OutboundEditor(currentOutbound, nullptr, this);
result = w->OpenEditor();
delete w;
}
ConnectionEditWindow *w = new ConnectionEditWindow(currentOutbound, nullptr, this);
auto result = w->OpenEditor();
delete w;
//
outbounds[row] = result;
on_outboundsList_currentRowChanged(row);
}
@ -160,3 +170,26 @@ void RouteEditor::on_insertDirectBtn_clicked()
{
auto freedom = GenerateFreedomOUT("as-is", "", 0);
}
void RouteEditor::on_editInboundBtn_clicked()
{
QJsonObject result;
int row = ui->inboundsList->currentRow();
auto currentInbound = inbounds[row].toObject();
auto protocol = currentInbound["protocol"].toString();
if (protocol != "http" && protocol != "mtproto" && protocol != "socks" && protocol != "dokodemo-door") {
QvMessageBox(this, tr("Cannot Edit"), tr("Currently, this type of outbound is not supported by the editor.") + "\r\n" +
tr("We will launch Json Editor instead."));
JsonEditor *w = new JsonEditor(currentInbound, this);
result = w->OpenEditor();
delete w;
} else {
InboundEditor *w = new InboundEditor(currentInbound, this);
result = w->OpenEditor();
delete w;
}
inbounds[row] = result;
on_inboundsList_currentRowChanged(row);
}

View File

@ -33,6 +33,8 @@ class RouteEditor : public QDialog
void on_insertDirectBtn_clicked();
void on_editInboundBtn_clicked();
private:
QJsonArray inbounds;
QJsonArray outbounds;

View File

@ -6,134 +6,29 @@
<rect>
<x>0</x>
<y>0</y>
<width>889</width>
<width>950</width>
<height>560</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>950</width>
<height>560</height>
</size>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QGridLayout" name="gridLayout" columnstretch="0,1,0">
<item row="0" column="1">
<layout class="QVBoxLayout" name="routesLayout" stretch="0,1,0,1">
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>Routes List</string>
</property>
</widget>
</item>
<item>
<widget class="QTableWidget" name="routesTable">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<column>
<property name="text">
<string>InBounds</string>
</property>
</column>
<column>
<property name="text">
<string>DomainOrIP</string>
</property>
</column>
<column>
<property name="text">
<string>Outbound</string>
</property>
</column>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="routesControlButtons">
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QToolButton" name="addRouteBtn">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../resources.qrc">
<normaloff>:/icons/add_connection_btn.png</normaloff>:/icons/add_connection_btn.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="delRouteBtn">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../resources.qrc">
<normaloff>:/icons/remove_connection_btn.png</normaloff>:/icons/remove_connection_btn.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="changeIOBtn">
<property name="text">
<string>Change IO</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Route Information</string>
</property>
<layout class="QFormLayout" name="formLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Domains</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>IPs</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
<item row="2" column="2">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item row="0" column="0">
<layout class="QVBoxLayout" name="inboundLayout" stretch="0,1,0,1">
@ -186,62 +81,99 @@
<string>Inbound Information</string>
</property>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<item row="1" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Tag</string>
</property>
</widget>
</item>
<item row="0" column="1">
<item row="1" column="1">
<widget class="QLabel" name="inboundTagLabel">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="0">
<item row="2" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Type</string>
</property>
</widget>
</item>
<item row="1" column="1">
<item row="2" column="1">
<widget class="QLabel" name="inboundTypeLabel">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="0">
<item row="3" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Address</string>
</property>
</widget>
</item>
<item row="3" column="0">
<item row="4" column="0">
<widget class="QLabel" name="label_13">
<property name="text">
<string>Port</string>
</property>
</widget>
</item>
<item row="2" column="1">
<item row="3" column="1">
<widget class="QLabel" name="inboundAddressLabel">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="3" column="1">
<item row="4" column="1">
<widget class="QLabel" name="inboundPortLabel">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>24</width>
<height>18</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="editInboundBtn">
<property name="text">
<string>Edit</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_6">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>24</width>
<height>18</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>
@ -400,15 +332,131 @@
</item>
</layout>
</item>
<item row="2" column="2">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
<item row="0" column="1">
<layout class="QVBoxLayout" name="routesLayout" stretch="0,1,0,1">
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>Routes List</string>
</property>
</widget>
</item>
<item>
<widget class="QTableWidget" name="routesTable">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<column>
<property name="text">
<string>Enabled</string>
</property>
</column>
<column>
<property name="text">
<string>InBounds</string>
</property>
</column>
<column>
<property name="text">
<string>DomainOrIP</string>
</property>
</column>
<column>
<property name="text">
<string>Outbound</string>
</property>
</column>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="routesControlButtons">
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QToolButton" name="addRouteBtn">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../resources.qrc">
<normaloff>:/icons/add_connection_btn.png</normaloff>:/icons/add_connection_btn.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="delRouteBtn">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../resources.qrc">
<normaloff>:/icons/remove_connection_btn.png</normaloff>:/icons/remove_connection_btn.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="changeIOBtn">
<property name="text">
<string>Change IO</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Route Information</string>
</property>
<layout class="QFormLayout" name="formLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Domains</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>IPs</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item row="2" column="0" colspan="2">
<widget class="QLabel" name="statusLabel">

View File

@ -1,5 +1,5 @@
#include "w_SubscribeEditor.h"
#include "ui_w_SubscribeEditor.h"
#include "w_SubscriptionEditor.h"
#include "ui_w_SubscriptionEditor.h"
#include "QvHTTPRequestHelper.h"
#include "QvUtils.h"
#include "QvCoreConfigOperations.h"

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