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: | run: |
mkdir build mkdir build
cd build cd build
qmake .. CONFIG+=no_increase_build_number qmake .. CONFIG+=no_increase_build_number PREFIX=/usr
- name: Building Qv2ray - name: Building Qv2ray
run: | run: |
cd build cd build
@ -124,8 +124,6 @@ jobs:
make install INSTALL_ROOT=AppDir make install INSTALL_ROOT=AppDir
cd AppDir cd AppDir
mkdir -p ./usr/lib/ 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/ 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 - name: Building AppImage using linuxdeployqt
run: | run: |

View File

@ -131,7 +131,7 @@ jobs:
run: | run: |
mkdir build mkdir build
cd build cd build
qmake .. CONFIG+=no_increase_build_number qmake .. CONFIG+=no_increase_build_number PREFIX=/usr
make -j2 make -j2
- name: Generating filesystem structure for AppImage - name: Generating filesystem structure for AppImage
run: | run: |
@ -139,8 +139,6 @@ jobs:
make install INSTALL_ROOT=AppDir make install INSTALL_ROOT=AppDir
cd AppDir cd AppDir
mkdir -p ./usr/lib/ 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/ 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 - name: Building AppImage using linuxdeployqt
run: | run: |

View File

@ -1 +1 @@
2781 2960

View File

@ -20,6 +20,10 @@ no_increase_build_number {
write_file("Build.Counter", _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 DEFINES += QT_DEPRECATED_WARNINGS QV2RAY_VERSION_STRING=\"\\\"v$${VERSION}\\\"\" QAPPLICATION_CLASS=QApplication
# Don't merge those configs with below. # Don't merge those configs with below.
@ -156,7 +160,7 @@ ICON = ./assets/icons/qv2ray.icns
message(" --> Qv2ray is not properly configured yet: ") message(" --> Qv2ray is not properly configured yet: ")
message(" gRPC and protobuf headers for v2ray API is missing.") 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(" --> 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("-----------------------------------------------")
message(" ") message(" ")
warning("IF YOU THINK IT'S A MISTAKE, PLEASE OPEN AN ISSUE") warning("IF YOU THINK IT'S A MISTAKE, PLEASE OPEN AN ISSUE")
@ -235,7 +239,7 @@ macx {
# For Linux and macOS # For Linux and macOS
message("Configuring for macOS specific environment") message("Configuring for macOS specific environment")
LIBS += -framework Carbon -framework Cocoa LIBS += -framework Carbon -framework Cocoa
LIBS += -lgpr LIBS += -lgpr -lupb
} }
# Reuse unix for macx as well # Reuse unix for macx as well
@ -255,27 +259,21 @@ unix {
message(" --> Generating desktop dependency.") message(" --> Generating desktop dependency.")
desktop.files += ./assets/qv2ray.desktop desktop.files += ./assets/qv2ray.desktop
desktop.path = /usr/share/applications/ desktop.path = $$PREFIX/share/applications/
message(" --> Generating icons dependency.") message(" --> Generating icons dependency.")
icon.files += ./assets/icons/qv2ray.png 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 INSTALLS += target desktop icon
} }
build_flatpak { with_metainfo {
# 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/
INSTALLS += appdataXml 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 任意分支或发行版即代表您同意本项目作者不承担任何由于您违反以上准则所带来的任何法律责任。* *任何尝试下载或下载 Qv2ray 任意分支或发行版即代表您同意本项目作者不承担任何由于您违反以上准则所带来的任何法律责任。*

View File

@ -1,8 +1,26 @@
[Desktop Entry] [Desktop Entry]
Version=1.0
Name=Qv2ray
Type=Application 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; Categories=Network;Qt;
Terminal=false
Icon=qv2ray Icon=qv2ray
Exec=qv2ray Exec=qv2ray
Name=Qv2ray Actions=noAPI;withToolbarPlugin;
Comment=Cross platform v2ray Qt GUI Client.
[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"?> <?xml version="1.0" encoding="UTF-8"?>
<component type="desktop-application"> <component type="desktop-application">
<id>com.github.Qv2ray</id> <id>com.github.lhy0403.Qv2ray</id>
<metadata_license>CC0-1.0</metadata_license> <metadata_license>CC0-1.0</metadata_license>
<project_license>GPL-3.0</project_license> <project_license>GPL-3.0</project_license>
<name>Qv2ray</name> <name>Qv2ray</name>
@ -13,7 +13,7 @@
</p> </p>
</description> </description>
<launchable type="desktop-id">com.github.Qv2ray.desktop</launchable> <launchable type="desktop-id">com.github.lhy0403.Qv2ray.desktop</launchable>
<screenshots> <screenshots>
<screenshot type="default"> <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 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: override_dh_auto_configure:
./tools/grpc_gen.sh ./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
#endif #endif
extern bool isDebug;
// Base folder suffix. // Base folder suffix.
#ifdef QT_DEBUG #ifdef QT_DEBUG
# define QV2RAY_CONFIG_DIR_SUFFIX "_debug/" # define QV2RAY_CONFIG_DIR_SUFFIX "_debug/"
@ -178,8 +179,9 @@ namespace Qv2ray
QString language; QString language;
bool useDarkTheme; bool useDarkTheme;
bool useDarkTrayIcon; bool useDarkTrayIcon;
Qv2rayUIConfig() : theme("Fusion"), language("en-US"), useDarkTheme(false), useDarkTrayIcon(true) { } int maximumLogLines;
XTOSTRUCT(O(theme, language, useDarkTheme, useDarkTrayIcon)) Qv2rayUIConfig() : theme("Fusion"), language("en-US"), useDarkTheme(false), useDarkTrayIcon(true), maximumLogLines(500) { }
XTOSTRUCT(O(theme, language, useDarkTheme, useDarkTrayIcon, maximumLogLines))
}; };
struct Qv2rayConnectionConfig { struct Qv2rayConnectionConfig {

View File

@ -37,7 +37,7 @@ namespace Qv2ray
vmessUriRoot["host"] = realHost; vmessUriRoot["host"] = realHost;
vmessUriRoot["path"] = transfer.wsSettings.path; vmessUriRoot["path"] = transfer.wsSettings.path;
} else if (transfer.network == "h2" || transfer.network == "http") { } 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; vmessUriRoot["path"] = transfer.httpSettings.path;
} }
@ -74,15 +74,27 @@ namespace Qv2ray
bool SaveSubscriptionConfig(CONFIGROOT obj, const QString &subscription, const QString &name) bool SaveSubscriptionConfig(CONFIGROOT obj, const QString &subscription, const QString &name)
{ {
auto str = JsonToString(obj); 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 there's already a file. THIS IS EXTREMELY RARE
if (config->exists()) { if (config->exists()) {
LOG(MODULE_FILE, "Trying to overrwrite an existing subscription config file. THIS IS RARE") LOG(MODULE_FILE, "Trying to overrwrite an existing subscription config file. THIS IS RARE")
} }
LOG(MODULE_CONFIG, "Saving a subscription named: " + name) LOG(MODULE_CONFIG, "Saving a subscription named: " + fName)
return StringToFile(&str, config); 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) bool RemoveConnection(const QString &alias)
@ -110,14 +122,23 @@ namespace Qv2ray
} }
// This generates global config containing only one outbound.... // 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 // Reset errMessage
*errMessage = ""; *errMessage = "";
if (!vmess.toLower().startsWith("vmess://")) { if (!vmess.toLower().startsWith("vmess://")) {
*errMessage = QObject::tr("VMess string should start with 'vmess://'"); *errMessage = QObject::tr("VMess string should start with 'vmess://'");
return CONFIGROOT(); return default;
} }
try { try {
@ -126,7 +147,7 @@ namespace Qv2ray
if (b64Str.isEmpty()) { if (b64Str.isEmpty()) {
*errMessage = QObject::tr("VMess string should be a valid base64 string"); *errMessage = QObject::tr("VMess string should be a valid base64 string");
return CONFIGROOT(); return default;
} }
auto vmessString = Base64Decode(b64Str); auto vmessString = Base64Decode(b64Str);
@ -134,23 +155,21 @@ namespace Qv2ray
if (!jsonErr.isEmpty()) { if (!jsonErr.isEmpty()) {
*errMessage = jsonErr; *errMessage = jsonErr;
return CONFIGROOT(); return default;
} }
auto vmessConf = JsonFromString(vmessString); auto vmessConf = JsonFromString(vmessString);
if (vmessConf.isEmpty()) { if (vmessConf.isEmpty()) {
*errMessage = QObject::tr("JSON should not be empty"); *errMessage = QObject::tr("JSON should not be empty");
return CONFIGROOT(); return default;
} }
bool flag = true;
// C is a quick hack... // C is a quick hack...
#define C(k) vmessConf.contains(k) #define C(k) vmessConf.contains(k)
bool flag = true; // id, aid, port and add are mandatory fields of a vmess:// link.
flag = flag && C("id"); flag = flag && C("id") && C("aid") && C("port") && C("add");
flag = flag && C("aid");
flag = flag && C("port");
flag = flag && C("add");
// Stream Settings // Stream Settings
auto net = C("net") ? vmessConf["net"].toString() : "tcp"; auto net = C("net") ? vmessConf["net"].toString() : "tcp";
@ -166,32 +185,58 @@ namespace Qv2ray
} catch (exception *e) { } catch (exception *e) {
LOG(MODULE_IMPORT, "Failed to decode vmess string: " + QString(e->what())) LOG(MODULE_IMPORT, "Failed to decode vmess string: " + QString(e->what()))
*errMessage = e->what(); *errMessage = e->what();
return CONFIGROOT(); return default;
} }
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
CONFIGROOT root; CONFIGROOT root;
QStringRef vmessJsonB64(&vmess, 8, vmess.length() - 8); auto b64String = QStringRef(&vmess, 8, vmess.length() - 8).toString();
auto vmessConf = JsonFromString(Base64Decode(vmessJsonB64.toString())); auto vmessConf = JsonFromString(Base64Decode(b64String));
// //
QString ps, add, id, net, type, host, path, tls; QString ps, add, id, net, type, host, path, tls;
int port, aid; int port, aid;
// //
ps = vmessConf.contains("ps") ? vmessConf["ps"].toVariant().toString() // key = key in JSON and the variable name.
: (vmessConf["add"].toVariant().toString() + ":" + vmessConf["port"].toVariant().toString()); // values = Candidate variable list, if not match, the first one is used as default.
add = vmessConf["add"].toVariant().toString(); // [[val.size() <= 1]] is used when only the default value exists.
id = vmessConf["id"].toVariant().toString(); // - It can be empty, if so, if the key is not in the JSON, or the value is empty, it'll report an error.
net = vmessConf.contains("net") ? vmessConf["net"].toVariant().toString() : "tcp"; // - Else if it contains one thing. if the key is not in the JSON, or the value is empty, it'll use that one.
type = vmessConf.contains("type") ? vmessConf["type"].toVariant().toString() : "none"; // - 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
host = vmessConf["host"].toVariant().toString(); // - Else, it'll use the value found from the JSON object.
path = vmessConf["path"].toVariant().toString(); //
tls = vmessConf.contains("tls") ? vmessConf["tls"].toVariant().toString() : ""; #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(); port = vmessConf["port"].toVariant().toInt();
aid = vmessConf["aid"].toVariant().toInt(); aid = vmessConf["aid"].toVariant().toInt();
// // Apply the settings.
// More strict check could be implemented, such as to check if the specified value is
// in the currect format.
// //
// User // User
VMessServerObject::UserObject user; VMessServerObject::UserObject user;
@ -218,7 +263,7 @@ namespace Qv2ray
} else if (net == "http" || net == "h2") { } else if (net == "http" || net == "h2") {
// Fill hosts for HTTP // Fill hosts for HTTP
for (auto _host : host.split(',')) { for (auto _host : host.split(',')) {
streaming.httpSettings.host.push_back(_host); streaming.httpSettings.host.push_back(_host.trimmed());
} }
streaming.httpSettings.path = path; streaming.httpSettings.path = path;
@ -245,7 +290,8 @@ namespace Qv2ray
// //
root["outbounds"] = QJsonArray() << outbound; root["outbounds"] = QJsonArray() << outbound;
// If previous alias is empty, just the PS is needed, else, append a "_" // 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; return root;
} }

View File

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

View File

@ -11,15 +11,20 @@ namespace Qv2ray
QvStartupOptions StartupOption = QvStartupOptions{}; QvStartupOptions StartupOption = QvStartupOptions{};
QvCommandArgParser::QvCommandArgParser() : QObject(), 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.setApplicationDescription(QObject::tr("Qv2ray - A cross-platform Qt frontend for V2ray."));
parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions); 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(noAPIOption);
parser.addOption(runAsRootOption); parser.addOption(runAsRootOption);
parser.addOption(debugOption);
parser.addOption(withToolbarOption);
helpOption = parser.addHelpOption(); helpOption = parser.addHelpOption();
versionOption = parser.addVersionOption(); versionOption = parser.addVersionOption();
} }
@ -38,13 +43,25 @@ namespace Qv2ray
return CommandLineHelpRequested; return CommandLineHelpRequested;
if (parser.isSet(noAPIOption)) { if (parser.isSet(noAPIOption)) {
DEBUG(MODULE_INIT, "No API subsystem is set.")
StartupOption.noAPI = true; StartupOption.noAPI = true;
} }
if (parser.isSet(runAsRootOption)) { if (parser.isSet(runAsRootOption)) {
DEBUG(MODULE_INIT, "Force run as root is set.")
StartupOption.forceRunAsRootUser = true; 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; return CommandLineOk;
} }
} }

View File

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

View File

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

View File

@ -17,81 +17,151 @@ namespace Qv2ray
{ {
namespace QvKernelInterations 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) bool V2rayKernelInstance::ValidateConfig(const QString &path)
{ {
auto conf = GetGlobalConfig(); auto conf = GetGlobalConfig();
QFile coreFile(conf.v2CorePath); QString v2rayCheckResult;
bool coreFileExists = coreFile.exists() && coreFile.open(QFile::ReadOnly);
if (coreFileExists) { if (ValidateKernel(conf.v2CorePath, conf.v2AssetsPath, &v2rayCheckResult)) {
coreFile.close(); DEBUG(MODULE_VCORE, "V2ray version: " + v2rayCheckResult)
} // Append assets location env.
if (coreFileExists) {
QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
env.insert("V2RAY_LOCATION_ASSET", conf.v2AssetsPath); env.insert("V2RAY_LOCATION_ASSET", conf.v2AssetsPath);
//
QProcess process; QProcess process;
process.setProcessEnvironment(env); process.setProcessEnvironment(env);
DEBUG(MODULE_VCORE, "Starting V2ray core with test options")
process.start(conf.v2CorePath, QStringList() << "-test" << "-config" << path, QIODevice::ReadWrite | QIODevice::Text); process.start(conf.v2CorePath, QStringList() << "-test" << "-config" << path, QIODevice::ReadWrite | QIODevice::Text);
if (!process.waitForFinished(1000) && process.exitCode() != 0) { if (!process.waitForFinished(1000) && process.exitCode() != 0) {
LOG(MODULE_VCORE, "v2ray core failed with exitcode: " + QSTRN(process.exitCode())) LOG(MODULE_VCORE, "V2ray core failed with an exit code: " + QSTRN(process.exitCode()))
QvMessageBox(nullptr, tr("Cannot start v2ray"), tr("v2ray core failed with errcode:") + QSTRN(process.exitCode())); QvMessageBoxWarn(nullptr, tr("Cannot start V2ray"), tr("V2ray core failed with an exit code: ") + QSTRN(process.exitCode()));
return false; return false;
} } else if (process.exitCode() != 0) {
QString output = QString(process.readAllStandardOutput()); QString output = QString(process.readAllStandardOutput());
QvMessageBoxWarn(nullptr, tr("Configuration Error"), output.mid(output.indexOf("anti-censorship.") + 17));
if (process.exitCode() != 0) {
QvMessageBox(nullptr, tr("Configuration Error"), output.mid(output.indexOf("anti-censorship.") + 17));
return false; return false;
}
return true;
} else { } else {
QvMessageBox(nullptr, tr("Cannot start v2ray"), DEBUG(MODULE_VCORE, "Config file check passed.")
tr("We cannot find v2ray core binary.") + NEWLINE + NEWLINE + return true;
tr("Possible solutions:") + NEWLINE + }
tr("1. The location is wrong, please go to Preference Window to change it.") + NEWLINE + } else {
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; return false;
} }
} }
V2rayKernelInstance::V2rayKernelInstance() V2rayKernelInstance::V2rayKernelInstance()
{ {
auto proc = new QProcess(); vProcess = new QProcess();;
vProcess = proc;
connect(vProcess, &QProcess::readyReadStandardOutput, this, [this]() { connect(vProcess, &QProcess::readyReadStandardOutput, this, [this]() {
emit onProcessOutputReadyRead(vProcess->readAllStandardOutput().trimmed()); 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) bool V2rayKernelInstance::StartConnection(CONFIGROOT root, int apiPort)
{ {
if (KernelStarted) {
LOG(MODULE_VCORE, "Status is invalid, expect STOPPED when calling StartConnection")
return false;
}
inboundTags.clear(); inboundTags.clear();
for (auto item : root["inbounds"].toArray()) { for (auto item : root["inbounds"].toArray()) {
auto tag = item.toObject()["tag"].toString(""); 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; continue;
}
inboundTags.append(tag); inboundTags.append(tag);
} }
LOG(MODULE_VCORE, "Found Inbound Tags: " + Stringify(inboundTags)) DEBUG(MODULE_VCORE, "Found inbound tags: " + inboundTags.join(";"))
QString json = JsonToString(root);
// Write the final configuration to the disk. // Write the final configuration to the disk.
QString json = JsonToString(root);
StringToFile(&json, new QFile(QV2RAY_GENERATED_FILE_PATH)); 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; auto filePath = QV2RAY_GENERATED_FILE_PATH;
if (ValidateConfig(filePath)) { if (ValidateConfig(filePath)) {
@ -100,10 +170,13 @@ namespace Qv2ray
vProcess->setProcessEnvironment(env); vProcess->setProcessEnvironment(env);
vProcess->start(GetGlobalConfig().v2CorePath, QStringList() << "-config" << filePath, QIODevice::ReadWrite | QIODevice::Text); vProcess->start(GetGlobalConfig().v2CorePath, QStringList() << "-config" << filePath, QIODevice::ReadWrite | QIODevice::Text);
vProcess->waitForStarted(); vProcess->waitForStarted();
ConnectionStatus = STARTED; DEBUG(MODULE_VCORE, "V2ray core started.")
KernelStarted = true;
if (StartupOption.noAPI) { 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 { } else {
// Config API // Config API
apiFailedCounter = 0; apiFailedCounter = 0;
@ -112,12 +185,12 @@ namespace Qv2ray
StatsService service; StatsService service;
Stub = service.NewStub(Channel); Stub = service.NewStub(Channel);
apiTimerId = startTimer(1000); apiTimerId = startTimer(1000);
LOG(MODULE_VCORE, "API Worker started.") DEBUG(MODULE_VCORE, "API Worker started.")
} }
return true; return true;
} else { } else {
ConnectionStatus = STOPPED; KernelStarted = false;
return false; return false;
} }
} }
@ -145,17 +218,17 @@ namespace Qv2ray
void V2rayKernelInstance::StopConnection() void V2rayKernelInstance::StopConnection()
{ {
KernelStarted = false;
vProcess->close(); vProcess->close();
killTimer(apiTimerId); killTimer(apiTimerId);
apiFailedCounter = 0; apiFailedCounter = 0;
transferData.clear(); transferData.clear();
transferSpeed.clear(); transferSpeed.clear();
ConnectionStatus = STOPPED;
} }
V2rayKernelInstance::~V2rayKernelInstance() V2rayKernelInstance::~V2rayKernelInstance()
{ {
if (ConnectionStatus != STOPPED) { if (KernelStarted) {
StopConnection(); StopConnection();
} }
@ -164,14 +237,14 @@ namespace Qv2ray
long V2rayKernelInstance::CallStatsAPIByName(QString name) long V2rayKernelInstance::CallStatsAPIByName(QString name)
{ {
if (ConnectionStatus != STARTED) { if (!KernelStarted) {
LOG(MODULE_VCORE, "Invalid connection status when calling API") LOG(MODULE_VCORE, "Invalid connection status when calling API")
return 0; return 0;
} }
if (apiFailedCounter == QV2RAY_API_CALL_FAILEDCHECK_THRESHOLD) { if (apiFailedCounter == QV2RAY_API_CALL_FAILEDCHECK_THRESHOLD) {
LOG(MODULE_VCORE, "API call failure threshold reached, cancelling further API aclls.") 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(); transferData.clear();
transferSpeed.clear(); transferSpeed.clear();
apiFailedCounter++; apiFailedCounter++;

View File

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

View File

@ -1,6 +1,6 @@
#include "QvLogHighlighter.hpp" #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]*))$" #define TO_EOL "(([\\s\\S]*)|([\\d\\D]*)|([\\w\\W]*))$"
namespace Qv2ray namespace Qv2ray
@ -59,17 +59,17 @@ namespace Qv2ray
// //
{ {
// IP IPv6 Host; // 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.pattern.setPatternOptions(QRegularExpression::PatternOption::ExtendedPatternSyntaxOption);
rule.format = ipHostFormat; rule.format = ipHostFormat;
highlightingRules.append(rule); 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.pattern.setPatternOptions(QRegularExpression::PatternOption::ExtendedPatternSyntaxOption);
rule.format = ipHostFormat; rule.format = ipHostFormat;
highlightingRules.append(rule); 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.pattern.setPatternOptions(QRegularExpression::PatternOption::ExtendedPatternSyntaxOption);
rule.format = ipHostFormat; rule.format = ipHostFormat;
highlightingRules.append(rule); highlightingRules.append(rule);

View File

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

View File

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

View File

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

View File

@ -68,11 +68,11 @@ void ConfigExporter::on_saveBtn_clicked()
void ConfigExporter::on_copyImageBtn_clicked() void ConfigExporter::on_copyImageBtn_clicked()
{ {
QGuiApplication::clipboard()->setImage(image); 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() void ConfigExporter::on_copyVMessBtn_clicked()
{ {
QGuiApplication::clipboard()->setText(message); 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")); 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->setChecked(!outboundsOnly);
keepImportedInboundCheckBox->setEnabled(!outboundsOnly); keepImportedInboundCheckBox->setEnabled(!partialImport);
routeEditBtn->setEnabled(!partialImport);
this->exec(); this->exec();
return this->result() == QDialog::Accepted ? connections : QMap<QString, CONFIGROOT>(); return this->result() == QDialog::Accepted ? connections : QMap<QString, CONFIGROOT>();
} }
@ -52,7 +53,7 @@ void ImportConfigWindow::on_qrFromScreenBtn_clicked()
if (str.trimmed().isEmpty()) { if (str.trimmed().isEmpty()) {
LOG(MODULE_UI, "Cannot decode QR Code from an image, size: h=" + QSTRN(pix.width()) + ", v=" + QSTRN(pix.height())) 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 { } else {
vmessConnectionStringTxt->appendPlainText(str.trimmed() + NEWLINE); vmessConnectionStringTxt->appendPlainText(str.trimmed() + NEWLINE);
} }
@ -71,7 +72,7 @@ void ImportConfigWindow::on_beginImportBtn_clicked()
QString path = fileLineTxt->text(); QString path = fileLineTxt->text();
if (!V2rayKernelInstance::ValidateConfig(path)) { 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; return;
} }
@ -120,12 +121,6 @@ void ImportConfigWindow::on_beginImportBtn_clicked()
break; break;
} }
case 2: {
QvMessageBox(this, tr("TODO"), tr("TODO"));
// Subscription link.
break;
}
} }
accept(); accept();
@ -143,7 +138,7 @@ void ImportConfigWindow::on_selectImageBtn_clicked()
auto str = QZXing().decodeImage(QImage::fromData(buf)); auto str = QZXing().decodeImage(QImage::fromData(buf));
if (str.isEmpty()) { 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; return;
} else { } else {
vmessConnectionStringTxt->appendPlainText(str.trimmed() + NEWLINE); vmessConnectionStringTxt->appendPlainText(str.trimmed() + NEWLINE);
@ -178,7 +173,7 @@ void ImportConfigWindow::on_editFileBtn_clicked()
QFile file(fileLineTxt->text()); QFile file(fileLineTxt->text());
if (!file.exists()) { 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; return;
} }
@ -204,7 +199,7 @@ void ImportConfigWindow::on_editFileBtn_clicked()
bool result = StringToFile(&str, &file); bool result = StringToFile(&str, &file);
if (!result) { 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 { } else {
LOG(MODULE_FILE, "Canceled saving a file.") LOG(MODULE_FILE, "Canceled saving a file.")
@ -242,6 +237,14 @@ void ImportConfigWindow::on_subscriptionButton_clicked()
hide(); hide();
SubscribeEditor w; SubscribeEditor w;
w.exec(); w.exec();
auto importToComplex = !keepImportedInboundCheckBox->isEnabled();
connections.clear();
if (importToComplex) {
auto _result = w.GetSelectedConfig();
connections[_result.first] = _result.second;
}
accept(); accept();
} }

View File

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

View File

@ -24,7 +24,7 @@ InboundEditor::InboundEditor(INBOUND root, QWidget *parent) :
} else { } else {
if (!root["protocol"].toString().isEmpty()) { if (!root["protocol"].toString().isEmpty()) {
LOG(MODULE_UI, "Unsupported inbound type: " + inboundType) 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); tr("Inbound: ") + inboundType);
} else { } else {
LOG(MODULE_UI, "Creating new inbound config") 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?")); //QvMessageBox(this, tr("Removing a user"), tr("No user has been removed. Why?"));
} else { } 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(); auto _user = list[i].toObject();
if (_user["user"].toString() == user) { 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; return;
} }
} }
@ -247,7 +247,7 @@ void InboundEditor::on_socksRemoveUserBtn_clicked()
} }
} }
} else { } 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(); auto _user = list[i].toObject();
if (_user["user"].toString() == user) { 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; return;
} }
} }

View File

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

View File

@ -15,7 +15,7 @@ JsonEditor::JsonEditor(QJsonObject rootObject, QWidget *parent) :
jsonTree->setModel(&model); jsonTree->setModel(&model);
model.loadJson(QJsonDocument(rootObject).toJson()); model.loadJson(QJsonDocument(rootObject).toJson());
} else { } 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)); jsonEditor->setText(JsonToString(rootObject));
@ -29,7 +29,7 @@ QJsonObject JsonEditor::OpenEditor()
auto string = jsonEditor->toPlainText(); auto string = jsonEditor->toPlainText();
while (resultCode == QDialog::Accepted && !VerifyJsonString(string).isEmpty()) { 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(); resultCode = this->exec();
string = jsonEditor->toPlainText(); string = jsonEditor->toPlainText();
} }
@ -69,6 +69,6 @@ void JsonEditor::on_formatJsonBtn_clicked()
jsonEditor->setPlainText(JsonToString(JsonFromString(string))); jsonEditor->setPlainText(JsonToString(JsonFromString(string)));
} else { } else {
RED(jsonEditor) 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_) \ #define SUBSCRIPTION_CONFIG_MODIFY_DENY(_item_) \
if (!CheckConfigType(_item_, REGULAR)) { \ 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; \ return; \
} \ } \
#define IsConnectableItem(item) (item != nullptr && item->childCount() == 0 && (CheckConfigType(item, REGULAR) || CheckConfigType(item, SUBSCRIPTION))) #define IsConnectableItem(item) (item != nullptr && item->childCount() == 0 && (CheckConfigType(item, REGULAR) || CheckConfigType(item, SUBSCRIPTION)))
#define IsSelectionConnectable (!connectionListWidget->selectedItems().empty() && IsConnectableItem(connectionListWidget->selectedItems().first())) #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::mwInstance = nullptr;
MainWindow::MainWindow(QWidget *parent): MainWindow::MainWindow(QWidget *parent):
@ -67,6 +85,13 @@ MainWindow::MainWindow(QWidget *parent):
currentConfig = GetGlobalConfig(); currentConfig = GetGlobalConfig();
vinstance = new V2rayKernelInstance(); vinstance = new V2rayKernelInstance();
connect(vinstance, &V2rayKernelInstance::onProcessOutputReadyRead, this, &MainWindow::UpdateVCoreLog); 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); setupUi(this);
// //
// Two browsers // Two browsers
@ -87,7 +112,7 @@ MainWindow::MainWindow(QWidget *parent):
masterLogBrowser->document()->adjustSize(); masterLogBrowser->document()->adjustSize();
masterLogBrowser->setLineWrapMode(QTextBrowser::LineWrapMode::NoWrap); masterLogBrowser->setLineWrapMode(QTextBrowser::LineWrapMode::NoWrap);
// //
logTimerId = startTimer(500); qvLogTimerId = startTimer(500);
// //
pacServer = new PACServer(); pacServer = new PACServer();
tcpingModel = new QvTCPingModel(3, this); tcpingModel = new QvTCPingModel(3, this);
@ -128,6 +153,7 @@ MainWindow::MainWindow(QWidget *parent):
tray_SystemProxyMenu->addAction(action_Tray_SetSystemProxy); tray_SystemProxyMenu->addAction(action_Tray_SetSystemProxy);
tray_SystemProxyMenu->addAction(action_Tray_ClearSystemProxy); tray_SystemProxyMenu->addAction(action_Tray_ClearSystemProxy);
tray_SystemProxyMenu->setTitle(tr("System Proxy")); tray_SystemProxyMenu->setTitle(tr("System Proxy"));
tray_SystemProxyMenu->setEnabled(false);
// //
tray_RootMenu->addAction(action_Tray_ShowHide); tray_RootMenu->addAction(action_Tray_ShowHide);
tray_RootMenu->addSeparator(); tray_RootMenu->addSeparator();
@ -158,7 +184,7 @@ MainWindow::MainWindow(QWidget *parent):
// //
QAction *action_RCM_RenameConnection = new QAction(tr("Rename"), this); QAction *action_RCM_RenameConnection = new QAction(tr("Rename"), this);
QAction *action_RCM_StartThis = new QAction(tr("Connect to this"), 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_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); 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(); hTray->show();
// //
connectionListMenu = new QMenu(this); connectionListMenu = new QMenu(this);
connectionListMenu->addAction(action_RCM_RenameConnection);
connectionListMenu->addAction(action_RCM_StartThis); connectionListMenu->addAction(action_RCM_StartThis);
connectionListMenu->addAction(action_RCM_ConvToComplex);
connectionListMenu->addAction(action_RCM_EditJson);
connectionListMenu->addAction(action_RCM_ShareQR); connectionListMenu->addAction(action_RCM_ShareQR);
connectionListMenu->addAction(action_RCM_RenameConnection);
connectionListMenu->addAction(action_RCM_EditJson);
connectionListMenu->addAction(action_RCM_ConvToComplex);
// //
OnConfigListChanged(false); OnConfigListChanged(false);
// //
@ -223,13 +249,18 @@ MainWindow::MainWindow(QWidget *parent):
MWFindAndStartAutoConfig(); MWFindAndStartAutoConfig();
// If we are not connected to anything, show the MainWindow. // If we are not connected to anything, show the MainWindow.
if (vinstance->ConnectionStatus != STARTED) { if (!vinstance->KernelStarted) {
this->show(); this->show();
} }
connect(requestHelper, &QvHttpRequestHelper::httpRequestFinished, this, &MainWindow::VersionUpdate); connect(requestHelper, &QvHttpRequestHelper::httpRequestFinished, this, &MainWindow::VersionUpdate);
requestHelper->get("https://api.github.com/repos/lhy0403/Qv2ray/releases/latest"); requestHelper->get("https://api.github.com/repos/lhy0403/Qv2ray/releases/latest");
if (StartupOption.enableToolbarPlguin) {
LOG(MODULE_UI, "Plugin daemon is enabled.")
StartProcessingPlugins(); StartProcessingPlugins();
}
CheckSubscriptionsUpdate(); CheckSubscriptionsUpdate();
} }
@ -280,7 +311,7 @@ void MainWindow::keyPressEvent(QKeyEvent *e)
void MainWindow::on_action_StartThis_triggered() void MainWindow::on_action_StartThis_triggered()
{ {
if (!IsSelectionConnectable) { 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; return;
} }
@ -324,9 +355,9 @@ void MainWindow::VersionUpdate(QByteArray &data)
void MainWindow::OnConfigListChanged(bool need_restart) 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") LOG(MODULE_UI, "Loading new GlobalConfig")
SetEditWidgetEnable(false); SetEditWidgetEnable(false);
@ -354,13 +385,12 @@ void MainWindow::OnConfigListChanged(bool need_restart)
connections[name] = _o; connections[name] = _o;
auto item = new QTreeWidgetItem(QStringList() << _o.connectionName); auto item = new QTreeWidgetItem(QStringList() << _o.connectionName);
item->setData(0, Qt::UserRole, QVariant::fromValue<QvConfigIdentifier>(_o)); item->setData(0, Qt::UserRole, QVariant::fromValue<QvConfigIdentifier>(_o));
DEBUG(MODULE_UI, ItemConnectionIdentifier(item).IdentifierString())
connectionListWidget->addTopLevelItem(item); connectionListWidget->addTopLevelItem(item);
} }
for (auto i = 0; i < _subsConnections.count(); i++) { for (auto i = 0; i < _subsConnections.count(); i++) {
auto subName = _subsConnections.keys()[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); connectionListWidget->addTopLevelItem(subTopLevel);
for (auto j = 0; j < _subsConnections.values()[i].count(); j++) { for (auto j = 0; j < _subsConnections.values()[i].count(); j++) {
@ -375,7 +405,6 @@ void MainWindow::OnConfigListChanged(bool need_restart)
connections[connName] = _o; connections[connName] = _o;
auto item = new QTreeWidgetItem(QStringList() << _o.connectionName); auto item = new QTreeWidgetItem(QStringList() << _o.connectionName);
item->setData(0, Qt::UserRole, QVariant::fromValue<QvConfigIdentifier>(_o)); item->setData(0, Qt::UserRole, QVariant::fromValue<QvConfigIdentifier>(_o));
DEBUG(MODULE_UI, ItemConnectionIdentifier(item).IdentifierString())
subTopLevel->addChild(item); subTopLevel->addChild(item);
} }
} }
@ -399,11 +428,11 @@ void MainWindow::OnConfigListChanged(bool need_restart)
connectionListWidget->sortItems(0, Qt::AscendingOrder); connectionListWidget->sortItems(0, Qt::AscendingOrder);
if (isRunning && need_restart) on_startButton_clicked(); if (wasRunning) on_startButton_clicked();
} }
MainWindow::~MainWindow() MainWindow::~MainWindow()
{ {
killTimer(logTimerId); killTimer(qvLogTimerId);
hTray->hide(); hTray->hide();
delete this->hTray; delete this->hTray;
delete this->vinstance; delete this->vinstance;
@ -411,6 +440,7 @@ MainWindow::~MainWindow()
void MainWindow::UpdateVCoreLog(const QString &log) void MainWindow::UpdateVCoreLog(const QString &log)
{ {
vCoreLogBrowser->append(log); vCoreLogBrowser->append(log);
CleanUpLogs(vCoreLogBrowser)
setMasterLogHBar(); setMasterLogHBar();
} }
void MainWindow::setMasterLogHBar() void MainWindow::setMasterLogHBar()
@ -424,7 +454,9 @@ void MainWindow::setMasterLogHBar()
} }
void MainWindow::on_startButton_clicked() void MainWindow::on_startButton_clicked()
{ {
if (vinstance->ConnectionStatus != STARTED) { vCoreLogBrowser->clear();
if (!vinstance->KernelStarted) {
// Reset the graph // Reset the graph
for (int i = 0; i < 30 ; i++) { for (int i = 0; i < 30 ; i++) {
uploadList[i] = 0; uploadList[i] = 0;
@ -435,7 +467,7 @@ void MainWindow::on_startButton_clicked()
// Check Selection // Check Selection
if (CurrentConnectionIdentifier.isEmpty()) { 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; return;
} }
@ -471,7 +503,7 @@ void MainWindow::on_startButton_clicked()
void MainWindow::on_stopButton_clicked() void MainWindow::on_stopButton_clicked()
{ {
if (vinstance->ConnectionStatus != STOPPED) { if (vinstance->KernelStarted) {
// Is running or starting // Is running or starting
killTimer(speedTimerId); killTimer(speedTimerId);
killTimer(pingTimerId); killTimer(pingTimerId);
@ -479,7 +511,6 @@ void MainWindow::on_stopButton_clicked()
MWStopConnection(); MWStopConnection();
// //
hTray->setToolTip(TRAY_TOOLTIP_PREFIX); hTray->setToolTip(TRAY_TOOLTIP_PREFIX);
vCoreLogBrowser->clear();
statusLabel->setText(tr("Disconnected")); statusLabel->setText(tr("Disconnected"));
action_Tray_Start->setEnabled(true); action_Tray_Start->setEnabled(true);
action_Tray_Stop->setEnabled(false); action_Tray_Stop->setEnabled(false);
@ -513,7 +544,7 @@ void MainWindow::on_activatedTray(QSystemTrayIcon::ActivationReason reason)
break; break;
case QSystemTrayIcon::MiddleClick: case QSystemTrayIcon::MiddleClick:
if (this->vinstance->ConnectionStatus == STARTED) { if (vinstance->KernelStarted) {
on_stopButton_clicked(); on_stopButton_clicked();
} else { } else {
on_startButton_clicked(); on_startButton_clicked();
@ -549,7 +580,10 @@ void MainWindow::ToggleVisibility()
} }
void MainWindow::quit() void MainWindow::quit()
{ {
if (StartupOption.enableToolbarPlguin) {
StopProcessingPlugins(); StopProcessingPlugins();
}
tcpingModel->StopAllPing(); tcpingModel->StopAllPing();
on_stopButton_clicked(); on_stopButton_clicked();
ExitQv2ray(); ExitQv2ray();
@ -578,7 +612,7 @@ void MainWindow::ShowAndSetConnection(QvConfigIdentifier fullIdentifier, bool Se
} }
if (conf.configType == CONNECTION_SUBSCRIPTION) { 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 // Get Connection info
@ -625,8 +659,7 @@ void MainWindow::on_connectionListWidget_currentItemChanged(QTreeWidgetItem *cur
if (!IsConnectableItem(current)) return; if (!IsConnectableItem(current)) return;
// no need to check !isRenamingInProgress since it's always true. // no need to check !isRenamingInProgress since it's always true.
bool canSetConnection = vinstance->ConnectionStatus != STARTED; ShowAndSetConnection(ItemConnectionIdentifier(current), !vinstance->KernelStarted, false);
ShowAndSetConnection(ItemConnectionIdentifier(current), canSetConnection, false);
//on_connectionListWidget_itemClicked(current, 0); //on_connectionListWidget_itemClicked(current, 0);
} }
void MainWindow::on_connectionListWidget_customContextMenuRequested(const QPoint &pos) void MainWindow::on_connectionListWidget_customContextMenuRequested(const QPoint &pos)
@ -666,17 +699,17 @@ void MainWindow::on_connectionListWidget_itemChanged(QTreeWidgetItem *item, int)
bool canContinueRename = true; bool canContinueRename = true;
if (newIdentifier.connectionName.trimmed().isEmpty()) { 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; canContinueRename = false;
} }
if (currentConfig.configs.contains(newIdentifier.connectionName)) { 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; canContinueRename = false;
} }
if (!IsValidFileName(newIdentifier.connectionName + QV2RAY_CONFIG_FILE_EXTENSION)) { 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; canContinueRename = false;
} }
@ -708,7 +741,7 @@ void MainWindow::on_connectionListWidget_itemChanged(QTreeWidgetItem *item, int)
if (CurrentConnectionIdentifier == renameOriginalIdentifier) { if (CurrentConnectionIdentifier == renameOriginalIdentifier) {
CurrentConnectionIdentifier = newIdentifier; CurrentConnectionIdentifier = newIdentifier;
if (vinstance->ConnectionStatus == STARTED) { if (vinstance->KernelStarted) {
on_reconnectButton_clicked(); on_reconnectButton_clicked();
} }
} }
@ -767,7 +800,7 @@ void MainWindow::on_removeConfigButton_clicked()
currentConfig.configs.removeOne(conn.connectionName); currentConfig.configs.removeOne(conn.connectionName);
if (!RemoveConnection(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) { } else if (connData.configType == CONNECTION_SUBSCRIPTION) {
if (subscriptionRemovalCheckStatus == -1) { if (subscriptionRemovalCheckStatus == -1) {
@ -778,7 +811,7 @@ void MainWindow::on_removeConfigButton_clicked()
if (subscriptionRemovalCheckStatus == 1) { if (subscriptionRemovalCheckStatus == 1) {
if (!RemoveSubscriptionConnection(connData.subscriptionName, connData.connectionName)) { 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 { } else {
@ -816,7 +849,7 @@ void MainWindow::on_editConfigButton_clicked()
{ {
// Check if we have a connection selected... // Check if we have a connection selected...
if (!IsSelectionConnectable) { 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; return;
} }
@ -867,7 +900,7 @@ void MainWindow::on_action_RCM_ConvToComplex_triggered()
{ {
// Check if we have a connection selected... // Check if we have a connection selected...
if (!IsSelectionConnectable) { 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; return;
} }
@ -898,7 +931,7 @@ void MainWindow::on_action_RCM_EditJson_triggered()
{ {
// Check if we have a connection selected... // Check if we have a connection selected...
if (!IsSelectionConnectable) { 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; 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) { 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()); aliases.append(connections.keys());
} }
} else if (selection.count() == 1) {
if (IsSelectionConnectable) {
// Current selection is a config
aliases.append(ItemConnectionIdentifier(selection.first()));
} else { } else {
// Current selection is a subscription or... something else strange. 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. // So we add another check to make sure the selected one is a subscription entry.
if (selection.first()->childCount() > 0) {
// Loop to add all sub-connections to the list. // Loop to add all sub-connections to the list.
for (auto i = 0; i < selection.first()->childCount(); i++) { for (auto j = 0; j < thisItem->childCount(); j++) {
aliases.append(ItemConnectionIdentifier(selection.first()->child(i))); aliases.append(ItemConnectionIdentifier(thisItem->child(j)));
} }
} else {
aliases.append(ItemConnectionIdentifier(thisItem));
} }
} }
} }
@ -976,7 +1009,7 @@ void MainWindow::on_shareBtn_clicked()
ConfigExporter v(vmess, this); ConfigExporter v(vmess, this);
v.OpenExport(); v.OpenExport();
} else { } 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() void MainWindow::on_action_RCM_ShareQR_triggered()
@ -1024,12 +1057,14 @@ void MainWindow::timerEvent(QTimerEvent *event)
dataamountLabel->setText(totalDataUp + NEWLINE + totalDataDown); dataamountLabel->setText(totalDataUp + NEWLINE + totalDataDown);
// //
hTray->setToolTip(TRAY_TOOLTIP_PREFIX NEWLINE + tr("Connected: ") + CurrentConnectionIdentifier.IdentifierString() + NEWLINE "Up: " + totalSpeedUp + " Down: " + totalSpeedDown); 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(); QString lastLog = readLastLog();
if (!lastLog.isEmpty()) { if (!lastLog.isEmpty()) {
qvAppLogBrowser->append(lastLog); qvAppLogBrowser->append(lastLog);
} }
CleanUpLogs(vCoreLogBrowser)
} else if (event->timerId() == pingTimerId) { } else if (event->timerId() == pingTimerId) {
MWTryPingConnection(CurrentConnectionIdentifier); 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 // ID for QTimers
// //
int logTimerId; int qvLogTimerId;
int speedTimerId; int speedTimerId;
int pingTimerId; int pingTimerId;
// //
@ -143,7 +143,7 @@ class MainWindow : public QMainWindow, Ui::MainWindow
QAction *action_Tray_SetSystemProxy; QAction *action_Tray_SetSystemProxy;
QAction *action_Tray_ClearSystemProxy; 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; bool systemProxyEnabled;
void MWFindAndStartAutoConfig(); void MWFindAndStartAutoConfig();
bool MWtryStartConnection(); bool MWtryStartConnection();

View File

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

View File

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

View File

@ -29,143 +29,19 @@
<bool>true</bool> <bool>true</bool>
</property> </property>
<layout class="QGridLayout" name="gridLayout_4"> <layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0"> <item row="2" column="0" colspan="2">
<layout class="QFormLayout" name="formLayout_5"> <layout class="QHBoxLayout" name="horizontalLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="ipLabel_3">
<property name="text">
<string>Tag</string>
</property>
</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> <item>
<property name="text"> <widget class="QDialogButtonBox" name="buttonBox">
<string notr="true">VMess</string> <property name="orientation">
<enum>Qt::Horizontal</enum>
</property> </property>
</item> <property name="standardButtons">
<item> <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
<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> </property>
</widget> </widget>
</item> </item>
</layout> </layout>
</widget>
</item> </item>
<item row="1" column="0" colspan="2"> <item row="1" column="0" colspan="2">
<widget class="QGroupBox" name="outboundSettingWrapper"> <widget class="QGroupBox" name="outboundSettingWrapper">
@ -1059,22 +935,193 @@
</layout> </layout>
</widget> </widget>
</item> </item>
<item row="2" column="0" colspan="2"> <item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_3"> <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>
</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> <item>
<widget class="QDialogButtonBox" name="buttonBox"> <property name="text">
<property name="orientation"> <string notr="true">VMess</string>
<enum>Qt::Horizontal</enum>
</property> </property>
<property name="standardButtons"> </item>
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> <item>
<property name="text">
<string notr="true">ShadowSocks</string>
</property> </property>
</item>
<item>
<property name="text">
<string>Socks</string>
</property>
</item>
</widget> </widget>
</item> </item>
</layout> </layout>
</item> </item>
</layout> </layout>
</widget> </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/> <resources/>
<connections> <connections>
<connection> <connection>

View File

@ -22,6 +22,14 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent),
setupUi(this); setupUi(this);
textBrowser->setHtml(StringFromFile(new QFile(":/assets/credit.html"))); textBrowser->setHtml(StringFromFile(new QFile(":/assets/credit.html")));
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); 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 // We add locales
languageComboBox->clear(); languageComboBox->clear();
QDirIterator it(":/translations"); QDirIterator it(":/translations");
@ -55,17 +63,16 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent),
// //
listenIPTxt->setText(CurrentConfig.inboundConfig.listenip); listenIPTxt->setText(CurrentConfig.inboundConfig.listenip);
bool pacEnabled = CurrentConfig.inboundConfig.pacConfig.enablePAC; bool pacEnabled = CurrentConfig.inboundConfig.pacConfig.enablePAC;
enablePACCB->setChecked(pacEnabled); pacGroupBox->setChecked(pacEnabled);
setSysProxyCB->setChecked(CurrentConfig.inboundConfig.setSystemProxy); setSysProxyCB->setChecked(CurrentConfig.inboundConfig.setSystemProxy);
// //
// PAC // PAC
pacGroupBox->setEnabled(pacEnabled);
pacPortSB->setValue(CurrentConfig.inboundConfig.pacConfig.port); pacPortSB->setValue(CurrentConfig.inboundConfig.pacConfig.port);
pacProxyTxt->setText(CurrentConfig.inboundConfig.pacConfig.localIP); pacProxyTxt->setText(CurrentConfig.inboundConfig.pacConfig.localIP);
pacProxyCB->setCurrentIndex(CurrentConfig.inboundConfig.pacConfig.useSocksProxy ? 1 : 0); pacProxyCB->setCurrentIndex(CurrentConfig.inboundConfig.pacConfig.useSocksProxy ? 1 : 0);
// //
bool have_http = CurrentConfig.inboundConfig.useHTTP; bool have_http = CurrentConfig.inboundConfig.useHTTP;
httpCB->setChecked(have_http); httpGroupBox->setChecked(have_http);
httpPortLE->setValue(CurrentConfig.inboundConfig.http_port); httpPortLE->setValue(CurrentConfig.inboundConfig.http_port);
httpAuthCB->setChecked(CurrentConfig.inboundConfig.http_useAuth); httpAuthCB->setChecked(CurrentConfig.inboundConfig.http_useAuth);
// //
@ -74,11 +81,10 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent),
httpAuthPasswordTxt->setEnabled(CurrentConfig.inboundConfig.http_useAuth); httpAuthPasswordTxt->setEnabled(CurrentConfig.inboundConfig.http_useAuth);
httpAuthUsernameTxt->setText(CurrentConfig.inboundConfig.httpAccount.user); httpAuthUsernameTxt->setText(CurrentConfig.inboundConfig.httpAccount.user);
httpAuthPasswordTxt->setText(CurrentConfig.inboundConfig.httpAccount.pass); httpAuthPasswordTxt->setText(CurrentConfig.inboundConfig.httpAccount.pass);
httpGroupBox->setEnabled(have_http);
// //
// //
bool have_socks = CurrentConfig.inboundConfig.useSocks; bool have_socks = CurrentConfig.inboundConfig.useSocks;
socksCB->setChecked(have_socks); socksGroupBox->setChecked(have_socks);
socksPortLE->setValue(CurrentConfig.inboundConfig.socks_port); socksPortLE->setValue(CurrentConfig.inboundConfig.socks_port);
// //
socksAuthCB->setChecked(CurrentConfig.inboundConfig.socks_useAuth); socksAuthCB->setChecked(CurrentConfig.inboundConfig.socks_useAuth);
@ -90,7 +96,6 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent),
socksUDPCB->setChecked(CurrentConfig.inboundConfig.socksUDP); socksUDPCB->setChecked(CurrentConfig.inboundConfig.socksUDP);
socksUDPIP->setEnabled(CurrentConfig.inboundConfig.socksUDP); socksUDPIP->setEnabled(CurrentConfig.inboundConfig.socksUDP);
socksUDPIP->setText(CurrentConfig.inboundConfig.socksLocalIP); socksUDPIP->setText(CurrentConfig.inboundConfig.socksLocalIP);
socksGroupBox->setEnabled(have_socks);
// //
// //
vCorePathTxt->setText(CurrentConfig.v2CorePath); vCorePathTxt->setText(CurrentConfig.v2CorePath);
@ -125,7 +130,7 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent),
nsBarPagesList->setCurrentRow(0); nsBarPagesList->setCurrentRow(0);
on_nsBarPagesList_currentRowChanged(0); on_nsBarPagesList_currentRowChanged(0);
} else { } else {
nsBarVerticalLayout->setEnabled(false); networkToolbarSettingsFrame->setEnabled(false);
nsBarLinesList->setEnabled(false); nsBarLinesList->setEnabled(false);
nsBarLineDelBTN->setEnabled(false); nsBarLineDelBTN->setEnabled(false);
nsBarLineAddBTN->setEnabled(false); nsBarLineAddBTN->setEnabled(false);
@ -154,8 +159,7 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent),
autoStartConnCombo->setCurrentText(autoCon); autoStartConnCombo->setCurrentText(autoCon);
// FP Settings // FP Settings
fpEnabledCB->setChecked(CurrentConfig.connectionConfig.forwardProxyConfig.enableForwardProxy); fpGroupBox->setChecked(CurrentConfig.connectionConfig.forwardProxyConfig.enableForwardProxy);
fpFrame->setEnabled(fpEnabledCB->isChecked());
fpUsernameTx->setText(CurrentConfig.connectionConfig.forwardProxyConfig.username); fpUsernameTx->setText(CurrentConfig.connectionConfig.forwardProxyConfig.username);
fpPasswordTx->setText(CurrentConfig.connectionConfig.forwardProxyConfig.password); fpPasswordTx->setText(CurrentConfig.connectionConfig.forwardProxyConfig.password);
fpAddressTx->setText(CurrentConfig.connectionConfig.forwardProxyConfig.serverAddress); fpAddressTx->setText(CurrentConfig.connectionConfig.forwardProxyConfig.serverAddress);
@ -165,6 +169,10 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent),
fpUsernameTx->setEnabled(fpUseAuthCB->isChecked()); fpUsernameTx->setEnabled(fpUseAuthCB->isChecked());
fpPasswordTx->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; finishedLoading = true;
} }
@ -175,32 +183,38 @@ PreferencesWindow::~PreferencesWindow()
void PreferencesWindow::on_buttonBox_accepted() void PreferencesWindow::on_buttonBox_accepted()
{ {
int sp = socksPortLE->text().toInt(); QSet<int> ports;
int hp = httpPortLE->text().toInt() ; auto size = 0;
if (!(sp == 0 || hp == 0) && sp == hp) { if (CurrentConfig.inboundConfig.useHTTP) {
QvMessageBox(this, tr("Preferences"), tr("Port numbers cannot be the same")); size ++;
return; ports << CurrentConfig.inboundConfig.http_port;
} }
if (CurrentConfig.inboundConfig.useSocks) {
size ++;
ports << CurrentConfig.inboundConfig.socks_port;
}
if (CurrentConfig.inboundConfig.pacConfig.enablePAC) {
size ++;
ports << CurrentConfig.inboundConfig.pacConfig.port;
}
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); SetGlobalConfig(CurrentConfig);
emit s_reload_config(IsConnectionPropertyChanged); emit s_reload_config(IsConnectionPropertyChanged);
} }
void PreferencesWindow::on_httpCB_stateChanged(int checked)
{
NEEDRESTART
bool enabled = checked == Qt::Checked;
httpGroupBox->setEnabled(enabled);
CurrentConfig.inboundConfig.useHTTP = enabled;
}
void PreferencesWindow::on_socksCB_stateChanged(int checked)
{
NEEDRESTART
bool enabled = checked == Qt::Checked;
socksGroupBox->setEnabled(enabled);
CurrentConfig.inboundConfig.useSocks = enabled;
} }
void PreferencesWindow::on_httpAuthCB_stateChanged(int checked) 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) void PreferencesWindow::on_languageComboBox_currentTextChanged(const QString &arg1)
{ {
LOADINGCHECK LOADINGCHECK
//
// A strange bug prevents us to change the UI language online
// https://github.com/lhy0403/Qv2ray/issues/34
//
CurrentConfig.uiConfig.language = arg1; 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) void PreferencesWindow::on_logLevelComboBox_currentIndexChanged(int index)
@ -299,7 +299,7 @@ void PreferencesWindow::on_localDNSCb_stateChanged(int arg1)
void PreferencesWindow::on_selectVAssetBtn_clicked() void PreferencesWindow::on_selectVAssetBtn_clicked()
{ {
NEEDRESTART 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()) { if (!dir.isEmpty()) {
vCoreAssetsPathTxt->setText(dir); vCoreAssetsPathTxt->setText(dir);
@ -309,7 +309,7 @@ void PreferencesWindow::on_selectVAssetBtn_clicked()
void PreferencesWindow::on_selectVCoreBtn_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()) { if (!core.isEmpty()) {
vCorePathTxt->setText(core); vCorePathTxt->setText(core);
@ -363,26 +363,26 @@ void PreferencesWindow::on_tProxyCheckBox_stateChanged(int arg1)
if (finishedLoading) { if (finishedLoading) {
// Set UID and GID for linux // Set UID and GID for linux
// Steps: // 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. // --> 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) { if (arg1 == Qt::Checked) {
// We enable it! // We enable it!
if (QvMessageBoxAsk(this, tr("Enable tProxy Support"), if (QvMessageBoxAsk(this, tr("Enable tProxy Support"),
tr("This will append capabilities to the v2ray executable.") + 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("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 + 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) { " https://lhy0403.github.io/Qv2ray/zh-CN/FAQ.html ") != QMessageBox::Yes) {
tProxyCheckBox->setChecked(false); tProxyCheckBox->setChecked(false);
LOG(MODULE_UI, "Canceled enabling tProxy feature.") LOG(MODULE_UI, "Canceled enabling tProxy feature.")
} else { } else {
LOG(MODULE_VCORE, "ENABLING tProxy Support") 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 v2ctlPath = QFileInfo(CurrentConfig.v2CorePath).path() + "/v2ctl";
auto newPath = QFileInfo(QV2RAY_DEFAULT_VCORE_PATH).path(); auto newPath = QFileInfo(QV2RAY_DEFAULT_VCORE_PATH).path();
// //
LOG(MODULE_FILE, " --> Origin v2ctl file is at: " + v2ctlPath) 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....") 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"; 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"; 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") { if (vCoreresult == "OK" && vCtlresult == "OK") {
LOG(MODULE_VCORE, " --> Done copying files.") LOG(MODULE_VCORE, " --> Done copying files.")
on_vCorePathTxt_textEdited(QV2RAY_DEFAULT_VCORE_PATH); on_vCorePathTxt_textEdited(QV2RAY_DEFAULT_VCORE_PATH);
} else { } else {
LOG(MODULE_VCORE, "FAILED to copy v2ray files. Aborting.") LOG(MODULE_VCORE, "FAILED to copy V2ray files. Aborting.")
QvMessageBox(this, tr("Enable tProxy Support"), QvMessageBoxWarn(this, tr("Enable tProxy Support"),
tr("Qv2ray cannot copy one or both v2ray files from: ") + NEWLINE + NEWLINE + tr("Qv2ray cannot copy one or both V2ray files from: ") + NEWLINE + NEWLINE +
CurrentConfig.v2CorePath + NEWLINE + v2ctlPath + NEWLINE + NEWLINE + CurrentConfig.v2CorePath + NEWLINE + v2ctlPath + NEWLINE + NEWLINE +
tr("to this path: ") + NEWLINE + newPath); tr("to this path: ") + NEWLINE + newPath);
return; return;
} }
} else { } 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.") 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) { if (ret != 0) {
LOG(MODULE_UI, "WARN: setcap exits with code: " + QSTRN(ret)) 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; CurrentConfig.tProxySupport = true;
@ -440,7 +440,7 @@ void PreferencesWindow::on_tProxyCheckBox_stateChanged(int arg1)
if (ret != 0) { if (ret != 0) {
LOG(MODULE_UI, "WARN: setcap exits with code: " + QSTRN(ret)) 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; CurrentConfig.tProxySupport = false;
@ -451,7 +451,7 @@ void PreferencesWindow::on_tProxyCheckBox_stateChanged(int arg1)
#else #else
Q_UNUSED(arg1) Q_UNUSED(arg1)
// No such tProxy thing on Windows and macOS // 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; CurrentConfig.tProxySupport = false;
tProxyCheckBox->setChecked(false); tProxyCheckBox->setChecked(false);
#endif #endif
@ -532,7 +532,7 @@ void PreferencesWindow::on_nsBarPageDelBTN_clicked()
nsBarLineAddBTN->setEnabled(false); nsBarLineAddBTN->setEnabled(false);
nsBarLineDelBTN->setEnabled(false); nsBarLineDelBTN->setEnabled(false);
nsBarLinesList->setEnabled(false); nsBarLinesList->setEnabled(false);
nsBarVerticalLayout->setEnabled(false); networkToolbarSettingsFrame->setEnabled(false);
nsBarPageYOffset->setEnabled(false); nsBarPageYOffset->setEnabled(false);
nsBarLinesList->clear(); nsBarLinesList->clear();
} }
@ -566,7 +566,7 @@ void PreferencesWindow::on_nsBarLineDelBTN_clicked()
CurrentBarLineId = 0; CurrentBarLineId = 0;
if (nsBarLinesList->count() <= 0) { if (nsBarLinesList->count() <= 0) {
nsBarVerticalLayout->setEnabled(false); networkToolbarSettingsFrame->setEnabled(false);
nsBarLineDelBTN->setEnabled(false); nsBarLineDelBTN->setEnabled(false);
} }
@ -595,7 +595,7 @@ void PreferencesWindow::on_nsBarPagesList_currentRowChanged(int currentRow)
nsBarLinesList->setCurrentRow(0); nsBarLinesList->setCurrentRow(0);
ShowLineParameters(CurrentBarLine); ShowLineParameters(CurrentBarLine);
} else { } else {
nsBarVerticalLayout->setEnabled(false); networkToolbarSettingsFrame->setEnabled(false);
} }
} }
@ -707,7 +707,7 @@ void PreferencesWindow::ShowLineParameters(QvBarLine &barLine)
nsBarContentCombo->setCurrentText(NetSpeedPluginMessages[barLine.ContentType]); nsBarContentCombo->setCurrentText(NetSpeedPluginMessages[barLine.ContentType]);
nsBarTagTxt->setText(barLine.Message); nsBarTagTxt->setText(barLine.Message);
finishedLoading = true; finishedLoading = true;
nsBarVerticalLayout->setEnabled(true); networkToolbarSettingsFrame->setEnabled(true);
} }
void PreferencesWindow::on_chooseColorBtn_clicked() void PreferencesWindow::on_chooseColorBtn_clicked()
@ -754,7 +754,7 @@ void PreferencesWindow::on_darkThemeCB_stateChanged(int arg1)
{ {
LOADINGCHECK LOADINGCHECK
CurrentConfig.uiConfig.useDarkTheme = arg1 == Qt::Checked; 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 #ifdef QV2RAY_USE_BUILTIN_DARKTHEME
themeCombo->setEnabled(arg1 != Qt::Checked); themeCombo->setEnabled(arg1 != Qt::Checked);
@ -772,15 +772,6 @@ void PreferencesWindow::on_darkTrayCB_stateChanged(int arg1)
CurrentConfig.uiConfig.useDarkTrayIcon = arg1 == Qt::Checked; 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() void PreferencesWindow::on_pacGoBtn_clicked()
{ {
LOADINGCHECK LOADINGCHECK
@ -831,7 +822,7 @@ void PreferencesWindow::on_pacGoBtn_clicked()
} }
LOG(MODULE_NETWORK, "Fetched: " + gfwLocation) 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); pacGoBtn->setEnabled(true);
gfwListCB->setEnabled(true); gfwListCB->setEnabled(true);
@ -848,7 +839,7 @@ void PreferencesWindow::on_pacPortSB_valueChanged(int arg1)
LOADINGCHECK LOADINGCHECK
NEEDRESTART NEEDRESTART
CurrentConfig.inboundConfig.pacConfig.port = arg1; 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) void PreferencesWindow::on_setSysProxyCB_stateChanged(int arg1)
@ -907,7 +898,7 @@ void PreferencesWindow::on_installBootStart_clicked()
// If failed to set the status. // If failed to set the status.
if (!GetLaunchAtLoginStatus()) { 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()); SetAutoStartButtonsState(GetLaunchAtLoginStatus());
@ -919,7 +910,7 @@ void PreferencesWindow::on_removeBootStart_clicked()
// If that setting still present. // If that setting still present.
if (GetLaunchAtLoginStatus()) { 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()); SetAutoStartButtonsState(GetLaunchAtLoginStatus());
@ -931,25 +922,21 @@ void PreferencesWindow::SetAutoStartButtonsState(bool isAutoStart)
removeBootStart->setEnabled(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) void PreferencesWindow::on_fpTypeCombo_currentIndexChanged(const QString &arg1)
{ {
LOADINGCHECK
CurrentConfig.connectionConfig.forwardProxyConfig.type = arg1; CurrentConfig.connectionConfig.forwardProxyConfig.type = arg1;
} }
void PreferencesWindow::on_fpAddressTx_textEdited(const QString &arg1) void PreferencesWindow::on_fpAddressTx_textEdited(const QString &arg1)
{ {
LOADINGCHECK
CurrentConfig.connectionConfig.forwardProxyConfig.serverAddress = arg1; CurrentConfig.connectionConfig.forwardProxyConfig.serverAddress = arg1;
} }
void PreferencesWindow::on_spPortSB_valueChanged(int arg1) void PreferencesWindow::on_spPortSB_valueChanged(int arg1)
{ {
LOADINGCHECK
CurrentConfig.connectionConfig.forwardProxyConfig.port = arg1; CurrentConfig.connectionConfig.forwardProxyConfig.port = arg1;
} }
@ -963,15 +950,73 @@ void PreferencesWindow::on_fpUseAuthCB_stateChanged(int arg1)
void PreferencesWindow::on_fpUsernameTx_textEdited(const QString &arg1) void PreferencesWindow::on_fpUsernameTx_textEdited(const QString &arg1)
{ {
LOADINGCHECK
CurrentConfig.connectionConfig.forwardProxyConfig.username = arg1; CurrentConfig.connectionConfig.forwardProxyConfig.username = arg1;
} }
void PreferencesWindow::on_fpPasswordTx_textEdited(const QString &arg1) void PreferencesWindow::on_fpPasswordTx_textEdited(const QString &arg1)
{ {
LOADINGCHECK
CurrentConfig.connectionConfig.forwardProxyConfig.password = arg1; CurrentConfig.connectionConfig.forwardProxyConfig.password = arg1;
} }
void PreferencesWindow::on_fpPortSB_valueChanged(int arg1) void PreferencesWindow::on_fpPortSB_valueChanged(int arg1)
{ {
LOADINGCHECK
CurrentConfig.connectionConfig.forwardProxyConfig.port = arg1; 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: private slots:
void on_buttonBox_accepted(); void on_buttonBox_accepted();
void on_httpCB_stateChanged(int arg1);
void on_socksCB_stateChanged(int arg1);
void on_httpAuthCB_stateChanged(int arg1); void on_httpAuthCB_stateChanged(int arg1);
void on_socksAuthCB_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_darkTrayCB_stateChanged(int arg1);
void on_enablePACCB_stateChanged(int arg1);
void on_pacGoBtn_clicked(); void on_pacGoBtn_clicked();
void on_pacPortSB_valueChanged(int arg1); void on_pacPortSB_valueChanged(int arg1);
@ -138,8 +132,6 @@ class PreferencesWindow : public QDialog, private Ui::PreferencesWindow
void on_removeBootStart_clicked(); void on_removeBootStart_clicked();
void on_fpEnabledCB_stateChanged(int arg1);
void on_fpTypeCombo_currentIndexChanged(const QString &arg1); void on_fpTypeCombo_currentIndexChanged(const QString &arg1);
void on_fpAddressTx_textEdited(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_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: private:
void SetAutoStartButtonsState(bool isAutoStart); void SetAutoStartButtonsState(bool isAutoStart);
// Set ui parameters for a line; // 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"); ruleJsonObject.remove("outboundTag");
// Find balancer list // Find balancer list
if (!_balancers.contains(_rule.balancerTag)) { if (!balancers.contains(_rule.balancerTag)) {
LOG(MODULE_UI, "Cannot find a balancer for tag: " + _rule.balancerTag) LOG(MODULE_UI, "Cannot find a balancer for tag: " + _rule.balancerTag)
} else { } else {
auto _balancerList = balancers[_rule.balancerTag]; auto _balancerList = balancers[_rule.balancerTag];
@ -373,19 +373,19 @@ void RouteEditor::ShowCurrentRuleDetail()
routePortTxt->setText(CurrentRule.port); routePortTxt->setText(CurrentRule.port);
// //
// Users // Users
QString users = Stringify(CurrentRule.user, NEWLINE); QString users = CurrentRule.user.join(NEWLINE);
routeUserTxt->setPlainText(users); routeUserTxt->setPlainText(users);
// //
// Incoming Sources // Incoming Sources
QString sources = Stringify(CurrentRule.source, NEWLINE); QString sources = CurrentRule.source.join(NEWLINE);
sourceIPList->setPlainText(sources); sourceIPList->setPlainText(sources);
// //
// Domains // Domains
QString domains = Stringify(CurrentRule.domain, NEWLINE); QString domains = CurrentRule.domain.join(NEWLINE);
hostList->setPlainText(domains); hostList->setPlainText(domains);
// //
// Outcoming IPs // Outcoming IPs
QString ips = Stringify(CurrentRule.ip, NEWLINE); QString ips = CurrentRule.ip.join(NEWLINE);
ipList->setPlainText(ips); ipList->setPlainText(ips);
LOAD_FLAG_END LOAD_FLAG_END
} }
@ -410,7 +410,7 @@ void RouteEditor::on_routeProtocolHTTPCB_stateChanged(int arg1)
if (routeProtocolBTCB->isChecked()) protocols.push_back("bittorrent"); if (routeProtocolBTCB->isChecked()) protocols.push_back("bittorrent");
CurrentRule.protocol = protocols; 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) 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"); if (routeProtocolBTCB->isChecked()) protocols.push_back("bittorrent");
CurrentRule.protocol = protocols; 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) 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"); if (routeProtocolTLSCB->isChecked()) protocols.push_back("tls");
CurrentRule.protocol = protocols; CurrentRule.protocol = protocols;
statusLabel->setText(tr("Protocol list changed: ") + Stringify(protocols)); statusLabel->setText(tr("Protocol list changed: ") + protocols.join(";"));
} }
void RouteEditor::on_balancerAddBtn_clicked() void RouteEditor::on_balancerAddBtn_clicked()
{ {
@ -551,7 +551,7 @@ void RouteEditor::on_enableBalancerCB_stateChanged(int arg1)
} }
} }
} else { } 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() void RouteEditor::on_addDefaultBtn_clicked()
@ -628,7 +628,7 @@ void RouteEditor::on_ruleEnableCB_stateChanged(int arg1)
void RouteEditor::on_delBtn_clicked() void RouteEditor::on_delBtn_clicked()
{ {
if (nodeScene->selectedNodes().empty()) { 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]; auto firstNode = nodeScene->selectedNodes()[0];
@ -677,7 +677,7 @@ void RouteEditor::on_delBtn_clicked()
void RouteEditor::on_editBtn_clicked() void RouteEditor::on_editBtn_clicked()
{ {
if (nodeScene->selectedNodes().empty()) { 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]; auto firstNode = nodeScene->selectedNodes()[0];
@ -692,7 +692,7 @@ void RouteEditor::on_editBtn_clicked()
int _code; int _code;
if (protocol != "http" && protocol != "mtproto" && protocol != "socks" && protocol != "dokodemo-door") { 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" + 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.")); tr("We will launch Json Editor instead."));
statusLabel->setText(tr("Opening JSON editor")); statusLabel->setText(tr("Opening JSON editor"));
JsonEditor *w = new JsonEditor(_in, this); JsonEditor *w = new JsonEditor(_in, this);
@ -726,7 +726,7 @@ void RouteEditor::on_editBtn_clicked()
int _code; int _code;
if (protocol != "vmess" && protocol != "shadowsocks" && protocol != "socks") { if (protocol != "vmess" && protocol != "shadowsocks" && protocol != "socks") {
QvMessageBox(this, tr("Unsupported Outbound Type"), QvMessageBoxWarn(this, tr("Unsupported Outbound Type"),
tr("This outbound entry is not supported by the GUI editor.") + NEWLINE + tr("This outbound entry is not supported by the GUI editor.") + NEWLINE +
tr("We will launch Json Editor instead.")); tr("We will launch Json Editor instead."));
JsonEditor w(_out, this); JsonEditor w(_out, this);
@ -769,5 +769,10 @@ void RouteEditor::on_defaultOutboundCombo_currentIndexChanged(const QString &arg
void RouteEditor::on_ruleTagLineEdit_textEdited(const QString &arg1) 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); RenameItemTag(RENAME_RULE, CurrentRule.QV2RAY_RULE_TAG, arg1);
} }

View File

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

View File

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

View File

@ -17,6 +17,11 @@ SubscribeEditor::SubscribeEditor(QWidget *parent) :
LoadSubscriptionList(subscriptions); LoadSubscriptionList(subscriptions);
} }
QPair<QString, CONFIGROOT> SubscribeEditor::GetSelectedConfig()
{
return currentSelectedConfig;
}
void SubscribeEditor::LoadSubscriptionList(QMap<QString, Qv2raySubscriptionConfig> list) void SubscribeEditor::LoadSubscriptionList(QMap<QString, Qv2raySubscriptionConfig> list)
{ {
subscriptionList->clear(); subscriptionList->clear();
@ -56,12 +61,12 @@ void SubscribeEditor::on_updateButton_clicked()
bool canGo = true; bool canGo = true;
if (newName.isEmpty() || !IsValidFileName(newName)) { 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; canGo = false;
} }
if (subscriptionList->findItems(newName, Qt::MatchExactly).count() > 0) { 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; canGo = false;
} }
@ -73,7 +78,7 @@ void SubscribeEditor::on_updateButton_clicked()
bool result = RenameSubscription(currentSubName, newName); bool result = RenameSubscription(currentSubName, newName);
if (!result) { 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; return;
} }
@ -92,7 +97,7 @@ void SubscribeEditor::on_updateButton_clicked()
SetGlobalConfig(conf); SetGlobalConfig(conf);
// This will set the name to the new name. // This will set the name to the new name.
LoadSubscriptionList(subscriptions); 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; subscriptions[currentSubName].updateInterval = newUpdateInterval;
@ -130,15 +135,19 @@ void SubscribeEditor::StartUpdateSubscription(const QString &subscriptionName)
LOG(MODULE_SUBSCRIPTION, "Processing a subscription with following error: " + errMessage) LOG(MODULE_SUBSCRIPTION, "Processing a subscription with following error: " + errMessage)
} else { } else {
connectionsList->addItem(_alias); 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()); subscriptions[subscriptionName].lastUpdated = system_clock::to_time_t(system_clock::now());
lastUpdatedLabel->setText(timeToString(subscriptions[subscriptionName].lastUpdated));
isUpdateInProgress = false; isUpdateInProgress = false;
} else { } else {
LOG(MODULE_NETWORK, "We have received an empty string from the URL.") 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); this->setEnabled(true);
@ -161,8 +170,7 @@ void SubscribeEditor::on_removeSubsButton_clicked()
auto conf = GetGlobalConfig(); auto conf = GetGlobalConfig();
if (conf.autoStartConfig.subscriptionName == name) { if (conf.autoStartConfig.subscriptionName == name) {
conf.autoStartConfig.subscriptionName.clear(); conf.autoStartConfig = QvConfigIdentifier();
conf.autoStartConfig.connectionName.clear();
SetGlobalConfig(conf); SetGlobalConfig(conf);
} }
@ -186,7 +194,7 @@ void SubscribeEditor::on_subscriptionList_currentRowChanged(int currentRow)
subNameTxt->setText(currentSubName); subNameTxt->setText(currentSubName);
subAddrTxt->setText(subscriptions[currentSubName].address); subAddrTxt->setText(subscriptions[currentSubName].address);
updateIntervalSB->setValue(subscriptions[currentSubName].updateInterval); updateIntervalSB->setValue(subscriptions[currentSubName].updateInterval);
lastUpdatedLabel->setText(QString::fromStdString(timeToString(subscriptions[currentSubName].lastUpdated))); lastUpdatedLabel->setText(timeToString(subscriptions[currentSubName].lastUpdated));
// //
connectionsList->clear(); connectionsList->clear();
auto _list = GetSubscriptionConnection(currentSubName); auto _list = GetSubscriptionConnection(currentSubName);
@ -225,3 +233,12 @@ void SubscribeEditor::on_updateIntervalSB_valueChanged(double arg1)
{ {
subscriptions[currentSubName].updateInterval = 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: public:
explicit SubscribeEditor(QWidget *parent = nullptr); explicit SubscribeEditor(QWidget *parent = nullptr);
~SubscribeEditor(); ~SubscribeEditor();
QPair<QString, CONFIGROOT> GetSelectedConfig();
private slots: private slots:
void on_addSubsButton_clicked(); void on_addSubsButton_clicked();
@ -29,6 +30,8 @@ class SubscribeEditor : public QDialog, private Ui::w_SubscribeEditor
void on_updateIntervalSB_valueChanged(double arg1); void on_updateIntervalSB_valueChanged(double arg1);
void on_connectionsList_itemClicked(QListWidgetItem *item);
private: private:
void StartUpdateSubscription(const QString &subscriptionName); void StartUpdateSubscription(const QString &subscriptionName);
void SaveConfig(); void SaveConfig();
@ -36,6 +39,7 @@ class SubscribeEditor : public QDialog, private Ui::w_SubscribeEditor
bool isUpdateInProgress = false; bool isUpdateInProgress = false;
QvHttpRequestHelper helper; QvHttpRequestHelper helper;
QPair<QString, CONFIGROOT> currentSelectedConfig;
QMap<QString, Qv2raySubscriptionConfig> subscriptions; QMap<QString, Qv2raySubscriptionConfig> subscriptions;
QString currentSubName; QString currentSubName;
}; };

View File

@ -5,10 +5,15 @@
// Forwarded from QvTinyLog // Forwarded from QvTinyLog
static QQueue<QString> __loggerBuffer; 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; 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); __loggerBuffer.enqueue(logString + NEWLINE);
} }
@ -17,7 +22,11 @@ const QString readLastLog()
QString result; QString result;
while (!__loggerBuffer.isEmpty()) { while (!__loggerBuffer.isEmpty()) {
result += __loggerBuffer.dequeue(); auto str = __loggerBuffer.dequeue();
if (!str.trimmed().isEmpty()) {
result += str;
}
} }
return result; return result;
@ -42,36 +51,6 @@ namespace Qv2ray
return randomString; 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) QString StringFromFile(QFile *source)
{ {
source->open(QFile::ReadOnly); source->open(QFile::ReadOnly);
@ -170,14 +149,20 @@ namespace Qv2ray
return GetFileList(dir).contains(fileName); 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); 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) 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) QString FormatBytes(long long bytes)

View File

@ -5,6 +5,8 @@
#include <QMessageBox> #include <QMessageBox>
#include <QUuid> #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 Qv2ray
{ {
namespace Utils namespace Utils
@ -17,16 +19,19 @@ namespace Qv2ray
list<string> SplitLines_std(const QString &_string); list<string> SplitLines_std(const QString &_string);
bool FileExistsIn(QDir dir, QString fileName); bool FileExistsIn(QDir dir, QString fileName);
const QString GenerateRandomString(int len = 12); 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); int QvMessageBoxAsk(QWidget *parent, QString title, QString text, QMessageBox::StandardButton extraButtons = QMessageBox::NoButton);
//
QString StringFromFile(QFile *source); QString StringFromFile(QFile *source);
bool StringToFile(const QString *text, QFile *target); bool StringToFile(const QString *text, QFile *target);
QJsonObject JsonFromString(QString string); QJsonObject JsonFromString(QString string);
QString JsonToString(QJsonObject json, QJsonDocument::JsonFormat format = QJsonDocument::JsonFormat::Indented); QString JsonToString(QJsonObject json, QJsonDocument::JsonFormat format = QJsonDocument::JsonFormat::Indented);
QString JsonToString(QJsonArray array, QJsonDocument::JsonFormat format = QJsonDocument::JsonFormat::Indented); QString JsonToString(QJsonArray array, QJsonDocument::JsonFormat format = QJsonDocument::JsonFormat::Indented);
QString VerifyJsonString(const QString &source); QString VerifyJsonString(const QString &source);
QString Stringify(list<string> list, QString saperator = ";"); //QString Stringify(list<string> list, QString saperator = ";");
QString Stringify(QList<QString> list, QString saperator = ";"); //QString Stringify(QList<QString> list, QString saperator = ";");
QString FormatBytes(long long bytes); QString FormatBytes(long long bytes);
void DeducePossibleFileName(const QString &baseDir, QString *fileName, const QString &extension); void DeducePossibleFileName(const QString &baseDir, QString *fileName, const QString &extension);
QString ConvertGFWToPAC(const QString &rawContent, const QString &customProxyString); QString ConvertGFWToPAC(const QString &rawContent, const QString &customProxyString);
@ -69,6 +74,11 @@ namespace Qv2ray
return QRegExp(R"([\/\\\"?%*:|><]|(^\.{1,2}$))").indexIn(str) == -1; 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. // These functions a sugers to prevent warnings from deprecated Qt 5.14 functions.
template<typename TYPE> template<typename TYPE>
std::list<TYPE> toStdList(QList<TYPE> list) std::list<TYPE> toStdList(QList<TYPE> list)
@ -142,13 +152,13 @@ namespace Qv2ray
return it != listOfElements.end(); return it != listOfElements.end();
} }
inline std::string timeToString(const time_t &t) inline QString timeToString(const time_t &t)
{ {
auto _tm = std::localtime(&t); auto _tm = std::localtime(&t);
char MY_TIME[128]; char MY_TIME[128];
// using strftime to display time // using strftime to display time
strftime(MY_TIME, sizeof(MY_TIME), "%x - %I:%M%p", _tm); 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. * 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(); const QString readLastLog();
#ifdef QV2RAY_LOG_WITH_FUNCTION_NAME #define QV2RAY_LOG_NORMAL 0
# define _QV2RAY_LOG_FUNCSTR __PRETTY_FUNCTION__ #define QV2RAY_LOG_DEBUG 1
#else
# define _QV2RAY_LOG_FUNCSTR ""
#endif
#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 LOG(MODULE, MSG) __LOG_IMPL(QV2RAY_LOG_NORMAL, (MODULE), (MSG));
#define DEBUG(module, msg) _LOG(__PRETTY_FUNCTION__, module, msg); #define DEBUG(MODULE, MSG) __LOG_IMPL(QV2RAY_LOG_DEBUG, (MODULE), (MSG));
#else
#define DEBUG(module, msg)
#endif
// Log modules used by Qv2ray // Log modules used by Qv2ray
#define MODULE_INIT "INIT" #define MODULE_INIT "INIT"

File diff suppressed because it is too large Load Diff