Merge branch 'version-v1' into dev, MERGED Version 1.3.7.1 back to dev.

Former-commit-id: 9982ed8f78
This commit is contained in:
Leroy.H.Y 2019-09-24 23:29:46 +08:00
commit f82367e04b
47 changed files with 670 additions and 2691 deletions

51
.github/ISSUE_TEMPLATE/bug---.md vendored Normal file
View File

@ -0,0 +1,51 @@
---
name: Bug 反馈
about: 用来反馈bug
title: ''
labels: Bugs, help wanted
assignees: ''
---
**简单描述这个 bug**
**如何复现这个bug**
1.
2.
3.
4.
**Qv2ray 本应该是怎样工作的?**
**如果是连接问题,在其他 v2ray 客户端里出现过吗?**
**相关截图或日志**
```
日志:
```
**系统信息**
- 系统类型windows, linux, macos
-
- 是从release下载的还是自己编译的
-
- 首选项里的版本号:
-
**其他信息**
你认为对我们修复bug有帮助的任何信息都可以在这里写出来
*在提供自己的vmess或者配置文件时注意隐藏好服务器地址和uuid*
-

34
.github/ISSUE_TEMPLATE/bug---english.md vendored Normal file
View File

@ -0,0 +1,34 @@
---
name: Bug Report - English Version
about: Used to report a bug...
title: ''
labels: Bugs, help wanted
assignees: ''
---
**Briefly describe this bug**
**How can I trigger this bug**
1.
2.
3.
4.
**What is expected to happen**
**Screenshots**
If appropriate, please attach some screenshots.
**System Information**
- OS Type: (Windows, Linux or MacOS)
- Built from the source or download from the release?
- Version string in the Preferences Window
**Other Info**
You may want to write anything if you think it helps us to fix the bug.
*Please hide your server address and UUID if you wish to post the vmess string or your connection setting.*

123
README.md
View File

@ -1,42 +1,69 @@
# Qv2ray [![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)
***注意Qv2ray 仅能用于 Qt/c++/linux/CI/自动化 等相关技术的学习和在法律允许范围内的使用,任何个人或集体不得使用 Qv2ray 进行任何违反相关法律法规的操作。***
*任何尝试下载或下载 Qv2ray 任意分支或发行版即代表您同意本项目作者不承担任何由于您违反以上准则所带来的任何法律责任。*
# Qv2ray
[![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)
使用 Qt 框架的跨平台 v2ray 客户端. 支持 Windows, Linux, MacOS
支持连接编辑,支持导入配置和 VMess
支持连接编辑,支持导入任意配置和 `vmess://` 分享链接
# 项目主页,下载链接和使用方法
- 项目网站: https://lhy0403.github.io/Qv2ray
- 最新 [Release](https://github.com/lhy0403/Qv2ray/releases/latest)
- AppImage 编译平台: [主分支/Release 版本](https://jenkins.himeki.net/job/Qv2ray-AppImage-master/)
- Crowdin 翻译平台 (Public Translation Platform): [Crowdin](https://crowdin.com/project/qv2ray) [开始翻译](https://crwd.in/qv2ray)
- ArchLinux - AUR: [qv2ray](https://aur.archlinux.org/packages/qv2ray/) 由 @aliyuchang33 维护
Special thanks to: [Hv2ray](https://github.com/aliyuchang33/Hv2ray) by [@aliyuchang33](https://github.com/aliyuchang33)
## Jenkins (Linux)
Himeki.net 的 Jenkins 编译服务器提供开发分支 `dev` 的 Linux AppImage 版本
- 访问链接: [https://jenkins.himeki.net/job/Qv2ray-AppImage-dev/](https://jenkins.himeki.net/job/Qv2ray-AppImage-dev/)
- 开发分支仅供测试使用
- 提供者 [@aliyuchang33](https://github.com/aliyuchang33)
-------------------------
## 相关链接:
Travis per machine badge provided by: [badge-matrix](https://github.com/exogen/badge-matrix)
- **功能介绍: https://lhy0403.github.io/Qv2ray**
- 最新 **[Release 发布版](https://github.com/lhy0403/Qv2ray/releases/latest)**
- 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)** 维护
| OS | 主分支 [master](https://github.com/lhy0403/Qv2ray/tree/master) | 开发分支 [dev](https://github.com/lhy0403/Qv2ray/tree/dev) | 版本分支 [v1](https://github.com/lhy0403/Qv2ray/tree/version-v1) |
| ------- | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
| [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) |
| [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 Linux in dev](http://badges.herokuapp.com/travis.com/lhy0403/Qv2ray?style=flat-square&env=BADGE=osx&label=MacOS-dev&branch=dev) | ![Build Status Linux in dev](http://badges.herokuapp.com/travis.com/lhy0403/Qv2ray?style=flat-square&env=BADGE=osx&label=MacOS-v1&branch=version-v1) |
### 首次使用请查看 ➡ [用户手册](https://lhy0403.github.io/Qv2ray)
## Qv2ray 项目状态
### Jenkins CI
- 访问链接: [https://jenkins.lhy0403.top/](https://jenkins.lhy0403.top/)
| | Master | Development | Versioning-V1 | Pull Requests |
| -------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
| 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-PullRequest/badge/icon)](https://jenkins.lhy0403.top/job/Qv2ray-AppImage-PullRequest/) |
| Windows | [![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-PullRequest/badge/icon)](https://jenkins.lhy0403.top/job/Qv2ray-Win32-PullRequest/) |
### Travis & Appveyor
*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) |
| --------------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
| [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) |
| [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 Linux in dev](http://badges.herokuapp.com/travis.com/lhy0403/Qv2ray?style=flat-square&env=BADGE=osx&label=MacOS-dev&branch=dev) | ![Build Status Linux in dev](http://badges.herokuapp.com/travis.com/lhy0403/Qv2ray?style=flat-square&env=BADGE=osx&label=MacOS-v1&branch=version-v1) |
| [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) |
----------------
# 编译
- 依赖项:`Qt >= 5.12` `gcc >=8``MinGW` (Windows) 或 `clang` (MacOS)
- 建议使用 QtCreator
## Linux & MacOS
## 编译
- 对于 MacOS需要额外使用 HomeBrew 安装 Qt 并正确设定 $PATH 环境变量,或者使用 Qt 官方包 (包含 QtCreator)
### 依赖环境
- 框架依赖:`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
@ -44,13 +71,12 @@ git clone --recursive https://github.com/lhy0403/Qv2ray && cd Qv2ray
# 按需要签出开发分支
# git checkout dev
lrelease ./Qv2ray.pro
mkdir build && cd build
qmake ../
make
```
## Windows
### Windows
- 建议使用 Qt Creator
@ -68,6 +94,43 @@ qmake ../
mingw32-make.exe
```
# License
[![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
## 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 |
| 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 |
## 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)
```
Qv2ray, A Qt frontend for v2ray. Written in c++
Copyright (C) 2019 Leroy.H.Y (@lhy0403) ---> Qv2ray Current Maintainer
Copyright (C) 2019 Hork (@aliyuchang33) ---> Hv2ray Initial Idea and Designs
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
(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/>.
```

11
docs/FAQ.md Normal file
View File

@ -0,0 +1,11 @@
# 经常出现的问题
## 在启用了 tPtoxy 支持后 v2ray 启动失败
详细内容:启动 v2ray 时出现 `Segmentation Fault`
此问题是部分 linux 的 suid 功能受限引起的,具体错误分析可以参考 [#59](https://github.com/lhy0403/Qv2ray/issues/59)
- 解决方案:`sudo sysctl fs.suid_dumpable=1`
- 此操作不会保存 sysctl 设置,重启之后会失效。如果需要持久性修改此参数,请参考 [这篇blog](http://ssdxiao.github.io/linux/2017/03/20/Sysctl-not-applay-on-boot.html)
## 在 Ubuntu 系统下 UI 变得超级丑
- 解决方案:运行时添加 `--style fusion` 即可解决问题

View File

@ -4,7 +4,11 @@ Qv2ray 是一款跨平台,真正支持 Linux 桌面环境的 v2ray 客户端
## 1. 连接导入
- Qv2ray 支持从文件导入VMess 连接字符串导入配置文件
Qv2ray 支持以下位置导入配置文件
- 文件导入
- VMess 连接字符串
- 手动添加
## 2. 连接编辑
@ -18,7 +22,7 @@ Qv2ray 是一款跨平台,真正支持 Linux 桌面环境的 v2ray 客户端
## 3. 自动连接
- 在启动时不显示主窗口,直接连接到设置的服务器
- 该功能自 [v1.2.0](./ReleaseNote/1.2/ReleaseNote-v1.2.md) 版本添加
- 该功能自 [v1.2.0](./ReleaseNotes/1.2/ReleaseNote-v1.2.md) 版本添加
### 如要将某个连接设为自动连接,请打开首选项
@ -40,4 +44,9 @@ Qv2ray 是一款跨平台,真正支持 Linux 桌面环境的 v2ray 客户端
## 6. 实时显示 v2ray 的输出
- 查看 bug 更方便
- 查看 bug 更方便
## 7. 自动 tProxy 支持 Linux
- 一键对 v2ray 主程序启用透明代理功能 (`setcap`)

View File

@ -4,18 +4,20 @@
## 1.3.x
- [v1.3.6.1](./ReleaseNote/1.3/v1.3.6.1.md) UI 图标,翻译更新和一个列表顺序修复
- [v1.3.6.0](./ReleaseNote/1.3/v1.3.6.0.md) 没有发布
- [v1.3.5-3](./ReleaseNote/1.3/v1.3.5.3.md) 针对 OpenSSL 1.1.1b-c-d 的更新
- [v1.3.5-2](./ReleaseNote/1.3/v1.3.5.2.md) 修复连接列表顺序问题,修复 1.3.5 带来的 Bug
- [v1.3.5-0](./ReleaseNote/1.3/v1.3.5.0.md) 此版本具有严重 Bug ,仅提供更新回滚方法
- [v1.3.3](./ReleaseNote/1.3/v1.3.3.0.md) Windows 版自动更新功能修复
- [v1.3.2](./ReleaseNote/1.3/v1.3.2.0.md) 此版本共 7 个功能更新3 个问题修复
- [v1.3.0](./ReleaseNote/1.3/v1.3.0.0.md) 增加对于 ShadowSocks 的支持
- [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 图标更新,翻译更新和一个列表顺序修复
- [v1.3.6.0](./ReleaseNotes/1.3/v1.3.6.0.md) 没有发布
- [v1.3.5-3](./ReleaseNotes/1.3/v1.3.5.3.md) 针对 OpenSSL 1.1.1b-c-d 的更新
- [v1.3.5-2](./ReleaseNotes/1.3/v1.3.5.2.md) 修复连接列表顺序问题,修复 1.3.5 带来的 Bug
- [v1.3.5-0](./ReleaseNotes/1.3/v1.3.5.0.md) 此版本具有严重 Bug ,仅提供更新回滚方法
- [v1.3.3](./ReleaseNotes/1.3/v1.3.3.0.md) Windows 版自动更新功能修复
- [v1.3.2](./ReleaseNotes/1.3/v1.3.2.0.md) 此版本共 7 个功能更新3 个问题修复
- [v1.3.0](./ReleaseNotes/1.3/v1.3.0.0.md) 增加对于 ShadowSocks 的支持
## 1.2.x
- [v1.2.0](./ReleaseNote/1.2/v1.2.0.0.md) 此版本共 7 个功能更新2 个问题修复3 个功能改善
- [v1.2.0](./ReleaseNotes/1.2/v1.2.0.0.md) 此版本共 7 个功能更新2 个问题修复3 个功能改善
## 1.1.x
@ -23,10 +25,10 @@
## 1.0.x
- [v1.0.1](./ReleaseNote/1.0/v1.0.1.0.md) 修复 RC 版本中的拼写错误
- [v1.0.0](./ReleaseNote/1.0/v1.0.0.0.md) Qv2ray v1 发布候选版本 RC1
- [v1.0.1](./ReleaseNotes/1.0/v1.0.1.0.md) 修复 RC 版本中的拼写错误
- [v1.0.0](./ReleaseNotes/1.0/v1.0.0.0.md) Qv2ray v1 发布候选版本 RC1
## 0.9.x
- [v0.9.9 (BETA)](./ReleaseNote/1.0/v0.9.9.0.md) - 第一个公开测试版
- [v0.9.9 (BETA)](./ReleaseNotes/1.0/v0.9.9.0.md) - 第一个公开测试版

View File

@ -2,7 +2,7 @@
- 基于 Qt 的跨平台 v2ray 图形客户端
- #### 当前最新版本: [v1.3.6.1](./ReleaseNote/1.3/v1.3.6.1.md)
- #### 当前最新版本: [v1.3.7.1](./ReleaseNotes/1.3/v1.3.7.1.md)
--------
@ -25,10 +25,14 @@
见 [更新历史记录](./History.md)
## 4. 共同翻译 Qv2ray
## 4. 常见问题 FAQ
见 [FAQ.md](./FAQ.md)
## 5. 共同翻译 Qv2ray
翻译平台使用 Crowdin[https://crowdin.com/project/qv2ray](https://crowdin.com/project/qv2ray)
# 开源协议 Open Source License
GPLv3
GPLv3

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

View File

@ -8,9 +8,21 @@
#define QV2RAY_VERSION_STRING "v" QV_MAJOR_VERSION
#define QV2RAY_CONFIG_VERSION 4
// 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")
#else
// MacOS and Linux....
#define QV2RAY_V2RAY_CORE_PATH (QV2RAY_V2RAY_CORE_DIR_PATH + "v2ray")
#endif
#define QV2RAY_CONNECTION_FILE_EXTENSION ".qv2ray.json"
#define QV2RAY_GENERATED_FILE_PATH (QV2RAY_CONFIG_DIR_PATH + "generated/config.gen.json")
@ -18,13 +30,6 @@
#define QV2RAY_VCORE_ACCESS_LOG_FILENAME "access.log"
#define QV2RAY_VCORE_ERROR_LOG_FILENAME "error.log"
// These is for early-2.0 version, final 2.0 will move these content into global config.
#define QV2RAY_CONFIG_TYPE_FILE "File"
#define QV2RAY_CONFIG_TYPE_MANUAL "Manual"
#define QV2RAY_CONFIG_TYPE_CONNECTIONSTRING "ConnectionString"
#define QV2RAY_CONFIG_TYPE_SUBSCRIPTION "Subscription"
#define QV2RAY_CONFIG_TYPE_JSON_KEY "_qv2ray.configSource"
// GUI TOOLS
#define RED(obj) \
auto _temp = ui->obj->palette(); \
@ -78,14 +83,13 @@ namespace Qv2ray
int logLevel;
//
string language;
string v2CorePath;
string v2AssetsPath;
string autoStartConfig;
//
string ignoredVersion;
//
bool proxyDefault;
bool bypassCN;
bool enableProxy;
bool withLocalDNS;
list<string> dnsList;
//
@ -97,14 +101,13 @@ namespace Qv2ray
#endif
map<string, string> subscribes;
MuxObject mux;
Qv2rayConfig(): config_version(QV2RAY_CONFIG_VERSION), tProxySupport(false), logLevel(), proxyDefault(), bypassCN(), withLocalDNS(), inBoundSettings(), configs(), subscribes(), mux() { }
Qv2rayConfig(string lang, string exePath, string assetsPath, int log, Qv2rayBasicInboundsConfig _inBoundSettings): Qv2rayConfig()
Qv2rayConfig(): config_version(QV2RAY_CONFIG_VERSION), tProxySupport(false), logLevel(), bypassCN(), enableProxy(), withLocalDNS(), inBoundSettings(), configs(), subscribes(), mux() { }
Qv2rayConfig(string lang, string assetsPath, int log, Qv2rayBasicInboundsConfig _inBoundSettings): Qv2rayConfig()
{
// These settings below are defaults.
ignoredVersion = "";
autoStartConfig = "";
language = lang;
v2CorePath = exePath;
v2AssetsPath = assetsPath;
logLevel = log;
inBoundSettings = _inBoundSettings;
@ -114,12 +117,13 @@ namespace Qv2ray
dnsList.push_back("1.1.1.1");
dnsList.push_back("4.4.4.4");
bypassCN = true;
proxyDefault = true;
enableProxy = true;
withLocalDNS = true;
}
XTOSTRUCT(O(config_version, tProxySupport, logLevel, language, autoStartConfig, ignoredVersion, v2CorePath, v2AssetsPath, proxyDefault, bypassCN, withLocalDNS, dnsList, inBoundSettings, mux, configs, subscribes))
XTOSTRUCT(O(config_version, tProxySupport, logLevel, language, autoStartConfig, ignoredVersion, v2AssetsPath, enableProxy, bypassCN, withLocalDNS, dnsList, inBoundSettings, mux, configs, subscribes))
};
// Extra header for QvConfigUpgrade.cpp
QJsonObject UpgradeConfig(int fromVersion, int toVersion, QJsonObject root);
}
}

View File

@ -3,7 +3,7 @@
// from old to newer versions of Qv2ray.
//
#include "Qv2rayBase.h"
#include "QvUtils.h"
#define UPDATELOG(msg) LOG(MODULE_CONFIG, "[" + to_string(fromVersion) + "-" + to_string(fromVersion + 1) + "] --> " msg)
@ -21,29 +21,62 @@ namespace Qv2ray
//
switch (fromVersion) {
case 1:
case 1: {
// From 1 to 2, we changed the config_version from 'string' to 'int'
root.remove("config_version");
root["config_version"] = 2;
UPDATELOG("Upgrading config_version from old value " + v1_oldConfigVersion.toStdString() + " to 2")
break;
}
case 2:
case 2 : {
// We copied those files.
auto vCoreFilePath = root["v2CorePath"].toString();
auto vCoreDestPath = QV2RAY_V2RAY_CORE_PATH;
// We also need v2ctl
auto v2CtlFilePath = QFileInfo(vCoreFilePath).dir().path() + "/v2ctl";
auto v2CtlDestPath = QFileInfo(vCoreDestPath).dir().path() + "/v2ctl";
#ifdef __WIN32
v2CtlFilePath = v2CtlFilePath.append(".exe");
v2CtlDestPath = v2CtlDestPath.append(".exe");
#endif
QFile::copy(vCoreFilePath, vCoreDestPath);
QFile::copy(v2CtlFilePath, v2CtlDestPath);
root.remove("v2CorePath");
UPDATELOG("v2CorePath value from: " + vCoreFilePath.toStdString() + " to " + vCoreDestPath.toStdString())
UPDATELOG("v2CtlFilePath value from: " + v2CtlFilePath.toStdString() + " to " + v2CtlDestPath.toStdString())
break;
}
case 3 : {
// We changed a key name in the config file.
//proxyDefault
auto oldProxyDefault = root["proxyDefault"].toBool();
root.remove("proxyDefault");
root["enableProxy"] = oldProxyDefault;
//enableProxy
UPDATELOG("key: proxyDefault->enableProxy, value from: " + to_string(oldProxyDefault) + " to " + to_string(oldProxyDefault))
break;
}
case 4: {
// From 2 to 3, we changed the "proxyCN" to "bypassCN" as it's easier to understand....
root.remove("proxyCN");
root.insert("bypassCN", !v2_oldProxyCN);
UPDATELOG("Upgrading proxyCN to bypassCN and changed the value to " + to_string(!v2_oldProxyCN))
break;
}
case 3:
case 5: {
// From 3 to 4, we changed 'runAsRoot' to 'tProxySupport'
root.remove("runAsRoot");
root.insert("tProxySupport", v3_oldrunAsRoot);
UPDATELOG("Upgrading runAsRoot to tProxySupport, the value is not changed: " + to_string(v3_oldrunAsRoot))
break;
}
}
root.insert("config_version", fromVersion + 1);
root["config_version"] = root["config_version"].toInt() + 1;
return root;
}

View File

@ -16,10 +16,6 @@ namespace Qv2ray
{
namespace V2ConfigModels
{
struct VMessProtocolConfigObject {
string v, ps, add, port, id, aid, net, type, host, path, tls;
XTOSTRUCT(O(v, ps, add, port, id, aid, net, type, host, path, tls))
};
//
// Used in config generation
struct AccountObject {

View File

@ -17,7 +17,7 @@ namespace Qv2ray
namespace ConfigOperations
{
// -------------------------- BEGIN CONFIG GENERATIONS ---------------------------------------------
QJsonObject GenerateRoutes(bool globalProxy, bool cnProxy);
QJsonObject GenerateRoutes(bool enableProxy, bool cnProxy);
QJsonObject GenerateSingleRouteRule(QStringList list, bool isDomain, QString outboundTag, QString type = "field");
QJsonObject GenerateDNS(bool withLocalhost, QStringList dnsServers);
//
@ -57,7 +57,7 @@ namespace Qv2ray
bool SaveConnectionConfig(QJsonObject obj, const QString *alias);
bool RenameConnection(QString originalName, QString newName);
// VMess Protocol
QJsonObject ConvertConfigFromVMessString(QString vmess, QString source = QV2RAY_CONFIG_TYPE_CONNECTIONSTRING);
QJsonObject ConvertConfigFromVMessString(QString vmess);
QJsonObject ConvertConfigFromFile(QString sourceFilePath, bool overrideInbounds);
// Load Configs
QMap<QString, QJsonObject> GetConnections(list<string> connections);

View File

@ -7,25 +7,39 @@ namespace Qv2ray
bool SaveConnectionConfig(QJsonObject obj, const QString *alias)
{
QFile config(QV2RAY_CONFIG_DIR_PATH + *alias + QV2RAY_CONNECTION_FILE_EXTENSION);
return StringToFile(JsonToString(obj), &config);
auto str = JsonToString(obj);
return StringToFile(&str, &config);
}
// This generates global config containing only one outbound....
QJsonObject ConvertConfigFromVMessString(QString str, QString source)
QJsonObject ConvertConfigFromVMessString(QString str)
{
DROOT
QStringRef vmessJsonB64(&str, 8, str.length() - 8);
auto vmessConf = StructFromJsonString<VMessProtocolConfigObject>(Base64Decode(vmessJsonB64.toString()));
auto vmessConf = JsonFromString(Base64Decode(vmessJsonB64.toString()));
string ps, add, id, net, type, host, path, tls;
int port, aid;
ps = vmessConf["ps"].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();
host = vmessConf["host"].toVariant().toString().toStdString();
path = vmessConf["path"].toVariant().toString().toStdString();
tls = vmessConf["tls"].toVariant().toString().toStdString();
//
port = vmessConf["port"].toVariant().toInt();
aid = vmessConf["aid"].toVariant().toInt();
//
// User
VMessServerObject::UserObject user;
user.id = vmessConf.id;
user.alterId = stoi(vmessConf.aid);
user.id = id;
user.alterId = aid;
//
// Server
VMessServerObject serv;
serv.port = stoi(vmessConf.port);
serv.address = vmessConf.add;
serv.port = port;
serv.address = add;
serv.users.push_back(user);
//
// VMess root config
@ -37,36 +51,39 @@ namespace Qv2ray
// Stream Settings
StreamSettingsObject streaming;
// Fill hosts for HTTP
foreach (auto host, QString::fromStdString(vmessConf.host).split(',')) {
streaming.httpSettings.host.push_back(host.toStdString());
if (net == "tcp") {
streaming.tcpSettings.header.type = type;
} else if (net == "http") {
// Fill hosts for HTTP
foreach (auto _host, QString::fromStdString(host).split(',')) {
streaming.httpSettings.host.push_back(_host.toStdString());
}
streaming.httpSettings.path = path;
} else if (net == "ws") {
streaming.wsSettings.headers.insert(make_pair("Host", host));
streaming.wsSettings.path = path;
} else if (net == "kcp") {
streaming.kcpSettings.header.type = type;
} else if (net == "domainsocket") {
streaming.dsSettings.path = path;
} else if (net == "quic") {
streaming.quicSettings.security = host;
streaming.quicSettings.header.type = type;
streaming.quicSettings.key = path;
}
// hosts for ws, h2 and security for QUIC
streaming.wsSettings.headers.insert(make_pair("Host", vmessConf.host));
streaming.quicSettings.security = vmessConf.host;
//
// Fake type for tcp, kcp and QUIC
streaming.tcpSettings.header.type = vmessConf.type;
streaming.kcpSettings.header.type = vmessConf.type;
streaming.quicSettings.header.type = vmessConf.type;
//
// Path for ws, h2, Quic
streaming.wsSettings.path = vmessConf.path;
streaming.httpSettings.path = vmessConf.path;
streaming.quicSettings.key = vmessConf.path;
streaming.security = vmessConf.tls;
streaming.security = tls;
//
// Network type
streaming.network = vmessConf.net;
streaming.network = net;
//
auto outbound = GenerateOutboundEntry("vmess", vConf, GetRootObject(streaming), GetRootObject(GetGlobalConfig().mux), "0.0.0.0", OUTBOUND_TAG_PROXY);
//
QJsonArray outbounds;
outbounds.append(outbound);
root.insert("outbounds", outbounds);
root.insert("QV2RAY_ALIAS", QString::fromStdString(vmessConf.ps));
root.insert(QV2RAY_CONFIG_TYPE_JSON_KEY, source);
root.insert("QV2RAY_ALIAS", QString::fromStdString(ps));
RROOT
}
@ -82,7 +99,6 @@ namespace Qv2ray
JSON_ROOT_TRY_REMOVE("api")
JSON_ROOT_TRY_REMOVE("stats")
JSON_ROOT_TRY_REMOVE("dns")
root.insert(QV2RAY_CONFIG_TYPE_JSON_KEY, QV2RAY_CONFIG_TYPE_FILE);
return root;
}
@ -107,7 +123,7 @@ namespace Qv2ray
int StartPreparation(QJsonObject fullConfig)
{
QString json = JsonToString(fullConfig);
StringToFile(json, new QFile(QV2RAY_GENERATED_FILE_PATH));
StringToFile(&json, new QFile(QV2RAY_GENERATED_FILE_PATH));
return 0;
}

View File

@ -6,23 +6,28 @@ namespace Qv2ray
{
static const QStringList vLogLevels = {"none", "debug", "info", "warning", "error"};
// -------------------------- BEGIN CONFIG GENERATIONS ----------------------------------------------------------------------------
QJsonObject GenerateRoutes(bool globalProxy, bool bypassCN)
QJsonObject GenerateRoutes(bool enableProxy, bool proxyCN)
{
DROOT
root.insert("domainStrategy", "IPIfNonMatch");
//
// For Rules list
QJsonArray rulesList;
//
if (!enableProxy) {
// This is added to disable all proxies, as a alternative influence of #64
rulesList.append(GenerateSingleRouteRule(QStringList() << "regexp:.*", true, OUTBOUND_TAG_DIRECT));
}
// Private IPs should always NOT TO PROXY!
rulesList.append(GenerateSingleRouteRule(QStringList({"geoip:private"}), false, OUTBOUND_TAG_DIRECT));
rulesList.append(GenerateSingleRouteRule(QStringList() << "geoip:private", false, OUTBOUND_TAG_DIRECT));
//
// Check if CN needs proxy, or direct.
rulesList.append(GenerateSingleRouteRule(QStringList({"geoip:cn"}), false, bypassCN ? OUTBOUND_TAG_DIRECT : OUTBOUND_TAG_PROXY));
rulesList.append(GenerateSingleRouteRule(QStringList({"geosite:cn"}), true, bypassCN ? OUTBOUND_TAG_DIRECT : OUTBOUND_TAG_PROXY));
rulesList.append(GenerateSingleRouteRule(QStringList() << "geoip:cn", false, proxyCN ? OUTBOUND_TAG_DIRECT : OUTBOUND_TAG_PROXY));
rulesList.append(GenerateSingleRouteRule(QStringList() << "geosite:cn", true, proxyCN ? OUTBOUND_TAG_DIRECT : OUTBOUND_TAG_PROXY));
//
// Check global proxy, or direct.
rulesList.append(GenerateSingleRouteRule(QStringList({"regexp:.*"}), true, globalProxy ? OUTBOUND_TAG_PROXY : OUTBOUND_TAG_DIRECT));
// As a bug fix of #64, this default rule has been disabled.
//rulesList.append(GenerateSingleRouteRule(QStringList({"regexp:.*"}), true, globalProxy ? OUTBOUND_TAG_PROXY : OUTBOUND_TAG_DIRECT));
root.insert("rules", rulesList);
RROOT
}
@ -76,6 +81,8 @@ namespace Qv2ray
QJsonArray servers(QJsonArray::fromStringList(dnsServers));
if (withLocalhost) {
// https://github.com/lhy0403/Qv2ray/issues/64
// The fix patch didn't touch this line below.
servers.append("localhost");
}
@ -185,11 +192,9 @@ namespace Qv2ray
// Note: The part below always makes the whole functionality in trouble......
// BE EXTREME CAREFUL when changing these code below...
//
// For SOME configs, there is no "route" entries, so, we add some...
// We don't use QV2RAY_CONFIG_TYPE_FILE to check because not all IMPORTED connections have routings.
if (!root.contains("routing")) {
if (root["outbounds"].toArray().count() != 1) {
// There are no ROUTING but 2 or more outbounds.... This is rare, but possible.
@ -198,7 +203,7 @@ namespace Qv2ray
}
LOG(MODULE_CONNECTION, "Current connection has NO ROUTING section, we insert default values.")
auto routeObject = GenerateRoutes(gConf.proxyDefault, gConf.bypassCN);
auto routeObject = GenerateRoutes(gConf.enableProxy, gConf.bypassCN);
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));

View File

@ -16,10 +16,15 @@ namespace Qv2ray
try {
QStringRef vmessJsonB64(&vmess, 8, vmess.length() - 8);
auto vmessString = Base64Decode(vmessJsonB64.toString());
auto vmessConf = StructFromJsonString<VMessProtocolConfigObject>(vmessString);
return 0;
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");
#undef C
return flag ? 0 : 1;
} catch (exception *e) {
LOG(MODULE_CONNECTION, QObject::tr("#VMessDecodeError").toStdString() << e->what())
LOG(MODULE_CONNECTION, "Failed to decode vmess string: " << e->what())
return -2;
}
}

View File

@ -1,56 +1,58 @@
#include <QObject>
#include <QWidget>
#include <QDesktopServices>
#include "QvCoreInteractions.h"
#include "QvCoreConfigOperations.h"
#include "QvTinyLog.h"
#include "w_MainWindow.h"
namespace Qv2ray
{
bool Qv2Instance::VerifyVConfigFile(const QString path)
bool Qv2Instance::VerifyVConfigFile(const QString *path)
{
if (ValidateV2rayCoreExe()) {
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
env.insert("V2RAY_LOCATION_ASSET", QString::fromStdString(GetGlobalConfig().v2AssetsPath));
QProcess process;
process.setProcessEnvironment(env);
process.start(QString::fromStdString(Utils::GetGlobalConfig().v2CorePath), QStringList() << "-test"
<< "-config" << path,
QIODevice::ReadWrite | QIODevice::Text);
process.start(QV2RAY_V2RAY_CORE_PATH, QStringList() << "-test" << "-config" << *path, QIODevice::ReadWrite | QIODevice::Text);
if (!process.waitForFinished()) {
qDebug() << "v2ray core failed with exit code " << process.exitCode();
LOG(MODULE_VCORE, "v2ray core failed with exitcode: " << process.exitCode())
return false;
}
QString output = QString(process.readAllStandardOutput());
if (!output.contains("Configuration OK")) {
Utils::QvMessageBox(nullptr, QObject::tr("ConfigurationError"), output.mid(output.indexOf("anti-censorship.") + 17));
if (process.exitCode() != 0) {
Utils::QvMessageBox(nullptr, QObject::tr("Configuration Error"), output.mid(output.indexOf("anti-censorship.") + 17));
return false;
} else
return true;
} else
return false;
}
return true;
}
return false;
}
Qv2Instance::Qv2Instance(QWidget *parent)
{
QProcess *proc = new QProcess();
auto proc = new QProcess();
vProcess = proc;
QObject::connect(vProcess, &QProcess::readyReadStandardOutput, static_cast<MainWindow *>(parent), &MainWindow::UpdateLog);
Status = STOPPED;
}
QString Qv2Instance::ReadProcessOutput()
{
return vProcess->readAllStandardOutput();
}
bool Qv2Instance::ValidateV2rayCoreExe()
{
auto path = QString::fromStdString(Utils::GetGlobalConfig().v2CorePath);
if (!QFile::exists(path)) {
Utils::QvMessageBox(nullptr, QObject::tr("CoreNotFound"), QObject::tr("CoreFileNotFoundExplainationAt:") + path);
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);
return false;
} else return true;
}
@ -64,13 +66,13 @@ namespace Qv2ray
Status = STARTING;
if (ValidateV2rayCoreExe()) {
if (VerifyVConfigFile(QV2RAY_GENERATED_FILE_PATH)) {
auto filePath = QV2RAY_GENERATED_FILE_PATH;
if (VerifyVConfigFile(&filePath)) {
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
env.insert("V2RAY_LOCATION_ASSET", QString::fromStdString(GetGlobalConfig().v2AssetsPath));
vProcess->setProcessEnvironment(env);
vProcess->start(QString::fromStdString(GetGlobalConfig().v2CorePath), QStringList() << "-config"
<< QV2RAY_GENERATED_FILE_PATH,
QIODevice::ReadWrite | QIODevice::Text);
vProcess->start(QV2RAY_V2RAY_CORE_PATH, QStringList() << "-config" << filePath, QIODevice::ReadWrite | QIODevice::Text);
vProcess->waitForStarted();
Status = STARTED;
return true;

View File

@ -20,7 +20,7 @@ namespace Qv2ray
bool Start();
void Stop();
V2RAY_INSTANCE_STARTUP_STATUS Status;
static bool VerifyVConfigFile(QString path);
static bool VerifyVConfigFile(const QString *path);
static bool ValidateV2rayCoreExe();
QString ReadProcessOutput();

View File

@ -7,9 +7,9 @@ namespace Qv2ray
{
static Qv2rayConfig GlobalConfig;
static QString ConfigDirPath;
void SetGlobalConfig(Qv2rayConfig conf)
void SetGlobalConfig(const Qv2rayConfig *conf)
{
GlobalConfig = conf;
GlobalConfig = *conf;
}
Qv2rayConfig GetGlobalConfig()
@ -22,16 +22,16 @@ namespace Qv2ray
return ConfigDirPath;
}
void SetConfigDirPath(QString path)
void SetConfigDirPath(const QString *path)
{
ConfigDirPath = path;
ConfigDirPath = *path;
}
void SaveGlobalConfig()
{
QFile config(QV2RAY_CONFIG_FILE_PATH);
QString str = StructToJsonString(GetGlobalConfig());
StringToFile(str, &config);
StringToFile(&str, &config);
}
QString Stringify(list<string> list, QString saperator)
@ -64,12 +64,12 @@ namespace Qv2ray
return out;
}
bool StringToFile(QString text, QFile *targetFile)
bool StringToFile(const QString *text, QFile *targetFile)
{
bool override = targetFile->exists();
targetFile->open(QFile::WriteOnly);
QTextStream stream(targetFile);
stream << text << endl;
stream << *text << endl;
stream.flush();
targetFile->close();
return override;
@ -124,7 +124,7 @@ namespace Qv2ray
QTextStream stream(&file);
auto str = stream.readAll();
auto config = StructFromJsonString<Qv2rayConfig>(str);
SetGlobalConfig(config);
SetGlobalConfig(&config);
file.close();
}

View File

@ -18,10 +18,10 @@ namespace Qv2ray
bool CheckFile(QDir *dir, QString fileName);
void SetConfigDirPath(QString path);
void SetConfigDirPath(const QString *path);
QString GetConfigDirPath();
void SetGlobalConfig(Qv2rayConfig conf);
void SetGlobalConfig(const Qv2rayConfig *conf);
Qv2rayConfig GetGlobalConfig();
void SaveGlobalConfig();
@ -30,7 +30,7 @@ namespace Qv2ray
void QvMessageBox(QWidget *parent, QString title, QString text);
int QvMessageBoxAsk(QWidget *parent, QString title, QString text, QMessageBox::StandardButton extraButtons = QMessageBox::NoButton);
//
bool StringToFile(QString text, QFile *target);
bool StringToFile(const QString *text, QFile *target);
QString StringFromFile(QFile *sourceFile);
//
QJsonObject JsonFromString(QString string);

View File

@ -9,96 +9,67 @@
#include "QvRunguard.h"
#include "w_MainWindow.h"
using namespace Qv2ray;
using namespace Qv2ray::Utils;
using namespace Qv2ray::QvConfigModels;
bool initQv()
{
#ifdef QT_DEBUG
QString configPath = QDir::homePath() + "/.qv2ray_debug";
#else
QString configPath = QDir::homePath() + "/.qv2ray";
#endif
/// Qv2ray Config Path and ends with "/"
QString exeDefaultPath = configPath + "/vcore/v2ray";
QString v2AssetsPath = configPath + "/vcore";
//
#if defined(__WIN32)
exeDefaultPath = exeDefaultPath + ".exe";
#elif defined(__linux__)
// Special case for GNU/Linux
//
// Unused these values
Q_UNUSED(v2AssetsPath)
Q_UNUSED(exeDefaultPath)
v2AssetsPath = "/etc/v2ray";
exeDefaultPath = "/bin/v2ray";
#endif
//
SetConfigDirPath(configPath);
auto ConfigDir = new QDir(configPath);
if (!ConfigDir->exists()) {
auto result = QDir().mkdir(QV2RAY_CONFIG_DIR_PATH);
if (result) {
LOG(MODULE_INIT, "Created Qv2ray config dir at: " + QV2RAY_CONFIG_DIR_PATH.toStdString())
} else {
LOG(MODULE_INIT, "Failed to create config dir at: " + QV2RAY_CONFIG_DIR_PATH.toStdString())
return false;
}
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())
}
auto genPath = QV2RAY_CONFIG_DIR_PATH + "generated/";
if (!QDir(genPath).exists()) {
auto result2 = QDir().mkdir(genPath);
if (result2) {
LOG(MODULE_INIT, "Created config generation dir at: " + genPath.toStdString())
} else {
LOG(MODULE_INIT, "Failed to create config generation dir at: " + genPath.toStdString())
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 configFile(QV2RAY_CONFIG_FILE_PATH);
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.")
}
if (!configFile.exists()) {
// This is first run!
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", exeDefaultPath.toStdString(), v2AssetsPath.toStdString(), 2, inboundSetting);
Qv2rayConfig conf = Qv2rayConfig("zh-CN", QV2RAY_V2RAY_CORE_DIR_PATH.toStdString(), 4, inboundSetting);
//
// Save initial config.
SetGlobalConfig(conf);
SetGlobalConfig(&conf);
SaveGlobalConfig();
//
LOG(MODULE_INIT, "Created initial config file.")
} else {
// Some config file upgrades.
auto conf = JsonFromString(StringFromFile(&configFile));
auto conf = JsonFromString(StringFromFile(&qvConfigFile));
auto confVersion = conf["config_version"].toVariant().toString();
auto newVersion = QSTRING(to_string(QV2RAY_CONFIG_VERSION));
if (confVersion != newVersion) {
// 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" +
QObject::tr("Qv2ray will now exit."));
return false;
} else if (QString::compare(confVersion, newVersion) != 0) {
conf = UpgradeConfig(stoi(confVersion.toStdString()), QV2RAY_CONFIG_VERSION, conf);
}
try {
auto confObject = StructFromJsonString<Qv2rayConfig>(JsonToString(conf));
SetGlobalConfig(confObject);
SaveGlobalConfig();
LOG(MODULE_INIT, "Loaded config file.")
} catch (...) {
LOG(MODULE_INIT, "FAILED TO LOAD config file. This is an error and should be reported, Qv2ray will now exit.")
return false;
}
auto confObject = StructFromJsonString<Qv2rayConfig>(JsonToString(conf));
SetGlobalConfig(&confObject);
SaveGlobalConfig();
LOG(MODULE_INIT, "Loaded config file.")
return true;
}
return true;
return false;
}
int main(int argc, char *argv[])
@ -111,6 +82,7 @@ int main(int argc, char *argv[])
"Hv2ray Initial Idea and Designs 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"
"Qv2ray " QV2RAY_VERSION_STRING " running on " +
QSysInfo::prettyProductName().toStdString() + " " + QSysInfo::currentCpuArchitecture().toStdString() +
@ -118,7 +90,11 @@ int main(int argc, char *argv[])
//
#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");
if (!it.hasNext()) {

View File

@ -150,7 +150,7 @@ void ConnectionEditWindow::ReLoad_GUI_JSON_ModelContent()
void ConnectionEditWindow::on_buttonBox_accepted()
{
// TODO : NAMING THE CONNECTION
auto alias = Alias == "" ? (ui->ipLineEdit->text() + "_" + ui->portLineEdit->text()) : Alias;
auto alias = Alias == "" ? (ui->ipLineEdit->text() + "_" + ui->portLineEdit->text()) : Alias;
//
Result = GenerateConnectionJson();
//QJsonArray outbounds;

View File

@ -17,11 +17,11 @@ class ConnectionEditWindow : public QDialog
public:
explicit ConnectionEditWindow(QWidget *parent = nullptr);
explicit ConnectionEditWindow(QJsonObject editRootObject, QString *alias, QWidget *parent = nullptr);
QJsonObject Result;
QString Tag;
QString Alias;
QJsonObject OpenEditor();
explicit ConnectionEditWindow(QJsonObject outboundEntry, QString *alias, QWidget *parent = nullptr);
~ConnectionEditWindow();
signals:
void s_reload_config(bool need_restart);

View File

@ -45,8 +45,9 @@ void ImportConfigWindow::on_buttonBox_accepted()
if (ui->importSourceCombo->currentIndex() == 0) {
// From File...
bool overrideInBound = !ui->keepImportedInboundCheckBox->isChecked();
auto fileName = ui->fileLineTxt->text();
if (!Qv2Instance::VerifyVConfigFile(ui->fileLineTxt->text())) {
if (!Qv2Instance::VerifyVConfigFile(&fileName)) {
QvMessageBox(this, tr("Import config file"), tr("Failed to check the validity of the config file."));
return;
}
@ -84,7 +85,7 @@ void ImportConfigWindow::on_buttonBox_accepted()
//
conf.configs.push_back(alias.toStdString());
//
SetGlobalConfig(conf);
SetGlobalConfig(&conf);
auto needReload = SaveConnectionConfig(config, &alias);
emit s_reload_config(needReload);
}

View File

@ -15,11 +15,11 @@
#include <windows.h>
#endif
#include "w_PrefrencesWindow.h"
#include "w_ImportConfig.h"
#include "w_ConnectionEditWindow.h"
#include "w_ImportConfig.h"
#include "w_MainWindow.h"
#include "w_RouteEditor.h"
#include "w_PrefrencesWindow.h"
#include "w_SubscribeEditor.h"
#define TRAY_TOOLTIP_PREFIX "Qv2ray " QV2RAY_VERSION_STRING "\r\n"
@ -76,23 +76,29 @@ MainWindow::MainWindow(QWidget *parent)
//
if (!vinstance->ValidateV2rayCoreExe()) {
on_prefrencesBtn_clicked();
}
auto conf = GetGlobalConfig();
if (conf.autoStartConfig != "" && QList<string>::fromStdList(conf.configs).contains(conf.autoStartConfig)) {
CurrentConnectionName = QSTRING(conf.autoStartConfig);
auto item = ui->connectionListWidget->findItems(QSTRING(conf.autoStartConfig), Qt::MatchExactly).front();
item->setSelected(true);
ui->connectionListWidget->setCurrentItem(item);
on_connectionListWidget_itemClicked(item);
on_startButton_clicked();
//ToggleVisibility();
this->hide();
trayMenu->actions()[0]->setText(tr("Show"));
QDesktopServices::openUrl(QUrl::fromLocalFile(QV2RAY_V2RAY_CORE_DIR_PATH));
} else {
this->show();
auto conf = GetGlobalConfig();
if (conf.autoStartConfig != "" && QList<string>::fromStdList(conf.configs).contains(conf.autoStartConfig)) {
CurrentConnectionName = QSTRING(conf.autoStartConfig);
auto item = ui->connectionListWidget->findItems(QSTRING(conf.autoStartConfig), Qt::MatchExactly).front();
item->setSelected(true);
ui->connectionListWidget->setCurrentItem(item);
on_connectionListWidget_itemClicked(item);
on_startButton_clicked();
//ToggleVisibility();
this->hide();
trayMenu->actions()[0]->setText(tr("Show"));
} else {
this->show();
if (ui->connectionListWidget->count() != 0) {
// The first one is default.
ui->connectionListWidget->setCurrentRow(0);
ShowAndSetConnection(ui->connectionListWidget->item(0)->text(), true, false);
}
}
}
}
@ -131,7 +137,7 @@ void MainWindow::VersionUpdate(QByteArray &data)
QDesktopServices::openUrl(QUrl::fromUserInput(link));
} else if (result == QMessageBox::Ignore) {
conf.ignoredVersion = newversion.toString().toStdString();
SetGlobalConfig(conf);
SetGlobalConfig(&conf);
save_reload_globalconfig(false);
}
}
@ -306,11 +312,8 @@ void MainWindow::QTextScrollToBottom()
if (bar->value() >= bar->maximum() - 10) bar->setValue(bar->maximum());
}
void MainWindow::ShowAndSetConnection(int index, bool SetConnection, bool ApplyConnection)
void MainWindow::ShowAndSetConnection(QString guiConnectionName, bool SetConnection, bool ApplyConnection)
{
if (index < 0) return;
auto guiConnectionName = ui->connectionListWidget->item(index)->text();
// --------- BRGIN Show Connection
auto outBoundRoot = (connections[guiConnectionName])["outbounds"].toArray().first().toObject();
//
@ -363,7 +366,12 @@ void MainWindow::on_connectionListWidget_itemClicked(QListWidgetItem *item)
{
Q_UNUSED(item)
int currentRow = ui->connectionListWidget->currentRow();
ShowAndSetConnection(currentRow, !isRenamingInProgress && (vinstance->Status != STARTED), false);
if (currentRow < 0) return;
QString currentText = ui->connectionListWidget->currentItem()->text();
bool canSetConnection = !isRenamingInProgress && vinstance->Status != STARTED ;
ShowAndSetConnection(currentText, canSetConnection, false);
}
void MainWindow::on_prefrencesBtn_clicked()
@ -375,7 +383,13 @@ void MainWindow::on_prefrencesBtn_clicked()
void MainWindow::on_connectionListWidget_doubleClicked(const QModelIndex &index)
{
ShowAndSetConnection(index.row(), true, true);
Q_UNUSED(index)
int currentRow = ui->connectionListWidget->currentRow();
if (currentRow < 0) return;
QString currentText = ui->connectionListWidget->currentItem()->text();
ShowAndSetConnection(currentText, true, true);
}
void MainWindow::on_clearlogButton_clicked()
@ -386,7 +400,7 @@ void MainWindow::on_clearlogButton_clicked()
void MainWindow::on_connectionListWidget_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous)
{
Q_UNUSED(previous)
isRenamingInProgress = true;
isRenamingInProgress = false;
on_connectionListWidget_itemClicked(current);
}
@ -402,14 +416,14 @@ void MainWindow::on_action_RenameConnection_triggered()
item->setFlags(item->flags() | Qt::ItemIsEditable);
ui->connectionListWidget->editItem(item);
originalName = item->text();
isRenamingInProgress = false;
isRenamingInProgress = true;
}
void MainWindow::on_connectionListWidget_itemChanged(QListWidgetItem *item)
{
LOG(MODULE_UI, "A connection ListViewItem is changed.")
if (!isRenamingInProgress) {
if (isRenamingInProgress) {
// In this case it's after we entered the name.
LOG(MODULE_CONNECTION, "RENAME: " + originalName.toStdString() + " -> " + item->text().toStdString())
auto newName = item->text();
@ -417,16 +431,16 @@ void MainWindow::on_connectionListWidget_itemChanged(QListWidgetItem *item)
auto configList = QList<string>::fromStdList(config.configs);
if (newName.trimmed().isEmpty()) {
QvMessageBox(this, tr("Rename A Connection"), tr("A name cannot be empty"));
QvMessageBox(this, tr("Rename a Connection"), tr("The name cannot be empty"));
return;
}
// If I really did some changes.
LOG("RENAME", "ORIGINAL: " + originalName.toStdString() + ", NEW: " + newName.toStdString())
// If I really did some changes.
if (originalName != newName) {
if (configList.contains(newName.toStdString())) {
QvMessageBox(this, tr("Rename A Connection"), tr("The name has been used already, Please choose another."));
QvMessageBox(this, tr("Rename a Connection"), tr("The name has been used already, Please choose another."));
return;
}
@ -439,7 +453,7 @@ void MainWindow::on_connectionListWidget_itemChanged(QListWidgetItem *item)
//
RenameConnection(originalName, newName);
//
SetGlobalConfig(config);
SetGlobalConfig(&config);
bool running = CurrentConnectionName == originalName;
if (running) CurrentConnectionName = newName;
@ -453,7 +467,7 @@ void MainWindow::on_connectionListWidget_itemChanged(QListWidgetItem *item)
void MainWindow::on_removeConfigButton_clicked()
{
if (QvMessageBoxAsk(this, tr("Removing A Connection"), tr("Are you sure to remove this connection?")) == QMessageBox::Yes) {
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();
@ -468,7 +482,7 @@ void MainWindow::on_removeConfigButton_clicked()
list.removeOne(ui->connectionListWidget->item(currentSelected)->text().toStdString());
conf.configs = list.toStdList();
SetGlobalConfig(conf);
SetGlobalConfig(&conf);
save_reload_globalconfig(isRemovingItemRunning);
}
}
@ -494,10 +508,8 @@ void MainWindow::on_addConfigButton_clicked()
void MainWindow::on_editConfigButton_clicked()
{
// Check if we have a connection selected...
auto index = ui->connectionListWidget->currentIndex().row();
if (index < 0) {
QvMessageBox(this, tr("NoConfigSelected"), tr("PleaseSelectAConfig"));
if (ui->connectionListWidget->currentIndex().row() < 0) {
QvMessageBox(this, tr("No Config Selected"), tr("Please Select a Config"));
return;
}
@ -510,14 +522,32 @@ void MainWindow::on_editConfigButton_clicked()
LOG(MODULE_UI, "WARNING:")
LOG(MODULE_UI, "THIS FEATURE IS NOT IMPLEMENTED YET!")
delete w;
/*
auto outBoundRoot = connections[alias];
if (outBoundRoot["outbounds"].toArray().count() > 1) {
/// Complicated version, currently not support editing.
if (QvMessageBoxAsk(this, tr("Not Supported"), tr("Qv2ray currently does not support editing complex configs.") + "\r\n" +
tr("Do you want to edit the config file manually?")) == QMessageBox::StandardButton::Yes) {
QDesktopServices::openUrl(QUrl::fromLocalFile(QV2RAY_CONFIG_DIR_PATH + alias + QV2RAY_CONNECTION_FILE_EXTENSION));
}
return;
}
ConnectionEditWindow *w = new ConnectionEditWindow(outBoundRoot, &alias, this);
connect(w, &ConnectionEditWindow::s_reload_config, this, &MainWindow::save_reload_globalconfig);
w->show();
*/
}
void MainWindow::on_editConfigAdvButton_clicked()
{
// Check if we have a connection selected...
auto index = ui->connectionListWidget->currentIndex().row();
if (index < 0) {
QvMessageBox(this, tr("#NoConfigSelected"), tr("#PleaseSelectAConfig"));
QvMessageBox(this, tr("No config selected"), tr("Please select a config."));
return;
}
@ -526,8 +556,7 @@ void MainWindow::on_editConfigAdvButton_clicked()
w->exec();
}
void MainWindow::on_pushButton_clicked()
void MainWindow::on_reconnectButton_clicked()
{
SubscribeEditor *w = new SubscribeEditor(this);
w->show();
on_restartButton_clicked();
}

View File

@ -58,8 +58,8 @@ class MainWindow : public QMainWindow
void on_editConfigButton_clicked();
void on_editConfigAdvButton_clicked();
void on_pushButton_clicked();
void on_reconnectButton_clicked();
private:
void on_action_StartThis_triggered();
@ -77,7 +77,7 @@ class MainWindow : public QMainWindow
QString originalName;
bool isRenamingInProgress;
//
void ShowAndSetConnection(int index, bool SetConnection, bool Apply);
void ShowAndSetConnection(QString currentText, bool SetConnection, bool Apply);
void LoadConnections();
void closeEvent(QCloseEvent *);
};

View File

@ -59,7 +59,7 @@
</widget>
</item>
<item>
<widget class="QPushButton" name="restartButton">
<widget class="QPushButton" name="reconnectButton">
<property name="text">
<string>Reconnect</string>
</property>
@ -246,7 +246,7 @@
<string>A</string>
</property>
<property name="icon">
<iconset resource="../resources.qrc">
<iconset resource="../../resources.qrc">
<normaloff>:/icons/add_connection_btn.png</normaloff>:/icons/add_connection_btn.png</iconset>
</property>
</widget>
@ -266,7 +266,7 @@
<string>I</string>
</property>
<property name="icon">
<iconset resource="../resources.qrc">
<iconset resource="../../resources.qrc">
<normaloff>:/icons/import_connection_btn.png</normaloff>:/icons/import_connection_btn.png</iconset>
</property>
</widget>
@ -280,7 +280,7 @@
<string>...</string>
</property>
<property name="icon">
<iconset resource="../resources.qrc">
<iconset resource="../../resources.qrc">
<normaloff>:/icons/edit_connection_btn.png</normaloff>:/icons/edit_connection_btn.png</iconset>
</property>
</widget>
@ -300,7 +300,7 @@
<string>R</string>
</property>
<property name="icon">
<iconset resource="../resources.qrc">
<iconset resource="../../resources.qrc">
<normaloff>:/icons/remove_connection_btn.png</normaloff>:/icons/remove_connection_btn.png</iconset>
</property>
</widget>
@ -393,7 +393,7 @@
<tabstop>clearlogButton</tabstop>
</tabstops>
<resources>
<include location="../resources.qrc"/>
<include location="../../resources.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -5,10 +5,6 @@
#include <iostream>
#ifdef __linux
#include <unistd.h>
#endif
#define NEEDRESTART if(finishedLoading) IsConnectionPropertyChanged = true;
PrefrencesWindow::PrefrencesWindow(QWidget *parent) : QDialog(parent),
@ -63,15 +59,16 @@ PrefrencesWindow::PrefrencesWindow(QWidget *parent) : QDialog(parent),
ui->socksPortLE->setValidator(new QIntValidator());
//
//
ui->vCoreExePathTxt->setText(QSTRING(CurrentConfig.v2CorePath));
ui->vCoreAssetsPathTxt->setText(QSTRING(CurrentConfig.v2AssetsPath));
//
//
ui->muxEnabledCB->setChecked(CurrentConfig.mux.enabled);
ui->muxConcurrencyTxt->setValue(CurrentConfig.mux.concurrency);
//
//
ui->bypassCNCb->setChecked(CurrentConfig.bypassCN);
ui->proxyDefaultCb->setChecked(CurrentConfig.proxyDefault);
ui->proxyDefaultCb->setChecked(CurrentConfig.enableProxy);
//
ui->localDNSCb->setChecked(CurrentConfig.withLocalDNS);
//
ui->DNSListTxt->clear();
@ -109,7 +106,7 @@ void PrefrencesWindow::on_buttonBox_accepted()
return;
}
SetGlobalConfig(CurrentConfig);
SetGlobalConfig(&CurrentConfig);
emit s_reload_config(IsConnectionPropertyChanged);
}
@ -178,12 +175,6 @@ void PrefrencesWindow::on_logLevelComboBox_currentIndexChanged(int index)
CurrentConfig.logLevel = index;
}
void PrefrencesWindow::on_vCoreExePathTxt_textEdited(const QString &arg1)
{
NEEDRESTART
CurrentConfig.v2CorePath = arg1.toStdString();
}
void PrefrencesWindow::on_vCoreAssetsPathTxt_textEdited(const QString &arg1)
{
NEEDRESTART
@ -247,7 +238,7 @@ void PrefrencesWindow::on_socksAuthPasswordTxt_textEdited(const QString &arg1)
void PrefrencesWindow::on_proxyDefaultCb_stateChanged(int arg1)
{
NEEDRESTART
CurrentConfig.proxyDefault = arg1 == Qt::Checked;
CurrentConfig.enableProxy = arg1 == Qt::Checked;
}
void PrefrencesWindow::on_localDNSCb_stateChanged(int arg1)
@ -256,23 +247,6 @@ void PrefrencesWindow::on_localDNSCb_stateChanged(int arg1)
CurrentConfig.withLocalDNS = arg1 == Qt::Checked;
}
void PrefrencesWindow::on_selectVCoreBtn_clicked()
{
NEEDRESTART
QString path = QFileDialog::getOpenFileName(this, tr("Open v2ray core file"), QDir::currentPath());
ui->vCoreExePathTxt->setText(path);
on_vCoreExePathTxt_textEdited(path);
// If we enabled tProxy feature... then not to change this automatically
if (CurrentConfig.tProxySupport) {
LOG(MODULE_CONFIG, "Not to automatically update v2ray assets path, because tProxy feature is enabled.")
} else {
auto dir = QFileInfo(path).dir().path();
ui->vCoreAssetsPathTxt->setText(dir);
on_vCoreAssetsPathTxt_textEdited(dir);
}
}
void PrefrencesWindow::on_selectVAssetBtn_clicked()
{
NEEDRESTART
@ -322,36 +296,52 @@ void PrefrencesWindow::on_cancelIgnoreVersionBtn_clicked()
void PrefrencesWindow::on_tProxyCheckBox_stateChanged(int arg1)
{
#ifdef __linux
LOG(MODULE_UI, "WARN: This feature is on development.")
// Set UID and GID for linux
// Steps:
// --> 1. Copy v2ray core files to the #CONFIG_DIR#/vcore/ dir.
// --> 2. Change GlobalConfig.v2CorePath.
// --> 3. Call `pkexec setcap SOMECAP` on the v2ray core.
QString vCorePath = QString::fromStdString(CurrentConfig.v2CorePath);
QFileInfo v2rayCoreExeFile(vCorePath);
if (arg1 == Qt::Checked && v2rayCoreExeFile.ownerId() != 0) {
QProcess::execute("pkexec", QStringList() << "bash"
<< "-c"
<< "chown root:root " + vCorePath + " && "
<< "chmod +s " + vCorePath);
CurrentConfig.tProxySupport = true;
} else if (arg1 != Qt::Checked && v2rayCoreExeFile.ownerId() == 0) {
uid_t uid = getuid();
gid_t gid = getgid();
QProcess::execute("pkexec", QStringList()
<< "chown" << QString::number(uid) + ":" + QString::number(gid)
<< vCorePath);
CurrentConfig.tProxySupport = false;
if (finishedLoading) {
//LOG(MODULE_UI, "Running getcap....")
//QProcess::execute("getcap " + QV2RAY_V2RAY_CORE_PATH);
// Set UID and GID for linux
// Steps:
// --> 1. Copy v2ray core files to the #CONFIG_DIR#/vcore/ dir.
// --> 2. Change GlobalConfig.v2CorePath.
// --> 3. Call `pkexec setcap CAP_NET_ADMIN,CAP_NET_RAW,CAP_NET_BIND_SERVICE=eip` on the v2ray core.
if (arg1 == Qt::Checked) {
// We enable it!
if (QvMessageBoxAsk(this, tr("Enable tProxy Support"), tr("This will append capabilities to the v2ray executable.") + "\r\n"
+ tr("If anything goes wrong after enabling this, please refer to issue #57 or the link below:") + "\r\n" +
" https://github.com/lhy0403/Qv2ray/blob/master/docs/FAQ.md ") != QMessageBox::Yes) {
ui->tProxyCheckBox->setChecked(false);
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);
if (ret != 0) {
LOG(MODULE_UI, "WARN: setcap exits with code: " + to_string(ret))
QvMessageBox(this, tr("Prefrences"), tr("Failed to setcap onto v2ray executable. You may need to run `setcap` manually."));
}
CurrentConfig.tProxySupport = true;
NEEDRESTART
} else {
int ret = QProcess::execute("pkexec setcap -r " + QV2RAY_V2RAY_CORE_PATH);
if (ret != 0) {
LOG(MODULE_UI, "WARN: setcap exits with code: " + to_string(ret))
QvMessageBox(this, tr("Prefrences"), tr("Failed to setcap onto v2ray executable. You may need to run `setcap` manually."));
}
CurrentConfig.tProxySupport = false;
NEEDRESTART
}
}
NEEDRESTART
#else
Q_UNUSED(arg1)
ui->tProxyCheckBox->setChecked(false);
// No such uid gid thing on Windows and MacOS is in TODO ....
QvMessageBox(this, tr("tProxy"), tr("tProxy is not supported on MacOS and Windows"));
// No such uid gid thing on Windows and MacOS
QvMessageBox(this, tr("Prefrences"), tr("tProxy is not supported on MacOS and Windows"));
#endif
}
void PrefrencesWindow::on_bypassCNCb_stateChanged(int arg1)

View File

@ -35,8 +35,6 @@ class PrefrencesWindow : public QDialog
void on_logLevelComboBox_currentIndexChanged(int index);
void on_vCoreExePathTxt_textEdited(const QString &arg1);
void on_vCoreAssetsPathTxt_textEdited(const QString &arg1);
void on_muxEnabledCB_stateChanged(int arg1);
@ -61,8 +59,6 @@ class PrefrencesWindow : public QDialog
void on_localDNSCb_stateChanged(int arg1);
void on_selectVCoreBtn_clicked();
void on_selectVAssetBtn_clicked();
void on_DNSListTxt_textChanged();

View File

@ -142,34 +142,13 @@
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_14">
<property name="text">
<string>v2ray Core Path</string>
</property>
</widget>
</item>
<item row="4" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLineEdit" name="vCoreExePathTxt"/>
</item>
<item>
<widget class="QPushButton" name="selectVCoreBtn">
<property name="text">
<string>#Select</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_15">
<property name="text">
<string>v2ray Assets Path</string>
</property>
</widget>
</item>
<item row="5" column="1">
<item row="4" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLineEdit" name="vCoreAssetsPathTxt"/>

View File

@ -37,7 +37,7 @@ void SubscribeEditor::on_buttonBox_accepted()
auto conf = GetGlobalConfig();
conf.subscribes = subscribes;
SetGlobalConfig(conf);
SetGlobalConfig(&conf);
emit s_update_config();
}
@ -69,7 +69,7 @@ void SubscribeEditor::ProcessSubscriptionEntry(QByteArray result, QString subsci
auto vmessList = content.split("\n");
for (auto vmess : vmessList) {
auto config = ConvertConfigFromVMessString(vmess, QV2RAY_CONFIG_TYPE_SUBSCRIPTION);
auto config = ConvertConfigFromVMessString(vmess);
if (subscriptions.contains(subsciptionName)) {
}

View File

@ -19,4 +19,8 @@ using namespace std;
#define MODULE_FILE "FILE"
#define MODULE_SUBSCRIPTION "SUBSCRIPTION"
#define WARN "WARN"
#define INFO "INFO"
#define ERROR "ERROR"
#endif // QVTINYLOG_H

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff