Merge pull request #256 from lhy0403/dev

RC3
This commit is contained in:
Qv2ray Bot 2020-01-19 18:37:50 +08:00 committed by GitHub
commit e92b24fb90
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 3820 additions and 2136 deletions

View File

@ -113,7 +113,7 @@ jobs:
run: |
mkdir build
cd build
qmake .. CONFIG+=no_increase_build_number
qmake .. CONFIG+=no_increase_build_number PREFIX=/usr
- name: Building Qv2ray
run: |
cd build
@ -124,8 +124,6 @@ jobs:
make install INSTALL_ROOT=AppDir
cd AppDir
mkdir -p ./usr/lib/
mkdir -p ./usr/bin/
mv ./usr/local/bin/qv2ray ./usr/bin/qv2ray
cp /usr/lib/x86_64-linux-gnu/libssl.so.1.1 /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 ./usr/lib/
- name: Building AppImage using linuxdeployqt
run: |

View File

@ -131,7 +131,7 @@ jobs:
run: |
mkdir build
cd build
qmake .. CONFIG+=no_increase_build_number
qmake .. CONFIG+=no_increase_build_number PREFIX=/usr
make -j2
- name: Generating filesystem structure for AppImage
run: |
@ -139,8 +139,6 @@ jobs:
make install INSTALL_ROOT=AppDir
cd AppDir
mkdir -p ./usr/lib/
mkdir -p ./usr/bin/
mv ./usr/local/bin/qv2ray ./usr/bin/qv2ray
cp /usr/lib/x86_64-linux-gnu/libssl.so.1.1 /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 ./usr/lib/
- name: Building AppImage using linuxdeployqt
run: |

View File

@ -1 +1 @@
2781
2960

View File

@ -20,6 +20,10 @@ no_increase_build_number {
write_file("Build.Counter", _BUILD_NUMBER)
}
isEmpty(PREFIX) {
PREFIX=/usr/local
}
DEFINES += QT_DEPRECATED_WARNINGS QV2RAY_VERSION_STRING=\"\\\"v$${VERSION}\\\"\" QAPPLICATION_CLASS=QApplication
# Don't merge those configs with below.
@ -156,7 +160,7 @@ ICON = ./assets/icons/qv2ray.icns
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(" --> Or consider reading the build wiki: https://github.com/lhy0403/Qv2ray/wiki/Manually-Build-Qv2ray")
message("-----------------------------------------------")
message(" ")
warning("IF YOU THINK IT'S A MISTAKE, PLEASE OPEN AN ISSUE")
@ -235,7 +239,7 @@ macx {
# For Linux and macOS
message("Configuring for macOS specific environment")
LIBS += -framework Carbon -framework Cocoa
LIBS += -lgpr
LIBS += -lgpr -lupb
}
# Reuse unix for macx as well
@ -255,27 +259,21 @@ unix {
message(" --> Generating desktop dependency.")
desktop.files += ./assets/qv2ray.desktop
desktop.path = /usr/share/applications/
desktop.path = $$PREFIX/share/applications/
message(" --> Generating icons dependency.")
icon.files += ./assets/icons/qv2ray.png
icon.path = /usr/share/icons/hicolor/256x256/apps/
icon.path = $$PREFIX/share/icons/hicolor/256x256/apps/
target.path = /usr/local/bin/
message(" --> Generating metainfo dependency.")
appdataXml.files += ./assets/qv2ray.metainfo.xml
appdataXml.path = $$PREFIX/share/metainfo/
target.path = $$PREFIX/bin/
INSTALLS += target desktop icon
}
build_flatpak {
# For Packaging
message("Configuring for packaging platform")
message(" --> Generating metainfo dependency.")
appdataXml.files += ./assets/qv2ray.metainfo.xml
appdataXml.path = /app/share/metainfo/
LIBS += -L/app/lib
INCLUDEPATH += /app/include/
desktop.path = /app/share/applications/
icon.path = /app/share/icons/hicolor/256x256/apps/
target.path = /app/bin/
with_metainfo {
INSTALLS += appdataXml
}

View File

@ -1,6 +1,6 @@
***注意Qv2ray 仅能用于 Qt/c++/linux/CI/自动化 等相关技术的学习和在法律允许范围内的使用,任何个人或集体不得使用 Qv2ray 进行任何违反相关法律法规的活动。***
***注意Qv2ray 仅能用于 Qt/C++/Linux/CI/自动化 等相关技术的学习和在法律允许范围内的使用,任何个人或集体不得使用 Qv2ray 进行任何违反相关法律法规的活动。***
> 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.
> 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.
*任何尝试下载或下载 Qv2ray 任意分支或发行版即代表您同意本项目作者不承担任何由于您违反以上准则所带来的任何法律责任。*

View File

@ -1,8 +1,26 @@
[Desktop Entry]
Version=1.0
Name=Qv2ray
Type=Application
Keywords=Internet;VPN;Proxy;v2ray;Qt;
GenericName=V2ray Graphical Frontend
GenericName[zh_CN]=V2ray
Comment=A Cross-platform v2ray Qt GUI Client
Comment[zh_CN]= v2ray Qt
Keywords=Internet;VPN;Proxy;v2ray;Qt;qv;
Keywords[zh_CN]=Internet;VPN;Proxy;v2ray;Qt;qv;;;;
Categories=Network;Qt;
Terminal=false
Icon=qv2ray
Exec=qv2ray
Name=Qv2ray
Comment=Cross platform v2ray Qt GUI Client.
Actions=noAPI;withToolbarPlugin;
[Desktop Action noAPI]
Name=Start without gRPC API
Name[zh_CN]=使 gRPC API
Exec=qv2ray --noAPI
[Desktop Action withToolbarPlugin]
Name=Start with Toolbar Plugin
Name[zh_CN]=使
Exec=qv2ray --withToolbarPlugin

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop-application">
<id>com.github.Qv2ray</id>
<id>com.github.lhy0403.Qv2ray</id>
<metadata_license>CC0-1.0</metadata_license>
<project_license>GPL-3.0</project_license>
<name>Qv2ray</name>
@ -13,7 +13,7 @@
</p>
</description>
<launchable type="desktop-id">com.github.Qv2ray.desktop</launchable>
<launchable type="desktop-id">com.github.lhy0403.Qv2ray.desktop</launchable>
<screenshots>
<screenshot type="default">

View File

@ -1,50 +0,0 @@
Description: <short summary of the patch>
TODO: Put a short summary on the line above and replace this paragraph
with a longer explanation of this change. Complete the meta-information
with other relevant fields (see below for details). To make it easier, the
information below has been extracted from the changelog. Adjust it or drop
it.
.
qv2ray (1.99.4-1) unstable; urgency=medium
.
* Bump version to v1.99.4
Author: Guobang Bi <ymshenyu@gmail.com>
---
The information above should follow the Patch Tagging Guidelines, please
checkout http://dep.debian.net/deps/dep3/ to learn about the format. Here
are templates for supplementary fields that you might want to add:
Origin: <vendor|upstream|other>, <url of original patch>
Bug: <url in upstream bugtracker>
Bug-Debian: https://bugs.debian.org/<bugnumber>
Bug-Ubuntu: https://launchpad.net/bugs/<bugnumber>
Forwarded: <no|not-needed|url proving that it has been forwarded>
Reviewed-By: <name and email of someone who approved the patch>
Last-Update: 2020-01-04
--- a/Qv2ray.pro
+++ b/Qv2ray.pro
@@ -245,11 +245,11 @@
message("Configuring for unix-like environment")
# For gRPC and protobuf in linux and macOS
message(" --> Linking against gRPC and protobuf library.")
- LIBS += -L/usr/local/lib -lgrpc++ -lprotobuf -lgrpc
+ LIBS += -L/usr/lib -lgrpc++ -lprotobuf -lgrpc
# macOS homebrew include path
message(" --> Adding local include folder to search path")
- INCLUDEPATH += /usr/local/include/
+ INCLUDEPATH += /usr/include/
message(" --> Adding Plasma Toolbox CPP files.")
SOURCES += src/ui/NetSpeedBar/QvNetSpeedBar_linux.cpp
@@ -262,7 +262,7 @@
icon.files += ./icons/qv2ray.png
icon.path = /usr/share/icons/hicolor/256x256/apps/
- target.path = /usr/local/bin/
+ target.path = /usr/bin/
INSTALLS += target desktop icon
}

View File

@ -1,2 +1 @@
disable-passing-isystem.patch
change-target-path.patch

2
debian/rules vendored
View File

@ -10,4 +10,4 @@ override_dh_auto_clean:
override_dh_auto_configure:
./tools/grpc_gen.sh
dh_auto_configure -- CONFIG+=no_increase_build_number
dh_auto_configure -- CONFIG+=no_increase_build_number PREFIX=/usr

View File

@ -18,6 +18,7 @@ const int QV2RAY_CONFIG_VERSION = 6;
# endif
#endif
extern bool isDebug;
// Base folder suffix.
#ifdef QT_DEBUG
# define QV2RAY_CONFIG_DIR_SUFFIX "_debug/"
@ -178,8 +179,9 @@ namespace Qv2ray
QString language;
bool useDarkTheme;
bool useDarkTrayIcon;
Qv2rayUIConfig() : theme("Fusion"), language("en-US"), useDarkTheme(false), useDarkTrayIcon(true) { }
XTOSTRUCT(O(theme, language, useDarkTheme, useDarkTrayIcon))
int maximumLogLines;
Qv2rayUIConfig() : theme("Fusion"), language("en-US"), useDarkTheme(false), useDarkTrayIcon(true), maximumLogLines(500) { }
XTOSTRUCT(O(theme, language, useDarkTheme, useDarkTrayIcon, maximumLogLines))
};
struct Qv2rayConnectionConfig {

View File

@ -37,7 +37,7 @@ namespace Qv2ray
vmessUriRoot["host"] = realHost;
vmessUriRoot["path"] = transfer.wsSettings.path;
} else if (transfer.network == "h2" || transfer.network == "http") {
vmessUriRoot["host"] = Stringify(transfer.httpSettings.host, ",");
vmessUriRoot["host"] = transfer.httpSettings.host.join(",");
vmessUriRoot["path"] = transfer.httpSettings.path;
}
@ -74,15 +74,27 @@ namespace Qv2ray
bool SaveSubscriptionConfig(CONFIGROOT obj, const QString &subscription, const QString &name)
{
auto str = JsonToString(obj);
QFile *config = new QFile(QV2RAY_SUBSCRIPTION_DIR + subscription + "/" + name + QV2RAY_CONFIG_FILE_EXTENSION);
auto fName = name;
if (!IsValidFileName(fName + QV2RAY_CONFIG_FILE_EXTENSION)) {
fName = QObject::tr("Invalid filename") + "_" + GenerateRandomString(6) + QV2RAY_CONFIG_FILE_EXTENSION;
}
QFile *config = new QFile(QV2RAY_SUBSCRIPTION_DIR + subscription + "/" + fName + QV2RAY_CONFIG_FILE_EXTENSION);
// If there's already a file. THIS IS EXTREMELY RARE
if (config->exists()) {
LOG(MODULE_FILE, "Trying to overrwrite an existing subscription config file. THIS IS RARE")
}
LOG(MODULE_CONFIG, "Saving a subscription named: " + name)
return StringToFile(&str, config);
LOG(MODULE_CONFIG, "Saving a subscription named: " + fName)
bool result = StringToFile(&str, config);
if (!result) {
LOG(MODULE_FILE, "Failed to save a connection config from subscription: " + subscription + ", name: " + fName)
}
return result;
}
bool RemoveConnection(const QString &alias)
@ -110,14 +122,23 @@ namespace Qv2ray
}
// This generates global config containing only one outbound....
CONFIGROOT ConvertConfigFromVMessString(const QString &vmess, QString *alias, QString *errMessage)
CONFIGROOT ConvertConfigFromVMessString(const QString &vmessStr, QString *alias, QString *errMessage)
{
#define default CONFIGROOT()
LOG(MODULE_CONFIG, "Trying to convert from a vmess string.")
QString vmess = vmessStr;
if (vmess.trimmed() != vmess) {
LOG(MODULE_CONFIG, "VMess string has some prefix/postfix spaces, trimming.")
vmess = vmessStr.trimmed();
}
// Reset errMessage
*errMessage = "";
if (!vmess.toLower().startsWith("vmess://")) {
*errMessage = QObject::tr("VMess string should start with 'vmess://'");
return CONFIGROOT();
return default;
}
try {
@ -126,7 +147,7 @@ namespace Qv2ray
if (b64Str.isEmpty()) {
*errMessage = QObject::tr("VMess string should be a valid base64 string");
return CONFIGROOT();
return default;
}
auto vmessString = Base64Decode(b64Str);
@ -134,23 +155,21 @@ namespace Qv2ray
if (!jsonErr.isEmpty()) {
*errMessage = jsonErr;
return CONFIGROOT();
return default;
}
auto vmessConf = JsonFromString(vmessString);
if (vmessConf.isEmpty()) {
*errMessage = QObject::tr("JSON should not be empty");
return CONFIGROOT();
return default;
}
bool flag = true;
// C is a quick hack...
#define C(k) vmessConf.contains(k)
bool flag = true;
flag = flag && C("id");
flag = flag && C("aid");
flag = flag && C("port");
flag = flag && C("add");
// id, aid, port and add are mandatory fields of a vmess:// link.
flag = flag && C("id") && C("aid") && C("port") && C("add");
// Stream Settings
auto net = C("net") ? vmessConf["net"].toString() : "tcp";
@ -166,32 +185,58 @@ namespace Qv2ray
} catch (exception *e) {
LOG(MODULE_IMPORT, "Failed to decode vmess string: " + QString(e->what()))
*errMessage = e->what();
return CONFIGROOT();
return default;
}
// --------------------------------------------------------------------------------------
CONFIGROOT root;
QStringRef vmessJsonB64(&vmess, 8, vmess.length() - 8);
auto vmessConf = JsonFromString(Base64Decode(vmessJsonB64.toString()));
auto b64String = QStringRef(&vmess, 8, vmess.length() - 8).toString();
auto vmessConf = JsonFromString(Base64Decode(b64String));
//
QString ps, add, id, net, type, host, path, tls;
int port, aid;
//
ps = vmessConf.contains("ps") ? vmessConf["ps"].toVariant().toString()
: (vmessConf["add"].toVariant().toString() + ":" + vmessConf["port"].toVariant().toString());
add = vmessConf["add"].toVariant().toString();
id = vmessConf["id"].toVariant().toString();
net = vmessConf.contains("net") ? vmessConf["net"].toVariant().toString() : "tcp";
type = vmessConf.contains("type") ? vmessConf["type"].toVariant().toString() : "none";
host = vmessConf["host"].toVariant().toString();
path = vmessConf["path"].toVariant().toString();
tls = vmessConf.contains("tls") ? vmessConf["tls"].toVariant().toString() : "";
// key = key in JSON and the variable name.
// values = Candidate variable list, if not match, the first one is used as default.
// [[val.size() <= 1]] is used when only the default value exists.
// - It can be empty, if so, if the key is not in the JSON, or the value is empty, it'll report an error.
// - Else if it contains one thing. if the key is not in the JSON, or the value is empty, it'll use that one.
// - Else if it contains many things, when the key IS in the JSON but not in those THINGS, it'll use the first one in the THINGS
// - Else, it'll use the value found from the JSON object.
//
#define empty_arg
#define __vmess_checker__func(key, values) \
{\
auto val = QStringList() values;\
if (vmessConf.contains(#key) && !vmessConf[#key].toVariant().toString().trimmed().isEmpty() \
&& (val.size() <= 1 || val.contains(vmessConf[#key].toVariant().toString()))) {\
key = vmessConf[#key].toVariant().toString();\
DEBUG(MODULE_IMPORT, "Found key \"" #key "\" within the vmess object.")\
} else if (!val.isEmpty()) {\
key = val.first(); \
DEBUG(MODULE_IMPORT, "Using key \"" #key "\" from the first candidate list: " + key)\
} else{\
*errMessage = QObject::tr(#key " does not exist."); \
LOG(MODULE_IMPORT, "Cannot process \"" #key "\" since it's not included in the json object." ) \
LOG(MODULE_IMPORT, " --> values: " + val.join(";")) \
LOG(MODULE_IMPORT, " --> PS: " + ps) \
}\
}
// Strict check of VMess protocol, to check if the specified value is in the correct range.
//
// Get Alias (AKA ps) from address and port.
__vmess_checker__func(ps, << vmessConf["add"].toVariant().toString() + ":" + vmessConf["port"].toVariant().toString());
__vmess_checker__func(add, empty_arg)
__vmess_checker__func(id, empty_arg)
__vmess_checker__func(net, << "tcp" << "http" << "h2" << "ws" << "kcp" << "domainsocket" << "quic")
__vmess_checker__func(type, << "none" << "http" << "srtp" << "utp" << "wechat-video")
__vmess_checker__func(path, << "")
__vmess_checker__func(host, << "")
__vmess_checker__func(tls, << "")
//
port = vmessConf["port"].toVariant().toInt();
aid = vmessConf["aid"].toVariant().toInt();
//
// More strict check could be implemented, such as to check if the specified value is
// in the currect format.
// Apply the settings.
//
// User
VMessServerObject::UserObject user;
@ -218,7 +263,7 @@ namespace Qv2ray
} else if (net == "http" || net == "h2") {
// Fill hosts for HTTP
for (auto _host : host.split(',')) {
streaming.httpSettings.host.push_back(_host);
streaming.httpSettings.host.push_back(_host.trimmed());
}
streaming.httpSettings.path = path;
@ -245,7 +290,8 @@ namespace Qv2ray
//
root["outbounds"] = QJsonArray() << outbound;
// If previous alias is empty, just the PS is needed, else, append a "_"
*alias = alias->isEmpty() ? ps : *alias + "_" + ps;
*alias = alias->trimmed().isEmpty() ? ps : *alias + "_" + ps;
#undef default
return root;
}

View File

@ -3,6 +3,7 @@
#include "Qv2rayBase.hpp"
#include "QvHelpers.hpp"
#include "QvCore/QvCommandLineArgs.hpp"
namespace Qv2ray
{

View File

@ -11,15 +11,20 @@ namespace Qv2ray
QvStartupOptions StartupOption = QvStartupOptions{};
QvCommandArgParser::QvCommandArgParser() : QObject(),
noAPIOption("FAKE"), runAsRootOption("FAKE"), helpOption("FAKE"), versionOption("FAKE")
noAPIOption("noAPI", QObject::tr("Disable gRPC API subsystems.")),
runAsRootOption("I-just-wanna-run-with-root", QObject::tr("Explicitly run Qv2ray as root.")),
debugOption("debug", QObject::tr("Enable Debug Output")),
withToolbarOption("withToolbarPlugin", QObject::tr("Enable Qv2ray network toolbar plugin")),
//
helpOption("FAKE"), versionOption("FAKE")
{
parser.setApplicationDescription(QObject::tr("Qv2ray - A cross-platform Qt frontend for V2ray."));
parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
//
noAPIOption = QCommandLineOption("noAPI", QObject::tr("Disable gRPC API subsystems."));
runAsRootOption = QCommandLineOption("I-just-wanna-run-with-root", QObject::tr("Explicitly run Qv2ray as root."));
parser.addOption(noAPIOption);
parser.addOption(runAsRootOption);
parser.addOption(debugOption);
parser.addOption(withToolbarOption);
helpOption = parser.addHelpOption();
versionOption = parser.addVersionOption();
}
@ -38,13 +43,25 @@ namespace Qv2ray
return CommandLineHelpRequested;
if (parser.isSet(noAPIOption)) {
DEBUG(MODULE_INIT, "No API subsystem is set.")
StartupOption.noAPI = true;
}
if (parser.isSet(runAsRootOption)) {
DEBUG(MODULE_INIT, "Force run as root is set.")
StartupOption.forceRunAsRootUser = true;
}
if (parser.isSet(debugOption)) {
DEBUG(MODULE_INIT, "Debug log is set.")
StartupOption.debugLog = true;
}
if (parser.isSet(withToolbarOption)) {
DEBUG(MODULE_INIT, "Run with network toolbar is set.")
StartupOption.enableToolbarPlguin = true;
}
return CommandLineOk;
}
}

View File

@ -12,6 +12,10 @@ namespace Qv2ray
bool noAPI;
/// Explicitly run as root user.
bool forceRunAsRootUser;
/// Enable Debug Log.
bool debugLog;
/// Enable Network toolbar plugin.
bool enableToolbarPlguin;
};
enum CommandLineParseResult {
CommandLineOk,
@ -37,6 +41,8 @@ namespace Qv2ray
QCommandLineParser parser;
QCommandLineOption noAPIOption;
QCommandLineOption runAsRootOption;
QCommandLineOption debugOption;
QCommandLineOption withToolbarOption;
QCommandLineOption helpOption;
QCommandLineOption versionOption;
};

View File

@ -18,7 +18,7 @@ namespace Qv2ray
QUrl qUrl = QUrl(url);
if (!qUrl.isValid()) {
LOG(MODULE_NETWORK, "Provided URL is invalid")
LOG(MODULE_NETWORK, "Provided URL is invalid: " + url)
return false;
}
@ -28,15 +28,16 @@ namespace Qv2ray
void QvHttpRequestHelper::setHeader(const QByteArray &key, const QByteArray &value)
{
DEBUG(MODULE_NETWORK, "Adding HTTP request header: " + key + ":" + value)
request.setRawHeader(key, value);
}
QByteArray QvHttpRequestHelper::syncget(const QString &url)
{
this->setUrl(url);
LOG(MODULE_NETWORK, "Using system proxy settings")
auto proxy = QNetworkProxyFactory::systemProxyForQuery();
accessManager.setProxy(proxy.first());
LOG(MODULE_NETWORK, "Sync get is using system proxy settings")
request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
reply = accessManager.get(request);
connect(reply, &QNetworkReply::finished, this, &QvHttpRequestHelper::onRequestFinished);
@ -102,6 +103,7 @@ namespace Qv2ray
void QvHttpRequestHelper::onReadyRead()
{
DEBUG(MODULE_NETWORK, "A request is now ready read")
this->data += reply->readAll();
}
}

View File

@ -17,81 +17,151 @@ namespace Qv2ray
{
namespace QvKernelInterations
{
bool V2rayKernelInstance::ValidateKernel(const QString &vCorePath, const QString &vAssetsPath, QString *message)
{
QFile coreFile(vCorePath);
if (!coreFile.exists()) {
DEBUG(MODULE_VCORE, "V2ray core file cannot be found.")
*message = tr("V2ray core executable not found.");
return false;
}
// Use open() here to prevent `executing` a folder, which may have the same name as the V2ray core.
if (!coreFile.open(QFile::ReadOnly)) {
DEBUG(MODULE_VCORE, "V2ray core file cannot be opened, possibly be a folder?")
*message = tr("V2ray core file cannot be opened, please ensure there's a file instead of a folder.");
return false;
}
coreFile.close();
//
// Check file existance.
// From: https://www.v2fly.org/chapter_02/env.html#asset-location
//
bool hasGeoIP = FileExistsIn(QDir(vAssetsPath), "geoip.dat");
bool hasGeoSite = FileExistsIn(QDir(vAssetsPath), "geosite.dat");
if (!hasGeoIP && !hasGeoSite) {
DEBUG(MODULE_VCORE, "V2ray assets path contains none of those two files.")
*message = tr("V2ray assets path is not valid.");
return false;
}
if (!hasGeoIP) {
DEBUG(MODULE_VCORE, "No geoip.dat in assets path, aborting.")
*message = tr("No geoip.dat in assets path.");
return false;
}
if (!hasGeoSite) {
DEBUG(MODULE_VCORE, "No geosite.dat in assets path, aborting.")
*message = tr("No geosite.dat in assets path.");
return false;
}
// Check if V2ray core returns a version number correctly.
QProcess proc;
proc.start(vCorePath + " -version");
if (!proc.waitForFinished(1000) || proc.exitCode() != 0) {
DEBUG(MODULE_VCORE, "V2ray core not exited within 1 sec, or it failed with an exit code: " + QSTRN(proc.exitCode()))
if (proc.exitCode() != 0) {
*message = tr("V2ray core failed with an exit code: ") + QSTRN(proc.exitCode());
} else {
*message = tr("V2ray core not responsed within 1 secs.");
}
return false;
}
QString output = QString(proc.readAllStandardOutput());
LOG(MODULE_VCORE, "V2ray output: " + SplitLines(output).join(";"))
*message = SplitLines(output).first();
return true;
}
bool V2rayKernelInstance::ValidateConfig(const QString &path)
{
auto conf = GetGlobalConfig();
QFile coreFile(conf.v2CorePath);
bool coreFileExists = coreFile.exists() && coreFile.open(QFile::ReadOnly);
QString v2rayCheckResult;
if (coreFileExists) {
coreFile.close();
}
if (coreFileExists) {
if (ValidateKernel(conf.v2CorePath, conf.v2AssetsPath, &v2rayCheckResult)) {
DEBUG(MODULE_VCORE, "V2ray version: " + v2rayCheckResult)
// Append assets location env.
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
env.insert("V2RAY_LOCATION_ASSET", conf.v2AssetsPath);
//
QProcess process;
process.setProcessEnvironment(env);
DEBUG(MODULE_VCORE, "Starting V2ray core with test options")
process.start(conf.v2CorePath, QStringList() << "-test" << "-config" << path, QIODevice::ReadWrite | QIODevice::Text);
if (!process.waitForFinished(1000) && process.exitCode() != 0) {
LOG(MODULE_VCORE, "v2ray core failed with exitcode: " + QSTRN(process.exitCode()))
QvMessageBox(nullptr, tr("Cannot start v2ray"), tr("v2ray core failed with errcode:") + QSTRN(process.exitCode()));
LOG(MODULE_VCORE, "V2ray core failed with an exit code: " + QSTRN(process.exitCode()))
QvMessageBoxWarn(nullptr, tr("Cannot start V2ray"), tr("V2ray core failed with an exit code: ") + QSTRN(process.exitCode()));
return false;
}
QString output = QString(process.readAllStandardOutput());
if (process.exitCode() != 0) {
QvMessageBox(nullptr, tr("Configuration Error"), output.mid(output.indexOf("anti-censorship.") + 17));
} else if (process.exitCode() != 0) {
QString output = QString(process.readAllStandardOutput());
QvMessageBoxWarn(nullptr, tr("Configuration Error"), output.mid(output.indexOf("anti-censorship.") + 17));
return false;
} else {
DEBUG(MODULE_VCORE, "Config file check passed.")
return true;
}
return true;
} else {
QvMessageBox(nullptr, tr("Cannot start v2ray"),
tr("We cannot find v2ray core binary.") + NEWLINE + NEWLINE +
tr("Possible solutions:") + NEWLINE +
tr("1. The location is wrong, please go to Preference Window to change it.") + NEWLINE +
tr("2. Please make sure the path is an excutable file, use \"chmod +x\" and make sure it's not a directory: ") + conf.v2CorePath + NEWLINE);
QvMessageBoxWarn(nullptr, tr("Cannot start V2ray"),
tr("V2ray core settings is incorrect.") + NEWLINE + NEWLINE +
tr("The error is: ") + NEWLINE + v2rayCheckResult);
return false;
}
}
V2rayKernelInstance::V2rayKernelInstance()
{
auto proc = new QProcess();
vProcess = proc;
vProcess = new QProcess();;
connect(vProcess, &QProcess::readyReadStandardOutput, this, [this]() {
emit onProcessOutputReadyRead(vProcess->readAllStandardOutput().trimmed());
});
ConnectionStatus = STOPPED;
connect(vProcess, &QProcess::stateChanged, [this](QProcess::ProcessState state) {
DEBUG(MODULE_VCORE, "V2ray kernel process status changed: " + QVariant::fromValue(state).toString())
// If V2ray crashed AFTER we start it.
if (KernelStarted && state == QProcess::NotRunning) {
LOG(MODULE_VCORE, "V2ray kernel crashed.")
emit onProcessErrored();
}
});
KernelStarted = false;
}
bool V2rayKernelInstance::StartConnection(CONFIGROOT root, int apiPort)
{
if (KernelStarted) {
LOG(MODULE_VCORE, "Status is invalid, expect STOPPED when calling StartConnection")
return false;
}
inboundTags.clear();
for (auto item : root["inbounds"].toArray()) {
auto tag = item.toObject()["tag"].toString("");
if (tag.isEmpty() || tag == API_TAG_INBOUND)
if (tag.isEmpty() || tag == API_TAG_INBOUND) {
// Ignore API tag and empty tags.
continue;
}
inboundTags.append(tag);
}
LOG(MODULE_VCORE, "Found Inbound Tags: " + Stringify(inboundTags))
QString json = JsonToString(root);
DEBUG(MODULE_VCORE, "Found inbound tags: " + inboundTags.join(";"))
// Write the final configuration to the disk.
QString json = JsonToString(root);
StringToFile(&json, new QFile(QV2RAY_GENERATED_FILE_PATH));
if (ConnectionStatus != STOPPED) {
LOG(MODULE_VCORE, "Status is invalid, expect STOPPED when calling StartV2rayCore")
return false;
}
ConnectionStatus = STARTING;
//
auto filePath = QV2RAY_GENERATED_FILE_PATH;
if (ValidateConfig(filePath)) {
@ -100,10 +170,13 @@ namespace Qv2ray
vProcess->setProcessEnvironment(env);
vProcess->start(GetGlobalConfig().v2CorePath, QStringList() << "-config" << filePath, QIODevice::ReadWrite | QIODevice::Text);
vProcess->waitForStarted();
ConnectionStatus = STARTED;
DEBUG(MODULE_VCORE, "V2ray core started.")
KernelStarted = true;
if (StartupOption.noAPI) {
LOG(MODULE_VCORE, "API is disabled by the command line arg \"--noAPI\"")
LOG(MODULE_VCORE, "API has been disabled by the command line argument \"-noAPI\"")
} else if (inboundTags.isEmpty()) {
LOG(MODULE_VCORE, "API is disabled since no inbound tags configured. This is probably caused by a bad complex config.")
} else {
// Config API
apiFailedCounter = 0;
@ -112,12 +185,12 @@ namespace Qv2ray
StatsService service;
Stub = service.NewStub(Channel);
apiTimerId = startTimer(1000);
LOG(MODULE_VCORE, "API Worker started.")
DEBUG(MODULE_VCORE, "API Worker started.")
}
return true;
} else {
ConnectionStatus = STOPPED;
KernelStarted = false;
return false;
}
}
@ -145,17 +218,17 @@ namespace Qv2ray
void V2rayKernelInstance::StopConnection()
{
KernelStarted = false;
vProcess->close();
killTimer(apiTimerId);
apiFailedCounter = 0;
transferData.clear();
transferSpeed.clear();
ConnectionStatus = STOPPED;
}
V2rayKernelInstance::~V2rayKernelInstance()
{
if (ConnectionStatus != STOPPED) {
if (KernelStarted) {
StopConnection();
}
@ -164,14 +237,14 @@ namespace Qv2ray
long V2rayKernelInstance::CallStatsAPIByName(QString name)
{
if (ConnectionStatus != STARTED) {
if (!KernelStarted) {
LOG(MODULE_VCORE, "Invalid connection status when calling API")
return 0;
}
if (apiFailedCounter == QV2RAY_API_CALL_FAILEDCHECK_THRESHOLD) {
LOG(MODULE_VCORE, "API call failure threshold reached, cancelling further API aclls.")
QvMessageBox(nullptr, tr("API Call Failed"), tr("Failed to get statistics data, please check if v2ray is running properly"));
QvMessageBoxWarn(nullptr, tr("API Call Failed"), tr("Failed to get statistics data, please check if V2ray is running properly"));
transferData.clear();
transferSpeed.clear();
apiFailedCounter++;

View File

@ -10,12 +10,6 @@ namespace Qv2ray
{
namespace QvKernelInterations
{
enum QvInstanceStatus {
STOPPED,
STARTING,
STARTED
};
class V2rayKernelInstance : public QObject
{
Q_OBJECT
@ -35,11 +29,13 @@ namespace Qv2ray
//
bool StartConnection(CONFIGROOT root, int apiPort);
void StopConnection();
QvInstanceStatus ConnectionStatus;
bool KernelStarted = false;
//
static bool ValidateConfig(const QString &path);
static bool ValidateKernel(const QString &vCorePath, const QString &vAssetsPath, QString *message);
signals:
void onProcessErrored();
void onProcessOutputReadyRead(QString);
private:

View File

@ -1,6 +1,6 @@
#include "QvLogHighlighter.hpp"
#define PORT_R "([0-9]|[1-9]\\d{1,3}|[1-5]\\d{4}|6[0-5]{2}[0-3][0-5])*"
#define REGEX_PORT_NUMBER "([0-9]|[1-9]\\d{1,3}|[1-5]\\d{4}|6[0-5]{2}[0-3][0-5])*"
#define TO_EOL "(([\\s\\S]*)|([\\d\\D]*)|([\\w\\W]*))$"
namespace Qv2ray
@ -59,17 +59,17 @@ namespace Qv2ray
//
{
// IP IPv6 Host;
rule.pattern = QRegularExpression("(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])\\:" PORT_R);
rule.pattern = QRegularExpression("(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])\\:" REGEX_PORT_NUMBER);
rule.pattern.setPatternOptions(QRegularExpression::PatternOption::ExtendedPatternSyntaxOption);
rule.format = ipHostFormat;
highlightingRules.append(rule);
//
rule.pattern = QRegularExpression("\\[\\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:)))(%.+)?\\s*\\]:" PORT_R);
rule.pattern = QRegularExpression(REGEX_IPV6_ADDR ":" REGEX_PORT_NUMBER);
rule.pattern.setPatternOptions(QRegularExpression::PatternOption::ExtendedPatternSyntaxOption);
rule.format = ipHostFormat;
highlightingRules.append(rule);
//
rule.pattern = QRegularExpression("([a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,6}(/|):" PORT_R);
rule.pattern = QRegularExpression("([a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,6}(/|):" REGEX_PORT_NUMBER);
rule.pattern.setPatternOptions(QRegularExpression::PatternOption::ExtendedPatternSyntaxOption);
rule.format = ipHostFormat;
highlightingRules.append(rule);

View File

@ -18,6 +18,7 @@ namespace Qv2ray
}
void PACServer::SetProxyString(const QString &proxyString)
{
DEBUG(MODULE_PROXY, "Setting new PAC proxy string: " + proxyString)
this->proxyString = proxyString;
}
void PACServer::StartListen()
@ -30,6 +31,8 @@ namespace Qv2ray
auto address = conf.inboundConfig.listenip;
auto port = conf.inboundConfig.pacConfig.port;
//
DEBUG(MODULE_PROXY, "PAC Listening local endpoint: " + address + ":" + QSTRN(port))
//
QString gfwContent = StringFromFile(new QFile(QV2RAY_RULES_GFWLIST_PATH));
pacContent = ConvertGFWToPAC(gfwContent, proxyString);
//
@ -37,10 +40,10 @@ namespace Qv2ray
if (result) {
isStarted = true;
LOG(MODULE_PROXY, "Started PAC listener")
DEBUG(MODULE_PROXY, "Started PAC handler")
} else {
LOG(MODULE_PROXY, "Failed to listen on port " + QSTRN(port) + ", please verify the permission.")
QvMessageBox(nullptr, tr("PAC Handler"), tr("Failed to listen PAC request on this port, please verify the permissions"));
LOG(MODULE_PROXY, "Failed to listen on port " + QSTRN(port) + ", possible permission denied.")
QvMessageBoxWarn(nullptr, tr("PAC Handler"), tr("Failed to listen PAC request on this port, please verify the permissions"));
}
}
@ -48,8 +51,9 @@ namespace Qv2ray
{
if (isStarted) {
pacServer->close();
delete pacServer;
DEBUG(MODULE_PROXY, "PAC Handler stopped.")
isStarted = false;
delete pacServer;
}
}
@ -60,6 +64,8 @@ namespace Qv2ray
if (req->method() == QHttpRequest::HTTP_GET) {
//
if (req->path() == "/pac") {
DEBUG(MODULE_PROXY, "Serving PAC file request.")
//
rsp->setHeader("Content-Type", "application/javascript; charset=utf-8");
rsp->writeHead(QHttpResponse::StatusCode::STATUS_OK);
rsp->end(pacContent.toUtf8());

View File

@ -27,7 +27,7 @@ namespace Qv2ray
}
}
LOG(MODULE_PROXY, "Found " + QSTRN(result.size()) + " network services: " + Stringify(result))
LOG(MODULE_PROXY, "Found " + QSTRN(result.size()) + " network services: " + result.join(";"))
return result;
}
#endif

View File

@ -15,6 +15,8 @@
#include "unistd.h"
#endif
bool isDebug = false;
bool verifyConfigAvaliability(QString path, bool checkExistingConfig)
{
// Does not exist.
@ -78,7 +80,7 @@ bool verifyConfigAvaliability(QString path, bool checkExistingConfig)
} catch (...) {
LOG(MODULE_CONFIG, "Exception raised when checking config: " + configFile.fileName())
//LOG(MODULE_INIT, e->what())
QvMessageBox(nullptr, QObject::tr("Warning"), QObject::tr("Qv2ray cannot load the config file from here:") + NEWLINE + configFile.fileName());
QvMessageBoxWarn(nullptr, QObject::tr("Warning"), QObject::tr("Qv2ray cannot load the config file from here:") + NEWLINE + configFile.fileName());
return false;
}
} else return true;
@ -142,10 +144,10 @@ bool initialiseQv2ray()
// Otherwise Qv2ray would have loaded this config already instead of notifying to
// create a new config in this folder.
LOG(MODULE_INIT, "This should not occur: Qv2ray config exists but failed to load.")
QvMessageBox(nullptr, QObject::tr("Failed to initialise Qv2ray"),
QObject::tr("Failed to determine the location of config file.") + NEWLINE +
QObject::tr("Qv2ray will now exit.") + NEWLINE +
QObject::tr("Please report if you think it's a bug."));
QvMessageBoxWarn(nullptr, QObject::tr("Failed to initialise Qv2ray"),
QObject::tr("Failed to determine the location of config file.") + NEWLINE +
QObject::tr("Qv2ray will now exit.") + NEWLINE +
QObject::tr("Please report if you think it's a bug."));
return false;
}
@ -162,18 +164,18 @@ bool initialiseQv2ray()
// Even the last folder failed to pass the check.
LOG(MODULE_INIT, "FATAL")
LOG(MODULE_INIT, " ---> CANNOT find a proper place to store Qv2ray config files.")
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 +
QObject::tr("Qv2ray will now exit."));
QString searchPath = configFilePaths.join(NEWLINE);
QvMessageBoxWarn(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 +
QObject::tr("Qv2ray will now exit."));
return false;
}
}
if (!QDir(QV2RAY_GENERATED_DIR).exists()) {
// The dir used to generate final config file, for v2ray interaction.
// The dir used to generate final config file, for V2ray interaction.
QDir().mkdir(QV2RAY_GENERATED_DIR);
LOG(MODULE_INIT, "Created config generation dir at: " + QV2RAY_GENERATED_DIR)
}
@ -187,6 +189,9 @@ int main(int argc, char *argv[])
// parse the command line before starting as a Qt application
{
std::unique_ptr<QCoreApplication> consoleApp(new QCoreApplication(argc, argv));
//
// Install a default translater. From the OS/DE
consoleApp->installTranslator(getTranslator(QLocale::system().name().replace("_", "-")));
QvCommandArgParser parser;
QString errorMessage;
@ -207,19 +212,18 @@ int main(int argc, char *argv[])
cout << parser.Parser()->helpText().toStdString() << endl;
return 0;
}
}
#ifdef Q_OS_UNIX
// Unix OS root user check.
// Do not use getuid() here since it's installed as owned by the root, someone may accidently setuid to it.
if (!StartupOption.forceRunAsRootUser && geteuid() == 0) {
LOG("ERROR", QObject::tr("You cannot run Qv2ray as root, please use --I-just-wanna-run-with-root if you REALLY want to do so."))
LOG("ERROR", QObject::tr(" --> USE IT AT YOUR OWN RISK!"))
return 1;
}
// Unix OS root user check.
// Do not use getuid() here since it's installed as owned by the root, someone may accidently setuid to it.
if (!StartupOption.forceRunAsRootUser && geteuid() == 0) {
LOG("ERROR", QObject::tr("You cannot run Qv2ray as root, please use --I-just-wanna-run-with-root if you REALLY want to do so."))
LOG("ERROR", QObject::tr(" --> USE IT AT YOUR OWN RISK!"))
return 1;
}
#endif
}
//
// finished: command line parsing
LOG(MODULE_INIT, "Qv2ray " QV2RAY_VERSION_STRING " running on " + QSysInfo::prettyProductName() + " " + QSysInfo::currentCpuArchitecture() + NEWLINE)
@ -232,23 +236,31 @@ int main(int argc, char *argv[])
//
#ifdef QT_DEBUG
// ----------------------------> For debug build...
isDebug = true;
SingleApplication::setApplicationName("Qv2ray - DEBUG");
#endif
//
if (StartupOption.debugLog) {
DEBUG(MODULE_INIT, "Debug log enabled")
}
//
SingleApplication _qApp(argc, argv, false, SingleApplication::Mode::User | SingleApplication::Mode::ExcludeAppPath | SingleApplication::Mode::ExcludeAppVersion);
// Early initialisation
//
//
// Not duplicated.
// Install a default translater. From the OS/DE
auto _lang = QLocale::system().name().replace("_", "-");
auto _sysTranslator = getTranslator(_lang);
if (_lang != "en-US") {
// Do not install en-US as it's the default language.
bool _result_ = qApp->installTranslator(_sysTranslator);
bool _result_ = _qApp.installTranslator(_sysTranslator);
LOG(MODULE_UI, "Installing a tranlator from OS: " + _lang + " -- " + (_result_ ? "OK" : "Failed"))
}
//
LOG("LICENCE", NEWLINE "This program comes with ABSOLUTELY NO WARRANTY." NEWLINE
"This is free software, and you are welcome to redistribute it" NEWLINE
"under certain conditions." NEWLINE NEWLINE
@ -278,7 +290,7 @@ int main(int argc, char *argv[])
if (langs.empty()) {
LOG(MODULE_INIT, "FAILED to find any translations. THIS IS A BUILD ERROR.")
QvMessageBox(nullptr, QObject::tr("Cannot load languages"), QObject::tr("Qv2ray will continue running, but you cannot change the UI language."));
QvMessageBoxWarn(nullptr, QObject::tr("Cannot load languages"), QObject::tr("Qv2ray will continue running, but you cannot change the UI language."));
} else {
for (auto lang : langs) {
LOG(MODULE_INIT, "Found Translator: " + lang)
@ -297,11 +309,11 @@ int main(int argc, char *argv[])
if (confVersion > 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 check if there's an issue explaining about it.") + NEWLINE +
QObject::tr("Or submit a new issue if you think this is an error.") + NEWLINE + NEWLINE +
QObject::tr("Qv2ray will now exit."));
QvMessageBoxWarn(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 check if there's an issue explaining about it.") + NEWLINE +
QObject::tr("Or submit a new issue if you think this is an error.") + NEWLINE + NEWLINE +
QObject::tr("Qv2ray will now exit."));
return -2;
}
@ -314,7 +326,7 @@ int main(int argc, char *argv[])
auto confObject = StructFromJsonString<Qv2rayConfig>(JsonToString(conf));
// Remove system translator, for loading custom translations.
qApp->removeTranslator(_sysTranslator);
LOG(MODULE_INIT, "Removing system translations")
LOG(MODULE_INIT, "Removed system translations")
if (confObject.uiConfig.language.isEmpty()) {
// Prevent empty.
@ -327,11 +339,10 @@ int main(int argc, char *argv[])
} else {
// Do not translate these.....
// If a translator fails to load, pop up a message.
QvMessageBox(
QvMessageBoxWarn(
nullptr, "Translation Failed",
"Cannot load translation for " + confObject.uiConfig.language + ", English is now used.\r\n\r\n"
"Please go to Preferences Window to change or Report a Bug at: \r\n"
"https://github.com/lhy0403/Qv2ray/issues/new");
"Cannot load translation for " + confObject.uiConfig.language + ", English is now used." + NEWLINE + NEWLINE +
"Please go to Preferences Window to change language or open an Issue");
}
// Let's save the config.
@ -345,15 +356,13 @@ int main(int argc, char *argv[])
if (!QSslSocket::supportsSsl()) {
LOG(MODULE_NETWORK, "Required OpenSSL version: " + osslReqVersion)
LOG(MODULE_NETWORK, "OpenSSL library MISSING, Quitting.")
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.") + "\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=" + osslReqVersion + "\r\n" +
"OSsl.Cr.V=" + osslCurVersion);
QvMessageBoxWarn(nullptr, QObject::tr("Dependency Missing"),
QObject::tr("Cannot find openssl libs") + NEWLINE +
QObject::tr("This could be caused by a missing of `openssl` package in your system.") + NEWLINE +
QObject::tr("If you are using an AppImage from Github Action, please report a bug.") + NEWLINE + NEWLINE +
QObject::tr("Technical Details") + NEWLINE +
"OSsl.Rq.V=" + osslReqVersion + NEWLINE +
"OSsl.Cr.V=" + osslCurVersion);
return -3;
}
@ -431,7 +440,7 @@ int main(int argc, char *argv[])
return rcode;
#ifndef QT_DEBUG
} catch (...) {
QvMessageBox(nullptr, "ERROR", "There's something wrong happened and Qv2ray will quit now.");
QvMessageBoxWarn(nullptr, "ERROR", "There's something wrong happened and Qv2ray will quit now.");
LOG(MODULE_INIT, "EXCEPTION THROWN: " __FILE__)
return -99;
}

View File

@ -94,23 +94,9 @@ namespace Qv2ray
case 105: {
// Current Connection Status
switch (instance->vinstance->ConnectionStatus) {
case STARTED: {
CL.Message = QObject::tr("Connected");
break;
}
case STOPPED: {
CL.Message = QObject::tr("Disconnected");
break;
}
case STARTING: {
CL.Message = QObject::tr("Connecting");
break;
}
}
CL.Message = instance->vinstance->KernelStarted
? QObject::tr("Connected")
: QObject::tr("Disconnected");
break;
}

View File

@ -68,11 +68,11 @@ void ConfigExporter::on_saveBtn_clicked()
void ConfigExporter::on_copyImageBtn_clicked()
{
QGuiApplication::clipboard()->setImage(image);
QvMessageBox(this, tr("Share Connection"), tr("Image has been copied to the clipboard."));
QvMessageBoxInfo(this, tr("Share Connection"), tr("Image has been copied to the clipboard."));
}
void ConfigExporter::on_copyVMessBtn_clicked()
{
QGuiApplication::clipboard()->setText(message);
QvMessageBox(this, tr("Share Connection"), tr("VMess string has been copied to the clipboard."));
QvMessageBoxInfo(this, tr("Share Connection"), tr("VMess string has been copied to the clipboard."));
}

View File

@ -26,11 +26,12 @@ ImportConfigWindow::ImportConfigWindow(QWidget *parent)
nameTxt->setText(QDateTime::currentDateTime().toString("MMdd_hhmm"));
}
QMap<QString, CONFIGROOT> ImportConfigWindow::OpenImport(bool outboundsOnly)
QMap<QString, CONFIGROOT> ImportConfigWindow::OpenImport(bool partialImport)
{
// if Outbound Only, set keepImported to false and disable the checkbox
// partial import means only import as an outbound, will set keepImported to false and disable the checkbox
// keepImportedInboundCheckBox->setChecked(!outboundsOnly);
keepImportedInboundCheckBox->setEnabled(!outboundsOnly);
keepImportedInboundCheckBox->setEnabled(!partialImport);
routeEditBtn->setEnabled(!partialImport);
this->exec();
return this->result() == QDialog::Accepted ? connections : QMap<QString, CONFIGROOT>();
}
@ -52,7 +53,7 @@ void ImportConfigWindow::on_qrFromScreenBtn_clicked()
if (str.trimmed().isEmpty()) {
LOG(MODULE_UI, "Cannot decode QR Code from an image, size: h=" + QSTRN(pix.width()) + ", v=" + QSTRN(pix.height()))
QvMessageBox(this, tr("Capture QRCode"), tr("Cannot find a valid QRCode from this region."));
QvMessageBoxWarn(this, tr("Capture QRCode"), tr("Cannot find a valid QRCode from this region."));
} else {
vmessConnectionStringTxt->appendPlainText(str.trimmed() + NEWLINE);
}
@ -71,7 +72,7 @@ void ImportConfigWindow::on_beginImportBtn_clicked()
QString path = fileLineTxt->text();
if (!V2rayKernelInstance::ValidateConfig(path)) {
QvMessageBox(this, tr("Import config file"), tr("Failed to check the validity of the config file."));
QvMessageBoxWarn(this, tr("Import config file"), tr("Failed to check the validity of the config file."));
return;
}
@ -120,12 +121,6 @@ void ImportConfigWindow::on_beginImportBtn_clicked()
break;
}
case 2: {
QvMessageBox(this, tr("TODO"), tr("TODO"));
// Subscription link.
break;
}
}
accept();
@ -143,7 +138,7 @@ void ImportConfigWindow::on_selectImageBtn_clicked()
auto str = QZXing().decodeImage(QImage::fromData(buf));
if (str.isEmpty()) {
QvMessageBox(this, tr("QRCode scanning failed"), tr("Cannot find any QRCode from the image."));
QvMessageBoxWarn(this, tr("QRCode scanning failed"), tr("Cannot find any QRCode from the image."));
return;
} else {
vmessConnectionStringTxt->appendPlainText(str.trimmed() + NEWLINE);
@ -178,7 +173,7 @@ void ImportConfigWindow::on_editFileBtn_clicked()
QFile file(fileLineTxt->text());
if (!file.exists()) {
QvMessageBox(this, tr("Edit file as JSON"), tr("Provided file not found: ") + fileLineTxt->text());
QvMessageBoxWarn(this, tr("Edit file as JSON"), tr("Provided file not found: ") + fileLineTxt->text());
return;
}
@ -204,7 +199,7 @@ void ImportConfigWindow::on_editFileBtn_clicked()
bool result = StringToFile(&str, &file);
if (!result) {
QvMessageBox(this, tr("Edit file as JSON"), tr("Failed to save file, please check if you have proper permissions"));
QvMessageBoxWarn(this, tr("Edit file as JSON"), tr("Failed to save file, please check if you have proper permissions"));
}
} else {
LOG(MODULE_FILE, "Canceled saving a file.")
@ -242,6 +237,14 @@ void ImportConfigWindow::on_subscriptionButton_clicked()
hide();
SubscribeEditor w;
w.exec();
auto importToComplex = !keepImportedInboundCheckBox->isEnabled();
connections.clear();
if (importToComplex) {
auto _result = w.GetSelectedConfig();
connections[_result.first] = _result.second;
}
accept();
}

View File

@ -377,10 +377,12 @@
</layout>
</widget>
<tabstops>
<tabstop>nameTxt</tabstop>
<tabstop>tabWidget</tabstop>
<tabstop>fileLineTxt</tabstop>
<tabstop>selectFileBtn</tabstop>
<tabstop>keepImportedInboundCheckBox</tabstop>
<tabstop>editFileBtn</tabstop>
<tabstop>selectFileBtn</tabstop>
<tabstop>imageFileEdit</tabstop>
<tabstop>selectImageBtn</tabstop>
<tabstop>qrFromScreenBtn</tabstop>
@ -388,8 +390,10 @@
<tabstop>vmessConnectionStringTxt</tabstop>
<tabstop>errorsList</tabstop>
<tabstop>connectionEditBtn</tabstop>
<tabstop>cancelImportBtn</tabstop>
<tabstop>routeEditBtn</tabstop>
<tabstop>subscriptionButton</tabstop>
<tabstop>beginImportBtn</tabstop>
<tabstop>cancelImportBtn</tabstop>
</tabstops>
<resources/>
<connections/>

View File

@ -24,7 +24,7 @@ InboundEditor::InboundEditor(INBOUND root, QWidget *parent) :
} else {
if (!root["protocol"].toString().isEmpty()) {
LOG(MODULE_UI, "Unsupported inbound type: " + inboundType)
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" +
QvMessageBoxWarn(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);
} else {
LOG(MODULE_UI, "Creating new inbound config")
@ -195,7 +195,7 @@ void InboundEditor::on_httpRemoveUserBtn_clicked()
//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."));
QvMessageBoxWarn(this, tr("Removing a user"), tr("You haven't selected a user yet."));
}
}
@ -211,7 +211,7 @@ void InboundEditor::on_httpAddUserBtn_clicked()
auto _user = list[i].toObject();
if (_user["user"].toString() == user) {
QvMessageBox(this, tr("Add a user"), tr("This user exists already."));
QvMessageBoxWarn(this, tr("Add a user"), tr("This user exists already."));
return;
}
}
@ -247,7 +247,7 @@ void InboundEditor::on_socksRemoveUserBtn_clicked()
}
}
} else {
QvMessageBox(this, tr("Removing a user"), tr("You haven't selected a user yet."));
QvMessageBoxWarn(this, tr("Removing a user"), tr("You haven't selected a user yet."));
}
}
@ -263,7 +263,7 @@ void InboundEditor::on_socksAddUserBtn_clicked()
auto _user = list[i].toObject();
if (_user["user"].toString() == user) {
QvMessageBox(this, tr("Add a user"), tr("This user exists already."));
QvMessageBoxWarn(this, tr("Add a user"), tr("This user exists already."));
return;
}
}

View File

@ -10,7 +10,7 @@
<x>0</x>
<y>0</y>
<width>815</width>
<height>503</height>
<height>545</height>
</rect>
</property>
<property name="windowTitle">

View File

@ -15,7 +15,7 @@ JsonEditor::JsonEditor(QJsonObject rootObject, QWidget *parent) :
jsonTree->setModel(&model);
model.loadJson(QJsonDocument(rootObject).toJson());
} else {
QvMessageBox(this, tr("Json Contains Syntax Errors"), tr("Original Json may contain syntax errors. Json tree is disabled."));
QvMessageBoxWarn(this, tr("Json Contains Syntax Errors"), tr("Original Json may contain syntax errors. Json tree is disabled."));
}
jsonEditor->setText(JsonToString(rootObject));
@ -29,7 +29,7 @@ QJsonObject JsonEditor::OpenEditor()
auto string = jsonEditor->toPlainText();
while (resultCode == QDialog::Accepted && !VerifyJsonString(string).isEmpty()) {
QvMessageBox(this, tr("Json Contains Syntax Errors"), tr("You must correct these errors before continue."));
QvMessageBoxWarn(this, tr("Json Contains Syntax Errors"), tr("You must correct these errors before continue."));
resultCode = this->exec();
string = jsonEditor->toPlainText();
}
@ -69,6 +69,6 @@ void JsonEditor::on_formatJsonBtn_clicked()
jsonEditor->setPlainText(JsonToString(JsonFromString(string)));
} else {
RED(jsonEditor)
QvMessageBox(this, tr("Syntax Errors"), tr("Please fix the JSON errors before continue"));
QvMessageBoxWarn(this, tr("Syntax Errors"), tr("Please fix the JSON errors before continue"));
}
}

View File

@ -50,13 +50,31 @@
#define SUBSCRIPTION_CONFIG_MODIFY_DENY(_item_) \
if (!CheckConfigType(_item_, REGULAR)) { \
QvMessageBox(this, QObject::tr("Editing a subscription config"), QObject::tr("You should not modity this property of a config from a subscription")); \
QvMessageBoxWarn(this, QObject::tr("Editing a subscription config"), QObject::tr("You should not modity this property of a config from a subscription")); \
return; \
} \
#define IsConnectableItem(item) (item != nullptr && item->childCount() == 0 && (CheckConfigType(item, REGULAR) || CheckConfigType(item, SUBSCRIPTION)))
#define IsSelectionConnectable (!connectionListWidget->selectedItems().empty() && IsConnectableItem(connectionListWidget->selectedItems().first()))
// From https://gist.github.com/jemyzhang/7130092
#define CleanUpLogs(browser) \
{\
auto maxLines = currentConfig.uiConfig.maximumLogLines; \
QTextBlock block = browser->document()->begin();\
while (block.isValid()) {\
if (browser->document()->blockCount() > maxLines) {\
QTextCursor cursor(block);\
block = block.next();\
cursor.select(QTextCursor::BlockUnderCursor);\
cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);\
cursor.removeSelectedText();\
} else {\
break;\
}\
}\
}
MainWindow *MainWindow::mwInstance = nullptr;
MainWindow::MainWindow(QWidget *parent):
@ -67,6 +85,13 @@ MainWindow::MainWindow(QWidget *parent):
currentConfig = GetGlobalConfig();
vinstance = new V2rayKernelInstance();
connect(vinstance, &V2rayKernelInstance::onProcessOutputReadyRead, this, &MainWindow::UpdateVCoreLog);
connect(vinstance, &V2rayKernelInstance::onProcessErrored, [this] {
on_stopButton_clicked();
this->show();
QvMessageBoxWarn(this, tr("V2ray vcore terminated."), tr("V2ray vcore terminated unexpectedly.") + NEWLINE + NEWLINE +
tr("To solve the problem, read the V2ray log in the log text browser."));
});
//
setupUi(this);
//
// Two browsers
@ -87,7 +112,7 @@ MainWindow::MainWindow(QWidget *parent):
masterLogBrowser->document()->adjustSize();
masterLogBrowser->setLineWrapMode(QTextBrowser::LineWrapMode::NoWrap);
//
logTimerId = startTimer(500);
qvLogTimerId = startTimer(500);
//
pacServer = new PACServer();
tcpingModel = new QvTCPingModel(3, this);
@ -128,6 +153,7 @@ MainWindow::MainWindow(QWidget *parent):
tray_SystemProxyMenu->addAction(action_Tray_SetSystemProxy);
tray_SystemProxyMenu->addAction(action_Tray_ClearSystemProxy);
tray_SystemProxyMenu->setTitle(tr("System Proxy"));
tray_SystemProxyMenu->setEnabled(false);
//
tray_RootMenu->addAction(action_Tray_ShowHide);
tray_RootMenu->addSeparator();
@ -158,7 +184,7 @@ MainWindow::MainWindow(QWidget *parent):
//
QAction *action_RCM_RenameConnection = new QAction(tr("Rename"), this);
QAction *action_RCM_StartThis = new QAction(tr("Connect to this"), this);
QAction *action_RCM_ConvToComplex = new QAction(tr("Edit as Complex Config"), this);
QAction *action_RCM_ConvToComplex = new QAction(QICON_R("edit.png"), tr("Edit as Complex Config"), this);
QAction *action_RCM_EditJson = new QAction(QICON_R("json.png"), tr("Edit as Json"), this);
QAction *action_RCM_ShareQR = new QAction(QICON_R("share.png"), tr("Share as QRCode/VMess URL"), this);
//
@ -178,11 +204,11 @@ MainWindow::MainWindow(QWidget *parent):
hTray->show();
//
connectionListMenu = new QMenu(this);
connectionListMenu->addAction(action_RCM_RenameConnection);
connectionListMenu->addAction(action_RCM_StartThis);
connectionListMenu->addAction(action_RCM_ConvToComplex);
connectionListMenu->addAction(action_RCM_EditJson);
connectionListMenu->addAction(action_RCM_ShareQR);
connectionListMenu->addAction(action_RCM_RenameConnection);
connectionListMenu->addAction(action_RCM_EditJson);
connectionListMenu->addAction(action_RCM_ConvToComplex);
//
OnConfigListChanged(false);
//
@ -223,13 +249,18 @@ MainWindow::MainWindow(QWidget *parent):
MWFindAndStartAutoConfig();
// If we are not connected to anything, show the MainWindow.
if (vinstance->ConnectionStatus != STARTED) {
if (!vinstance->KernelStarted) {
this->show();
}
connect(requestHelper, &QvHttpRequestHelper::httpRequestFinished, this, &MainWindow::VersionUpdate);
requestHelper->get("https://api.github.com/repos/lhy0403/Qv2ray/releases/latest");
StartProcessingPlugins();
if (StartupOption.enableToolbarPlguin) {
LOG(MODULE_UI, "Plugin daemon is enabled.")
StartProcessingPlugins();
}
CheckSubscriptionsUpdate();
}
@ -280,7 +311,7 @@ void MainWindow::keyPressEvent(QKeyEvent *e)
void MainWindow::on_action_StartThis_triggered()
{
if (!IsSelectionConnectable) {
QvMessageBox(this, tr("No connection selected!"), tr("Please select a config from the list."));
QvMessageBoxWarn(this, tr("No connection selected!"), tr("Please select a config from the list."));
return;
}
@ -324,9 +355,9 @@ void MainWindow::VersionUpdate(QByteArray &data)
void MainWindow::OnConfigListChanged(bool need_restart)
{
bool isRunning = vinstance->ConnectionStatus == STARTED;
auto wasRunning = vinstance->KernelStarted && need_restart;
if (isRunning && need_restart) on_stopButton_clicked();
if (wasRunning) on_stopButton_clicked();
LOG(MODULE_UI, "Loading new GlobalConfig")
SetEditWidgetEnable(false);
@ -354,13 +385,12 @@ void MainWindow::OnConfigListChanged(bool need_restart)
connections[name] = _o;
auto item = new QTreeWidgetItem(QStringList() << _o.connectionName);
item->setData(0, Qt::UserRole, QVariant::fromValue<QvConfigIdentifier>(_o));
DEBUG(MODULE_UI, ItemConnectionIdentifier(item).IdentifierString())
connectionListWidget->addTopLevelItem(item);
}
for (auto i = 0; i < _subsConnections.count(); i++) {
auto subName = _subsConnections.keys()[i];
auto subTopLevel = new QTreeWidgetItem(QStringList() << tr("Subscription:") + " " + subName);
auto subTopLevel = new QTreeWidgetItem(QStringList() << tr("Subscription") + ": " + subName);
connectionListWidget->addTopLevelItem(subTopLevel);
for (auto j = 0; j < _subsConnections.values()[i].count(); j++) {
@ -375,7 +405,6 @@ void MainWindow::OnConfigListChanged(bool need_restart)
connections[connName] = _o;
auto item = new QTreeWidgetItem(QStringList() << _o.connectionName);
item->setData(0, Qt::UserRole, QVariant::fromValue<QvConfigIdentifier>(_o));
DEBUG(MODULE_UI, ItemConnectionIdentifier(item).IdentifierString())
subTopLevel->addChild(item);
}
}
@ -399,11 +428,11 @@ void MainWindow::OnConfigListChanged(bool need_restart)
connectionListWidget->sortItems(0, Qt::AscendingOrder);
if (isRunning && need_restart) on_startButton_clicked();
if (wasRunning) on_startButton_clicked();
}
MainWindow::~MainWindow()
{
killTimer(logTimerId);
killTimer(qvLogTimerId);
hTray->hide();
delete this->hTray;
delete this->vinstance;
@ -411,6 +440,7 @@ MainWindow::~MainWindow()
void MainWindow::UpdateVCoreLog(const QString &log)
{
vCoreLogBrowser->append(log);
CleanUpLogs(vCoreLogBrowser)
setMasterLogHBar();
}
void MainWindow::setMasterLogHBar()
@ -424,7 +454,9 @@ void MainWindow::setMasterLogHBar()
}
void MainWindow::on_startButton_clicked()
{
if (vinstance->ConnectionStatus != STARTED) {
vCoreLogBrowser->clear();
if (!vinstance->KernelStarted) {
// Reset the graph
for (int i = 0; i < 30 ; i++) {
uploadList[i] = 0;
@ -435,7 +467,7 @@ void MainWindow::on_startButton_clicked()
// Check Selection
if (CurrentConnectionIdentifier.isEmpty()) {
QvMessageBox(this, tr("No connection selected!"), tr("Please select a config from the list."));
QvMessageBoxWarn(this, tr("No connection selected!"), tr("Please select a config from the list."));
return;
}
@ -471,7 +503,7 @@ void MainWindow::on_startButton_clicked()
void MainWindow::on_stopButton_clicked()
{
if (vinstance->ConnectionStatus != STOPPED) {
if (vinstance->KernelStarted) {
// Is running or starting
killTimer(speedTimerId);
killTimer(pingTimerId);
@ -479,7 +511,6 @@ void MainWindow::on_stopButton_clicked()
MWStopConnection();
//
hTray->setToolTip(TRAY_TOOLTIP_PREFIX);
vCoreLogBrowser->clear();
statusLabel->setText(tr("Disconnected"));
action_Tray_Start->setEnabled(true);
action_Tray_Stop->setEnabled(false);
@ -513,7 +544,7 @@ void MainWindow::on_activatedTray(QSystemTrayIcon::ActivationReason reason)
break;
case QSystemTrayIcon::MiddleClick:
if (this->vinstance->ConnectionStatus == STARTED) {
if (vinstance->KernelStarted) {
on_stopButton_clicked();
} else {
on_startButton_clicked();
@ -549,7 +580,10 @@ void MainWindow::ToggleVisibility()
}
void MainWindow::quit()
{
StopProcessingPlugins();
if (StartupOption.enableToolbarPlguin) {
StopProcessingPlugins();
}
tcpingModel->StopAllPing();
on_stopButton_clicked();
ExitQv2ray();
@ -578,7 +612,7 @@ void MainWindow::ShowAndSetConnection(QvConfigIdentifier fullIdentifier, bool Se
}
if (conf.configType == CONNECTION_SUBSCRIPTION) {
routeCountLabel->setText(routeCountLabel->text().append(" (" + tr("From subscription") + ":" + conf.subscriptionName + ")"));
routeCountLabel->setText(routeCountLabel->text().append(" (" + tr("Subscription") + ":" + conf.subscriptionName + ")"));
}
// Get Connection info
@ -625,8 +659,7 @@ void MainWindow::on_connectionListWidget_currentItemChanged(QTreeWidgetItem *cur
if (!IsConnectableItem(current)) return;
// no need to check !isRenamingInProgress since it's always true.
bool canSetConnection = vinstance->ConnectionStatus != STARTED;
ShowAndSetConnection(ItemConnectionIdentifier(current), canSetConnection, false);
ShowAndSetConnection(ItemConnectionIdentifier(current), !vinstance->KernelStarted, false);
//on_connectionListWidget_itemClicked(current, 0);
}
void MainWindow::on_connectionListWidget_customContextMenuRequested(const QPoint &pos)
@ -666,17 +699,17 @@ void MainWindow::on_connectionListWidget_itemChanged(QTreeWidgetItem *item, int)
bool canContinueRename = true;
if (newIdentifier.connectionName.trimmed().isEmpty()) {
QvMessageBox(this, tr("Rename a Connection"), tr("The name cannot be empty"));
QvMessageBoxWarn(this, tr("Rename a Connection"), tr("The name cannot be empty"));
canContinueRename = false;
}
if (currentConfig.configs.contains(newIdentifier.connectionName)) {
QvMessageBox(this, tr("Rename a Connection"), tr("The name has been used already, Please choose another."));
QvMessageBoxWarn(this, tr("Rename a Connection"), tr("The name has been used already, Please choose another."));
canContinueRename = false;
}
if (!IsValidFileName(newIdentifier.connectionName + QV2RAY_CONFIG_FILE_EXTENSION)) {
QvMessageBox(this, tr("Rename a Connection"), tr("The name you suggested is not valid, please try another."));
QvMessageBoxWarn(this, tr("Rename a Connection"), tr("The name you suggested is not valid, please try another."));
canContinueRename = false;
}
@ -708,7 +741,7 @@ void MainWindow::on_connectionListWidget_itemChanged(QTreeWidgetItem *item, int)
if (CurrentConnectionIdentifier == renameOriginalIdentifier) {
CurrentConnectionIdentifier = newIdentifier;
if (vinstance->ConnectionStatus == STARTED) {
if (vinstance->KernelStarted) {
on_reconnectButton_clicked();
}
}
@ -767,7 +800,7 @@ void MainWindow::on_removeConfigButton_clicked()
currentConfig.configs.removeOne(conn.connectionName);
if (!RemoveConnection(conn.connectionName)) {
QvMessageBox(this, tr("Removing this Connection"), tr("Failed to delete connection file, please delete manually."));
QvMessageBoxWarn(this, tr("Removing this Connection"), tr("Failed to delete connection file, please delete manually."));
}
} else if (connData.configType == CONNECTION_SUBSCRIPTION) {
if (subscriptionRemovalCheckStatus == -1) {
@ -778,7 +811,7 @@ void MainWindow::on_removeConfigButton_clicked()
if (subscriptionRemovalCheckStatus == 1) {
if (!RemoveSubscriptionConnection(connData.subscriptionName, connData.connectionName)) {
QvMessageBox(this, tr("Removing this Connection"), tr("Failed to delete connection file, please delete manually."));
QvMessageBoxWarn(this, tr("Removing this Connection"), tr("Failed to delete connection file, please delete manually."));
}
}
} else {
@ -816,7 +849,7 @@ void MainWindow::on_editConfigButton_clicked()
{
// Check if we have a connection selected...
if (!IsSelectionConnectable) {
QvMessageBox(this, tr("No Config Selected"), tr("Please Select a Config"));
QvMessageBoxWarn(this, tr("No Config Selected"), tr("Please Select a Config"));
return;
}
@ -867,7 +900,7 @@ void MainWindow::on_action_RCM_ConvToComplex_triggered()
{
// Check if we have a connection selected...
if (!IsSelectionConnectable) {
QvMessageBox(this, tr("No Config Selected"), tr("Please Select a Config"));
QvMessageBoxWarn(this, tr("No Config Selected"), tr("Please Select a Config"));
return;
}
@ -898,7 +931,7 @@ void MainWindow::on_action_RCM_EditJson_triggered()
{
// Check if we have a connection selected...
if (!IsSelectionConnectable) {
QvMessageBox(this, tr("No Config Selected"), tr("Please Select a Config"));
QvMessageBoxWarn(this, tr("No Config Selected"), tr("Please Select a Config"));
return;
}
@ -934,18 +967,18 @@ void MainWindow::on_pingTestBtn_clicked()
if (QvMessageBoxAsk(this, tr("Latency Test"), tr("You are about to run latency test on all servers, do you want to continue?")) == QMessageBox::Yes) {
aliases.append(connections.keys());
}
} else if (selection.count() == 1) {
if (IsSelectionConnectable) {
// Current selection is a config
aliases.append(ItemConnectionIdentifier(selection.first()));
} else {
// Current selection is a subscription or... something else strange.
// So we add another check to make sure the selected one is a subscription entry.
if (selection.first()->childCount() > 0) {
} else {
for (auto i = 0; i < selection.count(); i++) {
auto thisItem = selection[i];
if (thisItem->childCount() > 0) {
// So we add another check to make sure the selected one is a subscription entry.
// Loop to add all sub-connections to the list.
for (auto i = 0; i < selection.first()->childCount(); i++) {
aliases.append(ItemConnectionIdentifier(selection.first()->child(i)));
for (auto j = 0; j < thisItem->childCount(); j++) {
aliases.append(ItemConnectionIdentifier(thisItem->child(j)));
}
} else {
aliases.append(ItemConnectionIdentifier(thisItem));
}
}
}
@ -976,7 +1009,7 @@ void MainWindow::on_shareBtn_clicked()
ConfigExporter v(vmess, this);
v.OpenExport();
} else {
QvMessageBox(this, tr("Share Connection"), tr("There're no support of sharing configs other than vmess"));
QvMessageBoxWarn(this, tr("Share Connection"), tr("There're no support of sharing configs other than vmess"));
}
}
void MainWindow::on_action_RCM_ShareQR_triggered()
@ -1024,12 +1057,14 @@ void MainWindow::timerEvent(QTimerEvent *event)
dataamountLabel->setText(totalDataUp + NEWLINE + totalDataDown);
//
hTray->setToolTip(TRAY_TOOLTIP_PREFIX NEWLINE + tr("Connected: ") + CurrentConnectionIdentifier.IdentifierString() + NEWLINE "Up: " + totalSpeedUp + " Down: " + totalSpeedDown);
} else if (event->timerId() == logTimerId) {
} else if (event->timerId() == qvLogTimerId) {
QString lastLog = readLastLog();
if (!lastLog.isEmpty()) {
qvAppLogBrowser->append(lastLog);
}
CleanUpLogs(vCoreLogBrowser)
} else if (event->timerId() == pingTimerId) {
MWTryPingConnection(CurrentConnectionIdentifier);
}

File diff suppressed because it is too large Load Diff

View File

@ -112,7 +112,7 @@ class MainWindow : public QMainWindow, Ui::MainWindow
//
// ID for QTimers
//
int logTimerId;
int qvLogTimerId;
int speedTimerId;
int pingTimerId;
//
@ -143,7 +143,7 @@ class MainWindow : public QMainWindow, Ui::MainWindow
QAction *action_Tray_SetSystemProxy;
QAction *action_Tray_ClearSystemProxy;
//
// ----------------------------------- Extra Headers For w_MainWindow_extra.cpp Handling v2ray Connectivities.
// ----------------------------------- Extra Headers For w_MainWindow_extra.cpp Handling V2ray Connectivities.
bool systemProxyEnabled;
void MWFindAndStartAutoConfig();
bool MWtryStartConnection();

View File

@ -661,7 +661,9 @@
<tabstop>stopButton</tabstop>
<tabstop>reconnectButton</tabstop>
<tabstop>clearlogButton</tabstop>
<tabstop>subsButton</tabstop>
<tabstop>preferencesBtn</tabstop>
<tabstop>connectionListWidget</tabstop>
<tabstop>importConfigButton</tabstop>
<tabstop>duplicateBtn</tabstop>
<tabstop>removeConfigButton</tabstop>
@ -669,6 +671,8 @@
<tabstop>editJsonBtn</tabstop>
<tabstop>pingTestBtn</tabstop>
<tabstop>shareBtn</tabstop>
<tabstop>masterLogBrowser</tabstop>
<tabstop>speedChart</tabstop>
</tabstops>
<resources/>
<connections/>

View File

@ -48,9 +48,9 @@ void MainWindow::MWFindAndStartAutoConfig()
tray_RootMenu->actions()[0]->setText(tr("Show"));
on_startButton_clicked();
} else {
QvMessageBox(this, tr("Autostarting a config"), tr("Could not find a specified config named: ") + NEWLINE +
name + NEWLINE + NEWLINE +
tr("Please reset the settings in Preference Window"));
QvMessageBoxWarn(this, tr("Autostarting a config"), tr("Could not find a specified config named: ") + NEWLINE +
name + NEWLINE + NEWLINE +
tr("Please reset the settings in Preference Window"));
}
} else if (connectionListWidget->topLevelItemCount() > 0) {
// Make the first one our default selected item.
@ -97,8 +97,8 @@ void MainWindow::MWSetSystemProxy()
LOG(MODULE_PROXY, "Failed to process pac due to following reasons:")
LOG(MODULE_PROXY, " --> PAC is configured to use socks but socks is not enabled.")
LOG(MODULE_PROXY, " --> PAC is configuted to use http but http is not enabled.")
QvMessageBox(this, tr("PAC Processing Failed"), tr("HTTP or SOCKS inbound is not properly configured for PAC") +
NEWLINE + tr("Qv2ray will continue, but will not set system proxy."));
QvMessageBoxWarn(this, tr("PAC Processing Failed"), tr("HTTP or SOCKS inbound is not properly configured for PAC") +
NEWLINE + tr("Qv2ray will continue, but will not set system proxy."));
canSetSystemProxy = false;
}
} else {
@ -106,10 +106,10 @@ void MainWindow::MWSetSystemProxy()
if (httpEnabled) {
// Not use PAC, System proxy should use HTTP
LOG(MODULE_PROXY, "Using system proxy with HTTP")
proxyAddress = "localhost";
proxyAddress = "http://localhost";
} else {
LOG(MODULE_PROXY, "HTTP is not enabled, cannot set system proxy.")
QvMessageBox(this, tr("Cannot set system proxy"), tr("HTTP inbound is not enabled"));
QvMessageBoxWarn(this, tr("Cannot set system proxy"), tr("HTTP inbound is not enabled"));
canSetSystemProxy = false;
}
}
@ -153,15 +153,15 @@ bool MainWindow::MWtryStartConnection()
pacProxyString = "SOCKS5 " + pacIP + ":" + QSTRN(currentConfig.inboundConfig.socks_port);
} else {
LOG(MODULE_UI, "PAC is using SOCKS, but it is not enabled")
QvMessageBox(this, tr("Configuring PAC"), tr("Could not start PAC server as it is configured to use SOCKS, but it is not enabled"));
QvMessageBoxWarn(this, tr("Configuring PAC"), tr("Could not start PAC server as it is configured to use SOCKS, but it is not enabled"));
canStartPAC = false;
}
} else {
if (httpEnabled) {
pacProxyString = "PROXY http://" + pacIP + ":" + QSTRN(currentConfig.inboundConfig.http_port);
pacProxyString = "PROXY " + pacIP + ":" + QSTRN(currentConfig.inboundConfig.http_port);
} else {
LOG(MODULE_UI, "PAC is using HTTP, but it is not enabled")
QvMessageBox(this, tr("Configuring PAC"), tr("Could not start PAC server as it is configured to use HTTP, but it is not enabled"));
QvMessageBoxWarn(this, tr("Configuring PAC"), tr("Could not start PAC server as it is configured to use HTTP, but it is not enabled"));
canStartPAC = false;
}
}
@ -205,7 +205,7 @@ void MainWindow::MWTryPingConnection(const QvConfigIdentifier &alias)
int port = get<1>(info);
tcpingModel->StartPing(alias, host, port);
} catch (...) {
QvMessageBox(this, tr("Latency Test"), tr("Failed to test latency for this connection."));
QvMessageBoxWarn(this, tr("Latency Test"), tr("Failed to test latency for this connection."));
}
}
@ -239,9 +239,9 @@ void MainWindow::CheckSubscriptionsUpdate()
}
if (!updateList.isEmpty()) {
QvMessageBox(this, tr("Update Subscriptions"),
tr("There are subscriptions need to be updated, please go to subscriptions window to update them.") + NEWLINE + NEWLINE +
tr("These subscriptions are out-of-date: ") + NEWLINE + Stringify(updateList));
QvMessageBoxWarn(this, tr("Update Subscriptions"),
tr("There are subscriptions need to be updated, please go to subscriptions window to update them.") + NEWLINE + NEWLINE +
tr("These subscriptions are out-of-date: ") + NEWLINE + updateList.join(";"));
on_subsButton_clicked();
}
}

View File

@ -29,144 +29,20 @@
<bool>true</bool>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0">
<layout class="QFormLayout" name="formLayout_5">
<item row="0" column="0">
<widget class="QLabel" name="ipLabel_3">
<property name="text">
<string>Tag</string>
<item row="2" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="tagTxt">
<property name="placeholderText">
<string>Tag of this outbound setting</string>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="ipLabel">
<property name="text">
<string>Host</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="ipLineEdit">
<property name="placeholderText">
<string>Hostname or IP/IPv6 Address</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="portLabel">
<property name="text">
<string>Port</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="portLineEdit">
<property name="maxLength">
<number>5</number>
</property>
<property name="placeholderText">
<string>Port</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_25">
<property name="text">
<string>Type</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QComboBox" name="outBoundTypeCombo">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item>
<property name="text">
<string notr="true">VMess</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">ShadowSocks</string>
</property>
</item>
<item>
<property name="text">
<string>Socks</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item row="0" column="1">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Misc Settings</string>
</property>
<layout class="QFormLayout" name="formLayout_8">
<item row="0" column="0">
<widget class="QLabel" name="label_32">
<property name="text">
<string>Use Mux</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="muxEnabledCB">
<property name="text">
<string>Enabled</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_33">
<property name="text">
<string>Mux Concurrency</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="muxConcurrencyTxt">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>1024</number>
</property>
<property name="value">
<number>8</number>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="useFPCB">
<property name="text">
<string>Enabled</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_23">
<property name="text">
<string>Use Forward Proxy</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QGroupBox" name="outboundSettingWrapper">
<property name="title">
@ -1059,22 +935,193 @@
</layout>
</widget>
</item>
<item row="2" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<item row="0" column="1">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Misc Settings</string>
</property>
<layout class="QFormLayout" name="formLayout_8">
<item row="0" column="0">
<widget class="QLabel" name="label_32">
<property name="text">
<string>Use Mux</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="muxEnabledCB">
<property name="text">
<string>Enabled</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_33">
<property name="text">
<string>Mux Concurrency</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="muxConcurrencyTxt">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>1024</number>
</property>
<property name="value">
<number>8</number>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="useFPCB">
<property name="text">
<string>Enabled</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_23">
<property name="text">
<string>Use Forward Proxy</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="0" column="0">
<layout class="QFormLayout" name="formLayout_5">
<item row="0" column="0">
<widget class="QLabel" name="ipLabel_3">
<property name="text">
<string>Tag</string>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="tagTxt">
<property name="placeholderText">
<string>Tag of this outbound setting</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="ipLabel">
<property name="text">
<string>Host</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="ipLineEdit">
<property name="placeholderText">
<string>Hostname or IP/IPv6 Address</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="portLabel">
<property name="text">
<string>Port</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="portLineEdit">
<property name="maxLength">
<number>5</number>
</property>
<property name="placeholderText">
<string>Port</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_25">
<property name="text">
<string>Type</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QComboBox" name="outBoundTypeCombo">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item>
<property name="text">
<string notr="true">VMess</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">ShadowSocks</string>
</property>
</item>
<item>
<property name="text">
<string>Socks</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<tabstops>
<tabstop>tagTxt</tabstop>
<tabstop>ipLineEdit</tabstop>
<tabstop>portLineEdit</tabstop>
<tabstop>outBoundTypeCombo</tabstop>
<tabstop>muxEnabledCB</tabstop>
<tabstop>muxConcurrencyTxt</tabstop>
<tabstop>useFPCB</tabstop>
<tabstop>idLineEdit</tabstop>
<tabstop>alterLineEdit</tabstop>
<tabstop>securityCombo</tabstop>
<tabstop>tlsCB</tabstop>
<tabstop>tranportCombo</tabstop>
<tabstop>tcpHeaderTypeCB</tabstop>
<tabstop>tcpRequestTxt</tabstop>
<tabstop>tcpRequestEditBtn</tabstop>
<tabstop>tcpRequestDefBtn</tabstop>
<tabstop>tcpRespTxt</tabstop>
<tabstop>tcpResponseEditBtn</tabstop>
<tabstop>tcpRespDefBtn</tabstop>
<tabstop>httpPathTxt</tabstop>
<tabstop>httpHostTxt</tabstop>
<tabstop>wsPathTxt</tabstop>
<tabstop>wsHeadersTxt</tabstop>
<tabstop>kcpMTU</tabstop>
<tabstop>kcpTTI</tabstop>
<tabstop>kcpUploadCapacSB</tabstop>
<tabstop>kcpCongestionCB</tabstop>
<tabstop>kcpDownCapacitySB</tabstop>
<tabstop>kcpReadBufferSB</tabstop>
<tabstop>kcpWriteBufferSB</tabstop>
<tabstop>kcpHeaderType</tabstop>
<tabstop>dsPathTxt</tabstop>
<tabstop>quicSecurityCB</tabstop>
<tabstop>quicKeyTxt</tabstop>
<tabstop>quicHeaderTypeCB</tabstop>
<tabstop>soMarkSpinBox</tabstop>
<tabstop>tcpFastOpenCB</tabstop>
<tabstop>tProxyCB</tabstop>
<tabstop>ss_emailTxt</tabstop>
<tabstop>ss_passwordTxt</tabstop>
<tabstop>ss_encryptionMethod</tabstop>
<tabstop>ss_levelSpin</tabstop>
<tabstop>ss_otaCheckBox</tabstop>
<tabstop>socks_UserNameTxt</tabstop>
<tabstop>socks_PasswordTxt</tabstop>
</tabstops>
<resources/>
<connections>
<connection>

View File

@ -22,6 +22,14 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent),
setupUi(this);
textBrowser->setHtml(StringFromFile(new QFile(":/assets/credit.html")));
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
//
// Set network Toolbar page state.
networkToolbarPage->setEnabled(StartupOption.enableToolbarPlguin);
if (!StartupOption.enableToolbarPlguin) {
networkToolbarInfoLabel->setText(tr("Qv2ray Network Toolbar is disabled and still under test. Add --withNetworkToolbar to enable."));
}
// We add locales
languageComboBox->clear();
QDirIterator it(":/translations");
@ -55,17 +63,16 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent),
//
listenIPTxt->setText(CurrentConfig.inboundConfig.listenip);
bool pacEnabled = CurrentConfig.inboundConfig.pacConfig.enablePAC;
enablePACCB->setChecked(pacEnabled);
pacGroupBox->setChecked(pacEnabled);
setSysProxyCB->setChecked(CurrentConfig.inboundConfig.setSystemProxy);
//
// PAC
pacGroupBox->setEnabled(pacEnabled);
pacPortSB->setValue(CurrentConfig.inboundConfig.pacConfig.port);
pacProxyTxt->setText(CurrentConfig.inboundConfig.pacConfig.localIP);
pacProxyCB->setCurrentIndex(CurrentConfig.inboundConfig.pacConfig.useSocksProxy ? 1 : 0);
//
bool have_http = CurrentConfig.inboundConfig.useHTTP;
httpCB->setChecked(have_http);
httpGroupBox->setChecked(have_http);
httpPortLE->setValue(CurrentConfig.inboundConfig.http_port);
httpAuthCB->setChecked(CurrentConfig.inboundConfig.http_useAuth);
//
@ -74,11 +81,10 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent),
httpAuthPasswordTxt->setEnabled(CurrentConfig.inboundConfig.http_useAuth);
httpAuthUsernameTxt->setText(CurrentConfig.inboundConfig.httpAccount.user);
httpAuthPasswordTxt->setText(CurrentConfig.inboundConfig.httpAccount.pass);
httpGroupBox->setEnabled(have_http);
//
//
bool have_socks = CurrentConfig.inboundConfig.useSocks;
socksCB->setChecked(have_socks);
socksGroupBox->setChecked(have_socks);
socksPortLE->setValue(CurrentConfig.inboundConfig.socks_port);
//
socksAuthCB->setChecked(CurrentConfig.inboundConfig.socks_useAuth);
@ -90,7 +96,6 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent),
socksUDPCB->setChecked(CurrentConfig.inboundConfig.socksUDP);
socksUDPIP->setEnabled(CurrentConfig.inboundConfig.socksUDP);
socksUDPIP->setText(CurrentConfig.inboundConfig.socksLocalIP);
socksGroupBox->setEnabled(have_socks);
//
//
vCorePathTxt->setText(CurrentConfig.v2CorePath);
@ -125,7 +130,7 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent),
nsBarPagesList->setCurrentRow(0);
on_nsBarPagesList_currentRowChanged(0);
} else {
nsBarVerticalLayout->setEnabled(false);
networkToolbarSettingsFrame->setEnabled(false);
nsBarLinesList->setEnabled(false);
nsBarLineDelBTN->setEnabled(false);
nsBarLineAddBTN->setEnabled(false);
@ -154,8 +159,7 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent),
autoStartConnCombo->setCurrentText(autoCon);
// FP Settings
fpEnabledCB->setChecked(CurrentConfig.connectionConfig.forwardProxyConfig.enableForwardProxy);
fpFrame->setEnabled(fpEnabledCB->isChecked());
fpGroupBox->setChecked(CurrentConfig.connectionConfig.forwardProxyConfig.enableForwardProxy);
fpUsernameTx->setText(CurrentConfig.connectionConfig.forwardProxyConfig.username);
fpPasswordTx->setText(CurrentConfig.connectionConfig.forwardProxyConfig.password);
fpAddressTx->setText(CurrentConfig.connectionConfig.forwardProxyConfig.serverAddress);
@ -165,6 +169,10 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent),
fpUsernameTx->setEnabled(fpUseAuthCB->isChecked());
fpPasswordTx->setEnabled(fpUseAuthCB->isChecked());
//
maxLogLinesSB->setValue(CurrentConfig.uiConfig.maximumLogLines);
//
pacListenAddrLabel->setText("http://" + (pacProxyTxt->text().isEmpty() ? "127.0.0.1" : pacProxyTxt->text()) + ":" + QSTRN(pacPortSB->value()) + "/pac");
//
finishedLoading = true;
}
@ -175,32 +183,38 @@ PreferencesWindow::~PreferencesWindow()
void PreferencesWindow::on_buttonBox_accepted()
{
int sp = socksPortLE->text().toInt();
int hp = httpPortLE->text().toInt() ;
QSet<int> ports;
auto size = 0;
if (!(sp == 0 || hp == 0) && sp == hp) {
QvMessageBox(this, tr("Preferences"), tr("Port numbers cannot be the same"));
return;
if (CurrentConfig.inboundConfig.useHTTP) {
size ++;
ports << CurrentConfig.inboundConfig.http_port;
}
SetGlobalConfig(CurrentConfig);
emit s_reload_config(IsConnectionPropertyChanged);
}
if (CurrentConfig.inboundConfig.useSocks) {
size ++;
ports << CurrentConfig.inboundConfig.socks_port;
}
void PreferencesWindow::on_httpCB_stateChanged(int checked)
{
NEEDRESTART
bool enabled = checked == Qt::Checked;
httpGroupBox->setEnabled(enabled);
CurrentConfig.inboundConfig.useHTTP = enabled;
}
if (CurrentConfig.inboundConfig.pacConfig.enablePAC) {
size ++;
ports << CurrentConfig.inboundConfig.pacConfig.port;
}
void PreferencesWindow::on_socksCB_stateChanged(int checked)
{
NEEDRESTART
bool enabled = checked == Qt::Checked;
socksGroupBox->setEnabled(enabled);
CurrentConfig.inboundConfig.useSocks = enabled;
if (!StartupOption.noAPI) {
size ++;
ports << CurrentConfig.connectionConfig.statsPort;
}
if (ports.size() != size) {
// Duplicates detected.
QvMessageBoxWarn(this, tr("Preferences"), tr("Duplicated port numbers detected, please check the port number settings."));
this->show();
this->exec();
} else {
SetGlobalConfig(CurrentConfig);
emit s_reload_config(IsConnectionPropertyChanged);
}
}
void PreferencesWindow::on_httpAuthCB_stateChanged(int checked)
@ -224,21 +238,7 @@ void PreferencesWindow::on_socksAuthCB_stateChanged(int checked)
void PreferencesWindow::on_languageComboBox_currentTextChanged(const QString &arg1)
{
LOADINGCHECK
//
// A strange bug prevents us to change the UI language online
// https://github.com/lhy0403/Qv2ray/issues/34
//
CurrentConfig.uiConfig.language = arg1;
//
//
//if (QApplication::installTranslator(getTranslator(arg1))) {
// LOG(MODULE_UI, "Loaded translations " + arg1)
// retranslateUi(this);
//} else {
// QvMessageBox(this, tr("#Preferences"), tr("#SwitchTranslationError"));
//}
//
//emit retranslateUi(this);
}
void PreferencesWindow::on_logLevelComboBox_currentIndexChanged(int index)
@ -299,7 +299,7 @@ void PreferencesWindow::on_localDNSCb_stateChanged(int arg1)
void PreferencesWindow::on_selectVAssetBtn_clicked()
{
NEEDRESTART
QString dir = QFileDialog::getExistingDirectory(this, tr("Open v2ray assets folder"), QDir::currentPath());
QString dir = QFileDialog::getExistingDirectory(this, tr("Open V2ray assets folder"), QDir::currentPath());
if (!dir.isEmpty()) {
vCoreAssetsPathTxt->setText(dir);
@ -309,7 +309,7 @@ void PreferencesWindow::on_selectVAssetBtn_clicked()
void PreferencesWindow::on_selectVCoreBtn_clicked()
{
QString core = QFileDialog::getOpenFileName(this, tr("Open v2ray core file"), QDir::currentPath());
QString core = QFileDialog::getOpenFileName(this, tr("Open V2ray core file"), QDir::currentPath());
if (!core.isEmpty()) {
vCorePathTxt->setText(core);
@ -363,26 +363,26 @@ void PreferencesWindow::on_tProxyCheckBox_stateChanged(int arg1)
if (finishedLoading) {
// Set UID and GID for linux
// Steps:
// --> 1. Copy v2ray core files to the #CONFIG_DIR#/vcore/ dir.
// --> 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.
// --> 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.") + NEWLINE + NEWLINE +
tr("Qv2ray will copy your v2ray core to this path: ") + NEWLINE + QV2RAY_DEFAULT_VCORE_PATH + NEWLINE + NEWLINE +
tr("This will append capabilities to the V2ray executable.") + NEWLINE + NEWLINE +
tr("Qv2ray will copy your V2ray core to this path: ") + NEWLINE + QV2RAY_DEFAULT_VCORE_PATH + NEWLINE + NEWLINE +
tr("If anything goes wrong after enabling this, please refer to issue #57 or the link below:") + NEWLINE +
" https://lhy0403.github.io/Qv2ray/zh-CN/FAQ.html ") != QMessageBox::Yes) {
tProxyCheckBox->setChecked(false);
LOG(MODULE_UI, "Canceled enabling tProxy feature.")
} else {
LOG(MODULE_VCORE, "ENABLING tProxy Support")
LOG(MODULE_FILE, " --> Origin v2ray core file is at: " + CurrentConfig.v2CorePath)
LOG(MODULE_FILE, " --> Origin V2ray core file is at: " + CurrentConfig.v2CorePath)
auto v2ctlPath = QFileInfo(CurrentConfig.v2CorePath).path() + "/v2ctl";
auto newPath = QFileInfo(QV2RAY_DEFAULT_VCORE_PATH).path();
//
LOG(MODULE_FILE, " --> Origin v2ctl file is at: " + v2ctlPath)
LOG(MODULE_FILE, " --> New v2ray files will be placed in: " + newPath)
LOG(MODULE_FILE, " --> New V2ray files will be placed in: " + newPath)
//
LOG(MODULE_FILE, " --> Copying files....")
@ -402,25 +402,25 @@ void PreferencesWindow::on_tProxyCheckBox_stateChanged(int arg1)
}
QString vCoreresult = QFile(CurrentConfig.v2CorePath).copy(QV2RAY_DEFAULT_VCORE_PATH) ? "OK" : "FAILED";
LOG(MODULE_FILE, " --> v2ray Core: " + vCoreresult)
LOG(MODULE_FILE, " --> V2ray Core: " + vCoreresult)
//
QString vCtlresult = QFile(v2ctlPath).copy(newPath + "/v2ctl") ? "OK" : "FAILED";
LOG(MODULE_FILE, " --> v2ray Ctl: " + vCtlresult)
LOG(MODULE_FILE, " --> V2ray Ctl: " + vCtlresult)
//
if (vCoreresult == "OK" && vCtlresult == "OK") {
LOG(MODULE_VCORE, " --> Done copying files.")
on_vCorePathTxt_textEdited(QV2RAY_DEFAULT_VCORE_PATH);
} else {
LOG(MODULE_VCORE, "FAILED to copy v2ray files. Aborting.")
QvMessageBox(this, tr("Enable tProxy Support"),
tr("Qv2ray cannot copy one or both v2ray files from: ") + NEWLINE + NEWLINE +
CurrentConfig.v2CorePath + NEWLINE + v2ctlPath + NEWLINE + NEWLINE +
tr("to this path: ") + NEWLINE + newPath);
LOG(MODULE_VCORE, "FAILED to copy V2ray files. Aborting.")
QvMessageBoxWarn(this, tr("Enable tProxy Support"),
tr("Qv2ray cannot copy one or both V2ray files from: ") + NEWLINE + NEWLINE +
CurrentConfig.v2CorePath + NEWLINE + v2ctlPath + NEWLINE + NEWLINE +
tr("to this path: ") + NEWLINE + newPath);
return;
}
} else {
LOG(MODULE_VCORE, "Skipped removing files since the current v2ray core is in the default path.")
LOG(MODULE_VCORE, "Skipped removing files since the current V2ray core is in the default path.")
LOG(MODULE_VCORE, " --> Actually because we don't know where else to obtain the files.")
}
@ -429,7 +429,7 @@ void PreferencesWindow::on_tProxyCheckBox_stateChanged(int arg1)
if (ret != 0) {
LOG(MODULE_UI, "WARN: setcap exits with code: " + QSTRN(ret))
QvMessageBox(this, tr("Preferences"), tr("Failed to setcap onto v2ray executable. You may need to run `setcap` manually."));
QvMessageBoxWarn(this, tr("Preferences"), tr("Failed to setcap onto V2ray executable. You may need to run `setcap` manually."));
}
CurrentConfig.tProxySupport = true;
@ -440,7 +440,7 @@ void PreferencesWindow::on_tProxyCheckBox_stateChanged(int arg1)
if (ret != 0) {
LOG(MODULE_UI, "WARN: setcap exits with code: " + QSTRN(ret))
QvMessageBox(this, tr("Preferences"), tr("Failed to setcap onto v2ray executable. You may need to run `setcap` manually."));
QvMessageBoxWarn(this, tr("Preferences"), tr("Failed to setcap onto V2ray executable. You may need to run `setcap` manually."));
}
CurrentConfig.tProxySupport = false;
@ -451,7 +451,7 @@ void PreferencesWindow::on_tProxyCheckBox_stateChanged(int arg1)
#else
Q_UNUSED(arg1)
// No such tProxy thing on Windows and macOS
QvMessageBox(this, tr("Preferences"), tr("tProxy is not supported on macOS and Windows"));
QvMessageBoxWarn(this, tr("Preferences"), tr("tProxy is not supported on macOS and Windows"));
CurrentConfig.tProxySupport = false;
tProxyCheckBox->setChecked(false);
#endif
@ -532,7 +532,7 @@ void PreferencesWindow::on_nsBarPageDelBTN_clicked()
nsBarLineAddBTN->setEnabled(false);
nsBarLineDelBTN->setEnabled(false);
nsBarLinesList->setEnabled(false);
nsBarVerticalLayout->setEnabled(false);
networkToolbarSettingsFrame->setEnabled(false);
nsBarPageYOffset->setEnabled(false);
nsBarLinesList->clear();
}
@ -566,7 +566,7 @@ void PreferencesWindow::on_nsBarLineDelBTN_clicked()
CurrentBarLineId = 0;
if (nsBarLinesList->count() <= 0) {
nsBarVerticalLayout->setEnabled(false);
networkToolbarSettingsFrame->setEnabled(false);
nsBarLineDelBTN->setEnabled(false);
}
@ -595,7 +595,7 @@ void PreferencesWindow::on_nsBarPagesList_currentRowChanged(int currentRow)
nsBarLinesList->setCurrentRow(0);
ShowLineParameters(CurrentBarLine);
} else {
nsBarVerticalLayout->setEnabled(false);
networkToolbarSettingsFrame->setEnabled(false);
}
}
@ -707,7 +707,7 @@ void PreferencesWindow::ShowLineParameters(QvBarLine &barLine)
nsBarContentCombo->setCurrentText(NetSpeedPluginMessages[barLine.ContentType]);
nsBarTagTxt->setText(barLine.Message);
finishedLoading = true;
nsBarVerticalLayout->setEnabled(true);
networkToolbarSettingsFrame->setEnabled(true);
}
void PreferencesWindow::on_chooseColorBtn_clicked()
@ -754,7 +754,7 @@ void PreferencesWindow::on_darkThemeCB_stateChanged(int arg1)
{
LOADINGCHECK
CurrentConfig.uiConfig.useDarkTheme = arg1 == Qt::Checked;
QvMessageBox(this, tr("Dark Mode"), tr("Please restart Qv2ray to fully apply this feature."));
QvMessageBoxWarn(this, tr("Dark Mode"), tr("Please restart Qv2ray to fully apply this feature."));
#ifdef QV2RAY_USE_BUILTIN_DARKTHEME
themeCombo->setEnabled(arg1 != Qt::Checked);
@ -772,15 +772,6 @@ void PreferencesWindow::on_darkTrayCB_stateChanged(int arg1)
CurrentConfig.uiConfig.useDarkTrayIcon = arg1 == Qt::Checked;
}
void PreferencesWindow::on_enablePACCB_stateChanged(int arg1)
{
LOADINGCHECK
NEEDRESTART
bool enabled = arg1 == Qt::Checked;
CurrentConfig.inboundConfig.pacConfig.enablePAC = enabled;
pacGroupBox->setEnabled(enabled);
}
void PreferencesWindow::on_pacGoBtn_clicked()
{
LOADINGCHECK
@ -831,7 +822,7 @@ void PreferencesWindow::on_pacGoBtn_clicked()
}
LOG(MODULE_NETWORK, "Fetched: " + gfwLocation)
QvMessageBox(this, tr("Download GFWList"), tr("Successfully downloaded GFWList."));
QvMessageBoxWarn(this, tr("Download GFWList"), tr("Successfully downloaded GFWList."));
pacGoBtn->setEnabled(true);
gfwListCB->setEnabled(true);
@ -848,7 +839,7 @@ void PreferencesWindow::on_pacPortSB_valueChanged(int arg1)
LOADINGCHECK
NEEDRESTART
CurrentConfig.inboundConfig.pacConfig.port = arg1;
//pacAccessPathTxt->setText("http://" + listenIPTxt->text() + ":" + QSTRN(arg1) + "/pac");
pacListenAddrLabel->setText("http://" + (pacProxyTxt->text().isEmpty() ? "127.0.0.1" : pacProxyTxt->text()) + ":" + QSTRN(pacPortSB->value()) + "/pac");
}
void PreferencesWindow::on_setSysProxyCB_stateChanged(int arg1)
@ -907,7 +898,7 @@ void PreferencesWindow::on_installBootStart_clicked()
// If failed to set the status.
if (!GetLaunchAtLoginStatus()) {
QvMessageBox(this, tr("Start with boot"), tr("Failed to set auto start option."));
QvMessageBoxWarn(this, tr("Start with boot"), tr("Failed to set auto start option."));
}
SetAutoStartButtonsState(GetLaunchAtLoginStatus());
@ -919,7 +910,7 @@ void PreferencesWindow::on_removeBootStart_clicked()
// If that setting still present.
if (GetLaunchAtLoginStatus()) {
QvMessageBox(this, tr("Start with boot"), tr("Failed to set auto start option."));
QvMessageBoxWarn(this, tr("Start with boot"), tr("Failed to set auto start option."));
}
SetAutoStartButtonsState(GetLaunchAtLoginStatus());
@ -931,25 +922,21 @@ void PreferencesWindow::SetAutoStartButtonsState(bool isAutoStart)
removeBootStart->setEnabled(isAutoStart);
}
void PreferencesWindow::on_fpEnabledCB_stateChanged(int arg1)
{
bool fpEnabled = arg1 == Qt::Checked;
CurrentConfig.connectionConfig.forwardProxyConfig.enableForwardProxy = fpEnabled;
fpFrame->setEnabled(fpEnabled);
}
void PreferencesWindow::on_fpTypeCombo_currentIndexChanged(const QString &arg1)
{
LOADINGCHECK
CurrentConfig.connectionConfig.forwardProxyConfig.type = arg1;
}
void PreferencesWindow::on_fpAddressTx_textEdited(const QString &arg1)
{
LOADINGCHECK
CurrentConfig.connectionConfig.forwardProxyConfig.serverAddress = arg1;
}
void PreferencesWindow::on_spPortSB_valueChanged(int arg1)
{
LOADINGCHECK
CurrentConfig.connectionConfig.forwardProxyConfig.port = arg1;
}
@ -963,15 +950,73 @@ void PreferencesWindow::on_fpUseAuthCB_stateChanged(int arg1)
void PreferencesWindow::on_fpUsernameTx_textEdited(const QString &arg1)
{
LOADINGCHECK
CurrentConfig.connectionConfig.forwardProxyConfig.username = arg1;
}
void PreferencesWindow::on_fpPasswordTx_textEdited(const QString &arg1)
{
LOADINGCHECK
CurrentConfig.connectionConfig.forwardProxyConfig.password = arg1;
}
void PreferencesWindow::on_fpPortSB_valueChanged(int arg1)
{
LOADINGCHECK
CurrentConfig.connectionConfig.forwardProxyConfig.port = arg1;
}
void PreferencesWindow::on_pacProxyTxt_textChanged(const QString &arg1)
{
Q_UNUSED(arg1)
pacListenAddrLabel->setText("http://" + (pacProxyTxt->text().isEmpty() ? "127.0.0.1" : pacProxyTxt->text()) + ":" + QSTRN(pacPortSB->value()) + "/pac");
}
void PreferencesWindow::on_checkVCoreSettings_clicked()
{
auto vcorePath = vCorePathTxt->text();
auto vAssetsPath = vCoreAssetsPathTxt->text();
QString result;
if (!V2rayKernelInstance::ValidateKernel(vcorePath, vAssetsPath, &result)) {
QvMessageBoxWarn(this, tr("V2ray Core Settings"), result);
} else {
QvMessageBoxInfo(this, tr("V2ray Core Settings"), tr("V2ray path configuration check passed.") + NEWLINE + NEWLINE +
tr("Current version of V2ray is: ") + NEWLINE + result);
}
}
void PreferencesWindow::on_httpGroupBox_clicked(bool checked)
{
LOADINGCHECK
NEEDRESTART
CurrentConfig.inboundConfig.useHTTP = checked;
}
void PreferencesWindow::on_socksGroupBox_clicked(bool checked)
{
LOADINGCHECK
NEEDRESTART
CurrentConfig.inboundConfig.useSocks = checked;
}
void PreferencesWindow::on_pacGroupBox_clicked(bool checked)
{
LOADINGCHECK
NEEDRESTART
CurrentConfig.inboundConfig.pacConfig.enablePAC = checked;
}
void PreferencesWindow::on_fpGroupBox_clicked(bool checked)
{
LOADINGCHECK
NEEDRESTART
CurrentConfig.connectionConfig.forwardProxyConfig.enableForwardProxy = checked;
}
void PreferencesWindow::on_maxLogLinesSB_valueChanged(int arg1)
{
LOADINGCHECK
NEEDRESTART
CurrentConfig.uiConfig.maximumLogLines = arg1;
}

View File

@ -18,10 +18,6 @@ class PreferencesWindow : public QDialog, private Ui::PreferencesWindow
private slots:
void on_buttonBox_accepted();
void on_httpCB_stateChanged(int arg1);
void on_socksCB_stateChanged(int arg1);
void on_httpAuthCB_stateChanged(int arg1);
void on_socksAuthCB_stateChanged(int arg1);
@ -116,8 +112,6 @@ class PreferencesWindow : public QDialog, private Ui::PreferencesWindow
void on_darkTrayCB_stateChanged(int arg1);
void on_enablePACCB_stateChanged(int arg1);
void on_pacGoBtn_clicked();
void on_pacPortSB_valueChanged(int arg1);
@ -138,8 +132,6 @@ class PreferencesWindow : public QDialog, private Ui::PreferencesWindow
void on_removeBootStart_clicked();
void on_fpEnabledCB_stateChanged(int arg1);
void on_fpTypeCombo_currentIndexChanged(const QString &arg1);
void on_fpAddressTx_textEdited(const QString &arg1);
@ -154,6 +146,20 @@ class PreferencesWindow : public QDialog, private Ui::PreferencesWindow
void on_fpPortSB_valueChanged(int arg1);
void on_pacProxyTxt_textChanged(const QString &arg1);
void on_checkVCoreSettings_clicked();
void on_httpGroupBox_clicked(bool checked);
void on_socksGroupBox_clicked(bool checked);
void on_pacGroupBox_clicked(bool checked);
void on_fpGroupBox_clicked(bool checked);
void on_maxLogLinesSB_valueChanged(int arg1);
private:
void SetAutoStartButtonsState(bool isAutoStart);
// Set ui parameters for a line;

File diff suppressed because it is too large Load Diff

View File

@ -267,7 +267,7 @@ CONFIGROOT RouteEditor::OpenEditor()
ruleJsonObject.remove("outboundTag");
// Find balancer list
if (!_balancers.contains(_rule.balancerTag)) {
if (!balancers.contains(_rule.balancerTag)) {
LOG(MODULE_UI, "Cannot find a balancer for tag: " + _rule.balancerTag)
} else {
auto _balancerList = balancers[_rule.balancerTag];
@ -373,19 +373,19 @@ void RouteEditor::ShowCurrentRuleDetail()
routePortTxt->setText(CurrentRule.port);
//
// Users
QString users = Stringify(CurrentRule.user, NEWLINE);
QString users = CurrentRule.user.join(NEWLINE);
routeUserTxt->setPlainText(users);
//
// Incoming Sources
QString sources = Stringify(CurrentRule.source, NEWLINE);
QString sources = CurrentRule.source.join(NEWLINE);
sourceIPList->setPlainText(sources);
//
// Domains
QString domains = Stringify(CurrentRule.domain, NEWLINE);
QString domains = CurrentRule.domain.join(NEWLINE);
hostList->setPlainText(domains);
//
// Outcoming IPs
QString ips = Stringify(CurrentRule.ip, NEWLINE);
QString ips = CurrentRule.ip.join(NEWLINE);
ipList->setPlainText(ips);
LOAD_FLAG_END
}
@ -410,7 +410,7 @@ void RouteEditor::on_routeProtocolHTTPCB_stateChanged(int arg1)
if (routeProtocolBTCB->isChecked()) protocols.push_back("bittorrent");
CurrentRule.protocol = protocols;
statusLabel->setText(tr("Protocol list changed: ") + Stringify(protocols));
statusLabel->setText(tr("Protocol list changed: ") + protocols.join(";"));
}
void RouteEditor::on_routeProtocolTLSCB_stateChanged(int arg1)
{
@ -424,7 +424,7 @@ void RouteEditor::on_routeProtocolTLSCB_stateChanged(int arg1)
if (routeProtocolBTCB->isChecked()) protocols.push_back("bittorrent");
CurrentRule.protocol = protocols;
statusLabel->setText(tr("Protocol list changed: ") + Stringify(protocols));
statusLabel->setText(tr("Protocol list changed: ") + protocols.join(";"));
}
void RouteEditor::on_routeProtocolBTCB_stateChanged(int arg1)
{
@ -438,7 +438,7 @@ void RouteEditor::on_routeProtocolBTCB_stateChanged(int arg1)
if (routeProtocolTLSCB->isChecked()) protocols.push_back("tls");
CurrentRule.protocol = protocols;
statusLabel->setText(tr("Protocol list changed: ") + Stringify(protocols));
statusLabel->setText(tr("Protocol list changed: ") + protocols.join(";"));
}
void RouteEditor::on_balancerAddBtn_clicked()
{
@ -551,7 +551,7 @@ void RouteEditor::on_enableBalancerCB_stateChanged(int arg1)
}
}
} else {
QvMessageBox(this, tr("Route Editor"), tr("To make this rule ready to use, you need to connect it to an outbound node."));
QvMessageBoxWarn(this, tr("Route Editor"), tr("To make this rule ready to use, you need to connect it to an outbound node."));
}
}
void RouteEditor::on_addDefaultBtn_clicked()
@ -628,7 +628,7 @@ void RouteEditor::on_ruleEnableCB_stateChanged(int arg1)
void RouteEditor::on_delBtn_clicked()
{
if (nodeScene->selectedNodes().empty()) {
QvMessageBox(this, tr("Remove Items"), tr("Please select a node from the graph to continue."));
QvMessageBoxWarn(this, tr("Remove Items"), tr("Please select a node from the graph to continue."));
}
auto firstNode = nodeScene->selectedNodes()[0];
@ -677,7 +677,7 @@ void RouteEditor::on_delBtn_clicked()
void RouteEditor::on_editBtn_clicked()
{
if (nodeScene->selectedNodes().empty()) {
QvMessageBox(this, tr("Edit Inbound/Outbound"), tr("Please select a node from the graph to continue."));
QvMessageBoxWarn(this, tr("Edit Inbound/Outbound"), tr("Please select a node from the graph to continue."));
}
auto firstNode = nodeScene->selectedNodes()[0];
@ -692,8 +692,8 @@ void RouteEditor::on_editBtn_clicked()
int _code;
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."));
QvMessageBoxWarn(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."));
statusLabel->setText(tr("Opening JSON editor"));
JsonEditor *w = new JsonEditor(_in, this);
_result = INBOUND(w->OpenEditor());
@ -726,9 +726,9 @@ void RouteEditor::on_editBtn_clicked()
int _code;
if (protocol != "vmess" && protocol != "shadowsocks" && protocol != "socks") {
QvMessageBox(this, tr("Unsupported Outbound Type"),
tr("This outbound entry is not supported by the GUI editor.") + NEWLINE +
tr("We will launch Json Editor instead."));
QvMessageBoxWarn(this, tr("Unsupported Outbound Type"),
tr("This outbound entry is not supported by the GUI editor.") + NEWLINE +
tr("We will launch Json Editor instead."));
JsonEditor w(_out, this);
statusLabel->setText(tr("Opening JSON editor"));
_result = OUTBOUND(w.OpenEditor());
@ -769,5 +769,10 @@ void RouteEditor::on_defaultOutboundCombo_currentIndexChanged(const QString &arg
void RouteEditor::on_ruleTagLineEdit_textEdited(const QString &arg1)
{
if (arg1.isEmpty()) {
ruleTagLineEdit->setText(CurrentRule.QV2RAY_RULE_TAG);
return;
}
RenameItemTag(RENAME_RULE, CurrentRule.QV2RAY_RULE_TAG, arg1);
}

View File

@ -262,7 +262,7 @@
<item row="1" column="1">
<widget class="QToolBox" name="toolBox">
<property name="currentIndex">
<number>0</number>
<number>1</number>
</property>
<widget class="QWidget" name="page_2">
<property name="geometry">
@ -270,7 +270,7 @@
<x>0</x>
<y>0</y>
<width>359</width>
<height>535</height>
<height>511</height>
</rect>
</property>
<attribute name="label">
@ -363,9 +363,9 @@
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<y>-140</y>
<width>345</width>
<height>607</height>
<height>651</height>
</rect>
</property>
<attribute name="label">
@ -648,7 +648,19 @@
</layout>
</widget>
<tabstops>
<tabstop>addInboundBtn</tabstop>
<tabstop>addDefaultBtn</tabstop>
<tabstop>addOutboundBtn</tabstop>
<tabstop>insertBlackBtn</tabstop>
<tabstop>insertDirectBtn</tabstop>
<tabstop>editBtn</tabstop>
<tabstop>addRouteBtn</tabstop>
<tabstop>delBtn</tabstop>
<tabstop>domainStrategyCombo</tabstop>
<tabstop>defaultOutboundCombo</tabstop>
<tabstop>ruleListWidget</tabstop>
<tabstop>ruleEnableCB</tabstop>
<tabstop>ruleTagLineEdit</tabstop>
<tabstop>netTCPRB</tabstop>
<tabstop>netUDPRB</tabstop>
<tabstop>netBothRB</tabstop>
@ -658,9 +670,13 @@
<tabstop>routePortTxt</tabstop>
<tabstop>enableBalancerCB</tabstop>
<tabstop>balancerSelectionCombo</tabstop>
<tabstop>balancerList</tabstop>
<tabstop>balancerDelBtn</tabstop>
<tabstop>balancerAddBtn</tabstop>
<tabstop>balancerDelBtn</tabstop>
<tabstop>balancerList</tabstop>
<tabstop>routeUserTxt</tabstop>
<tabstop>sourceIPList</tabstop>
<tabstop>hostList</tabstop>
<tabstop>ipList</tabstop>
</tabstops>
<resources/>
<connections>

View File

@ -8,7 +8,12 @@
void RouteEditor::AddNewInbound(INBOUND in)
{
QString tag = in["tag"].toString();
QString tag = getTag(in);
if (inbounds.contains(tag)) {
tag = tag + "_" + GenerateRandomString(5);
}
auto _nodeData = make_unique<QvInboundNodeModel>(make_shared<InboundNodeData>(tag));
auto &node = nodeScene->createNode(std::move(_nodeData));
auto pos = QPointF();
@ -22,6 +27,11 @@ void RouteEditor::AddNewInbound(INBOUND in)
void RouteEditor::AddNewOutbound(OUTBOUND out)
{
QString tag = getTag(out);
if (outbounds.contains(tag)) {
tag = tag + "_" + GenerateRandomString(5);
}
auto _nodeData = make_unique<QvOutboundNodeModel>(make_shared<OutboundNodeData>(tag));
auto pos = nodeGraphWidget->pos();
pos.setX(pos.x() + 850 + GRAPH_GLOBAL_OFFSET_X);
@ -68,15 +78,16 @@ void RouteEditor::RenameItemTag(ROUTE_EDIT_MODE mode, const QString &originalTag
case RENAME_RULE:
if (rules.contains(originalTag) && ruleNodes.contains(originalTag)) {
if (rules.contains(newTag) && rules.contains(newTag)) {
QvMessageBox(this, tr("Rename tags"), tr("The new tag has been used, please suggest another."));
QvMessageBoxWarn(this, tr("Rename tags"), tr("The new tag has been used, please suggest another."));
return;
}
//
auto node = static_cast<QvRuleNodeDataModel *>(ruleNodes[originalTag]->nodeDataModel());
node->setData(newTag);
//
rules[newTag] = rules.take(originalTag);
ruleNodes[newTag] = ruleNodes.take(originalTag);
//
auto node = static_cast<QvRuleNodeDataModel *>(ruleNodes[newTag]->nodeDataModel());
node->setData(newTag);
// No other operation needed, but need to rename the one in the ruleOrder list widget.
auto items = ruleListWidget->findItems(originalTag, Qt::MatchExactly);
@ -103,7 +114,7 @@ void RouteEditor::RenameItemTag(ROUTE_EDIT_MODE mode, const QString &originalTag
case RENAME_OUTBOUND:
if (outbounds.contains(originalTag) && outboundNodes.contains(originalTag)) {
if (outbounds.contains(newTag) && outboundNodes.contains(newTag)) {
QvMessageBox(this, tr("Rename tags"), tr("The new tag has been used, please suggest another."));
QvMessageBoxWarn(this, tr("Rename tags"), tr("The new tag has been used, please suggest another."));
return;
}
@ -131,7 +142,7 @@ void RouteEditor::RenameItemTag(ROUTE_EDIT_MODE mode, const QString &originalTag
case RENAME_INBOUND:
if (inbounds.contains(originalTag) && inboundNodes.contains(originalTag)) {
if (inbounds.contains(newTag) && inboundNodes.contains(newTag)) {
QvMessageBox(this, tr("Rename tags"), tr("The new tag has been used, please suggest another."));
QvMessageBoxWarn(this, tr("Rename tags"), tr("The new tag has been used, please suggest another."));
return;
}

View File

@ -17,6 +17,11 @@ SubscribeEditor::SubscribeEditor(QWidget *parent) :
LoadSubscriptionList(subscriptions);
}
QPair<QString, CONFIGROOT> SubscribeEditor::GetSelectedConfig()
{
return currentSelectedConfig;
}
void SubscribeEditor::LoadSubscriptionList(QMap<QString, Qv2raySubscriptionConfig> list)
{
subscriptionList->clear();
@ -56,12 +61,12 @@ void SubscribeEditor::on_updateButton_clicked()
bool canGo = true;
if (newName.isEmpty() || !IsValidFileName(newName)) {
QvMessageBox(this, tr("Renaming a subscription"), tr("The subscription name is invalid, please try another."));
QvMessageBoxWarn(this, tr("Renaming a subscription"), tr("The subscription name is invalid, please try another."));
canGo = false;
}
if (subscriptionList->findItems(newName, Qt::MatchExactly).count() > 0) {
QvMessageBox(this, tr("Renaming a subscription"), tr("New name of this subscription has been used already, please suggest another one"));
QvMessageBoxWarn(this, tr("Renaming a subscription"), tr("New name of this subscription has been used already, please suggest another one"));
canGo = false;
}
@ -73,7 +78,7 @@ void SubscribeEditor::on_updateButton_clicked()
bool result = RenameSubscription(currentSubName, newName);
if (!result) {
QvMessageBox(this, tr("Renaming a subscription"), tr("Failed to rename a subscription, this is an unknown error."));
QvMessageBoxWarn(this, tr("Renaming a subscription"), tr("Failed to rename a subscription, this is an unknown error."));
return;
}
@ -92,7 +97,7 @@ void SubscribeEditor::on_updateButton_clicked()
SetGlobalConfig(conf);
// This will set the name to the new name.
LoadSubscriptionList(subscriptions);
QvMessageBox(this, tr("Renaming a subscription"), tr("Successfully renamed a subscription"));
QvMessageBoxInfo(this, tr("Renaming a subscription"), tr("Successfully renamed a subscription"));
}
subscriptions[currentSubName].updateInterval = newUpdateInterval;
@ -130,15 +135,19 @@ void SubscribeEditor::StartUpdateSubscription(const QString &subscriptionName)
LOG(MODULE_SUBSCRIPTION, "Processing a subscription with following error: " + errMessage)
} else {
connectionsList->addItem(_alias);
SaveSubscriptionConfig(config, subscriptionName, _alias);
if (!SaveSubscriptionConfig(config, subscriptionName, _alias)) {
// Cannot save.
}
}
}
subscriptions[subscriptionName].lastUpdated = system_clock::to_time_t(system_clock::now());
lastUpdatedLabel->setText(timeToString(subscriptions[subscriptionName].lastUpdated));
isUpdateInProgress = false;
} else {
LOG(MODULE_NETWORK, "We have received an empty string from the URL.")
QvMessageBox(this, tr("Updating subscriptions"), tr("Failed to process the result from the upstream, please check your Url"));
QvMessageBoxWarn(this, tr("Updating subscriptions"), tr("Failed to process the result from the upstream, please check your Url."));
}
this->setEnabled(true);
@ -161,8 +170,7 @@ void SubscribeEditor::on_removeSubsButton_clicked()
auto conf = GetGlobalConfig();
if (conf.autoStartConfig.subscriptionName == name) {
conf.autoStartConfig.subscriptionName.clear();
conf.autoStartConfig.connectionName.clear();
conf.autoStartConfig = QvConfigIdentifier();
SetGlobalConfig(conf);
}
@ -186,7 +194,7 @@ void SubscribeEditor::on_subscriptionList_currentRowChanged(int currentRow)
subNameTxt->setText(currentSubName);
subAddrTxt->setText(subscriptions[currentSubName].address);
updateIntervalSB->setValue(subscriptions[currentSubName].updateInterval);
lastUpdatedLabel->setText(QString::fromStdString(timeToString(subscriptions[currentSubName].lastUpdated)));
lastUpdatedLabel->setText(timeToString(subscriptions[currentSubName].lastUpdated));
//
connectionsList->clear();
auto _list = GetSubscriptionConnection(currentSubName);
@ -225,3 +233,12 @@ void SubscribeEditor::on_updateIntervalSB_valueChanged(double arg1)
{
subscriptions[currentSubName].updateInterval = arg1;
}
void SubscribeEditor::on_connectionsList_itemClicked(QListWidgetItem *item)
{
if (item != nullptr) {
auto name = item->text();
currentSelectedConfig.first = name;
currentSelectedConfig.second = GetSubscriptionConnection(currentSubName)[name];
}
}

View File

@ -13,6 +13,7 @@ class SubscribeEditor : public QDialog, private Ui::w_SubscribeEditor
public:
explicit SubscribeEditor(QWidget *parent = nullptr);
~SubscribeEditor();
QPair<QString, CONFIGROOT> GetSelectedConfig();
private slots:
void on_addSubsButton_clicked();
@ -29,6 +30,8 @@ class SubscribeEditor : public QDialog, private Ui::w_SubscribeEditor
void on_updateIntervalSB_valueChanged(double arg1);
void on_connectionsList_itemClicked(QListWidgetItem *item);
private:
void StartUpdateSubscription(const QString &subscriptionName);
void SaveConfig();
@ -36,6 +39,7 @@ class SubscribeEditor : public QDialog, private Ui::w_SubscribeEditor
bool isUpdateInProgress = false;
QvHttpRequestHelper helper;
QPair<QString, CONFIGROOT> currentSelectedConfig;
QMap<QString, Qv2raySubscriptionConfig> subscriptions;
QString currentSubName;
};

View File

@ -5,10 +5,15 @@
// Forwarded from QvTinyLog
static QQueue<QString> __loggerBuffer;
void _LOG(const std::string &func, const QString &module, const QString &log)
void __QV2RAY_LOG_FUNC__(int type, const std::string &func, int line, const QString &module, const QString &log)
{
auto logString = "[" + module + "]: " + log;
cout << func << logString.toStdString() << endl;
if (StartupOption.debugLog || (isDebug && type == QV2RAY_LOG_DEBUG)) {
logString.prepend(QString::fromStdString(func + ":" + to_string(line) + " "));
}
cout << logString.toStdString() << endl;
__loggerBuffer.enqueue(logString + NEWLINE);
}
@ -17,7 +22,11 @@ const QString readLastLog()
QString result;
while (!__loggerBuffer.isEmpty()) {
result += __loggerBuffer.dequeue();
auto str = __loggerBuffer.dequeue();
if (!str.trimmed().isEmpty()) {
result += str;
}
}
return result;
@ -42,36 +51,6 @@ namespace Qv2ray
return randomString;
}
QString Stringify(list<string> list, QString saperator)
{
QString out;
for (auto item : list) {
out.append(QString::fromStdString(item));
out.append(saperator);
}
if (out.length() >= 1)
out = out.remove(out.length() - 1, 1);
return out;
}
QString Stringify(QList<QString> list, QString saperator)
{
QString out;
for (auto item : list) {
out.append(item);
out.append(saperator);
}
if (out.length() >= 1)
out = out.remove(out.length() - 1, 1);
return out;
}
QString StringFromFile(QFile *source)
{
source->open(QFile::ReadOnly);
@ -170,14 +149,20 @@ namespace Qv2ray
return GetFileList(dir).contains(fileName);
}
void QvMessageBox(QWidget *parent, QString title, QString text)
void QvMessageBoxWarn(QWidget *parent, QString title, QString text)
{
QMessageBox::warning(parent, title, text, QMessageBox::Ok | QMessageBox::Default, 0);
}
void QvMessageBoxInfo(QWidget *parent, QString title, QString text)
{
QMessageBox::information(parent, title, text, QMessageBox::Ok | QMessageBox::Default, 0);
}
int QvMessageBoxAsk(QWidget *parent, QString title, QString text, QMessageBox::StandardButton extraButtons)
{
return QMessageBox::information(parent, title, text, QMessageBox::Yes | QMessageBox::No | extraButtons);
return QMessageBox::question(parent, title, text, QMessageBox::Yes | QMessageBox::No | extraButtons);
}
QString FormatBytes(long long bytes)

View File

@ -5,6 +5,8 @@
#include <QMessageBox>
#include <QUuid>
#define REGEX_IPV6_ADDR "\\[\\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:)))(%.+)?\\s*\\]"
namespace Qv2ray
{
namespace Utils
@ -17,16 +19,19 @@ namespace Qv2ray
list<string> SplitLines_std(const QString &_string);
bool FileExistsIn(QDir dir, QString fileName);
const QString GenerateRandomString(int len = 12);
void QvMessageBox(QWidget *parent, QString title, QString text);
//
void QvMessageBoxWarn(QWidget *parent, QString title, QString text);
void QvMessageBoxInfo(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);
QJsonObject JsonFromString(QString string);
QString JsonToString(QJsonObject json, QJsonDocument::JsonFormat format = QJsonDocument::JsonFormat::Indented);
QString JsonToString(QJsonArray array, QJsonDocument::JsonFormat format = QJsonDocument::JsonFormat::Indented);
QString VerifyJsonString(const QString &source);
QString Stringify(list<string> list, QString saperator = ";");
QString Stringify(QList<QString> list, QString saperator = ";");
//QString Stringify(list<string> list, QString saperator = ";");
//QString Stringify(QList<QString> list, QString saperator = ";");
QString FormatBytes(long long bytes);
void DeducePossibleFileName(const QString &baseDir, QString *fileName, const QString &extension);
QString ConvertGFWToPAC(const QString &rawContent, const QString &customProxyString);
@ -69,6 +74,11 @@ namespace Qv2ray
return QRegExp(R"([\/\\\"?%*:|><]|(^\.{1,2}$))").indexIn(str) == -1;
}
inline bool IsIPv6Address(const QString &addr)
{
return QRegularExpression(REGEX_IPV6_ADDR).match(addr).hasMatch();
}
// These functions a sugers to prevent warnings from deprecated Qt 5.14 functions.
template<typename TYPE>
std::list<TYPE> toStdList(QList<TYPE> list)
@ -142,13 +152,13 @@ namespace Qv2ray
return it != listOfElements.end();
}
inline std::string timeToString(const time_t &t)
inline QString timeToString(const time_t &t)
{
auto _tm = std::localtime(&t);
char MY_TIME[128];
// using strftime to display time
strftime(MY_TIME, sizeof(MY_TIME), "%x - %I:%M%p", _tm);
return MY_TIME;
return QString(MY_TIME);
}
}

View File

@ -10,22 +10,16 @@ using namespace std;
* Tiny log module.
*/
void _LOG(const std::string &func, const QString &module, const QString &log);
void __QV2RAY_LOG_FUNC__(int type, const std::string &func, int line, const QString &module, const QString &log);
const QString readLastLog();
#ifdef QV2RAY_LOG_WITH_FUNCTION_NAME
# define _QV2RAY_LOG_FUNCSTR __PRETTY_FUNCTION__
#else
# define _QV2RAY_LOG_FUNCSTR ""
#endif
#define QV2RAY_LOG_NORMAL 0
#define QV2RAY_LOG_DEBUG 1
#define LOG(module, msg) _LOG(_QV2RAY_LOG_FUNCSTR, module, msg);
#define __LOG_IMPL(LEVEL, MODULE, MSG) __QV2RAY_LOG_FUNC__(LEVEL, __PRETTY_FUNCTION__, __LINE__, MODULE, MSG);
#ifdef QT_DEBUG
#define DEBUG(module, msg) _LOG(__PRETTY_FUNCTION__, module, msg);
#else
#define DEBUG(module, msg)
#endif
#define LOG(MODULE, MSG) __LOG_IMPL(QV2RAY_LOG_NORMAL, (MODULE), (MSG));
#define DEBUG(MODULE, MSG) __LOG_IMPL(QV2RAY_LOG_DEBUG, (MODULE), (MSG));
// Log modules used by Qv2ray
#define MODULE_INIT "INIT"

File diff suppressed because it is too large Load Diff