From f3727d1352d3159608f51c729aad9455a44d0445 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Thu, 19 Mar 2020 23:15:38 +0800 Subject: [PATCH 01/92] add: added plugin interface stub --- .gitmodules | 3 +++ src/components/plugins/host | 1 + 2 files changed, 4 insertions(+) create mode 160000 src/components/plugins/host diff --git a/.gitmodules b/.gitmodules index 529abebf..8039cc4a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -19,3 +19,6 @@ [submodule "libs/puresource"] path = libs/puresource url = https://github.com/Qv2ray/PureSource/ +[submodule "src/components/plugins/host"] + path = src/components/plugins/host + url = https://github.com/Qv2ray/QvPlugin-Host/ diff --git a/src/components/plugins/host b/src/components/plugins/host new file mode 160000 index 00000000..7d127d14 --- /dev/null +++ b/src/components/plugins/host @@ -0,0 +1 @@ +Subproject commit 7d127d14d9c4868ad8bc70527f6a342e417be73c From 5b3a7658bcbbb7720dbb52049b3d1141d9ae227e Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Fri, 20 Mar 2020 00:42:08 +0800 Subject: [PATCH 02/92] add: implement loading plugins --- .gitmodules | 4 +- CMakeLists.txt | 3 ++ makespec/BUILDVERSION | 2 +- src/components/plugins/QvPluginHost.cpp | 57 +++++++++++++++++++++++++ src/components/plugins/QvPluginHost.hpp | 15 +++++++ src/components/plugins/host | 1 - src/components/plugins/interface | 1 + 7 files changed, 79 insertions(+), 4 deletions(-) create mode 100644 src/components/plugins/QvPluginHost.cpp create mode 100644 src/components/plugins/QvPluginHost.hpp delete mode 160000 src/components/plugins/host create mode 160000 src/components/plugins/interface diff --git a/.gitmodules b/.gitmodules index 8039cc4a..98fd45d7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -19,6 +19,6 @@ [submodule "libs/puresource"] path = libs/puresource url = https://github.com/Qv2ray/PureSource/ -[submodule "src/components/plugins/host"] - path = src/components/plugins/host +[submodule "src/components/plugins/interface"] + path = src/components/plugins/interface url = https://github.com/Qv2ray/QvPlugin-Host/ diff --git a/CMakeLists.txt b/CMakeLists.txt index e3f7112d..ca66834c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -158,6 +158,7 @@ set(QV2RAY_SOURCES src/components/plugins/toolbar/QvToolbar.cpp src/components/plugins/toolbar/QvToolbar_linux.cpp src/components/plugins/toolbar/QvToolbar_win.cpp + src/components/plugins/QvPluginHost.cpp src/components/proxy/QvProxyConfigurator.cpp src/components/speedchart/speedplotview.cpp src/components/speedchart/speedwidget.cpp @@ -235,6 +236,8 @@ set(QV2RAY_SOURCES src/components/icmping/win/ICMPPinger.hpp src/components/pac/QvPACHandler.hpp src/components/plugins/toolbar/QvToolbar.hpp + src/components/plugins/interface/QvPluginInterface.hpp + src/components/plugins/QvPluginHost.hpp src/components/proxy/QvProxyConfigurator.hpp src/components/speedchart/speedplotview.hpp src/components/speedchart/speedwidget.hpp diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 25fcdaca..a7df46a4 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -4737 \ No newline at end of file +4743 \ No newline at end of file diff --git a/src/components/plugins/QvPluginHost.cpp b/src/components/plugins/QvPluginHost.cpp new file mode 100644 index 00000000..5e327db0 --- /dev/null +++ b/src/components/plugins/QvPluginHost.cpp @@ -0,0 +1,57 @@ +#include "QvPluginHost.hpp" + +#include "base/Qv2rayLog.hpp" +#include "components/plugins/interface/QvPluginInterface.hpp" + +#include +#include +#include +#include +using namespace Qv2ray::base; +namespace Qv2ray::components::plugins +{ + QvPluginHost::QvPluginHost(QObject *parent) : QObject(parent) + { + LoadPlugin(); + } + bool QvPluginHost::LoadPlugin() + { + QDir pluginsDir(QCoreApplication::applicationDirPath()); +#if defined(Q_OS_WIN) + if (pluginsDir.dirName().toLower() == "debug" || pluginsDir.dirName().toLower() == "release") + pluginsDir.cdUp(); +#elif defined(Q_OS_MAC) + if (pluginsDir.dirName() == "MacOS") + { + pluginsDir.cdUp(); + pluginsDir.cdUp(); + pluginsDir.cdUp(); + } +#endif + pluginsDir.cd("plugins"); + const QStringList entries = pluginsDir.entryList(QDir::Files); + for (const QString &fileName : entries) + { + QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName)); + QObject *plugin = pluginLoader.instance(); + if (plugin) + { + auto echoInterface = qobject_cast(plugin); + + // + if (echoInterface) + { + QString name = echoInterface->Name(); + QString author = echoInterface->Author(); + // + std::cout << name.toStdString() << std::endl; + std::cout << author.toStdString() << std::endl; + return true; + } + pluginLoader.unload(); + } + } + + return false; + } +} // namespace Qv2ray::components::plugins diff --git a/src/components/plugins/QvPluginHost.hpp b/src/components/plugins/QvPluginHost.hpp new file mode 100644 index 00000000..3e7294ac --- /dev/null +++ b/src/components/plugins/QvPluginHost.hpp @@ -0,0 +1,15 @@ +#pragma once +#include + +namespace Qv2ray::components::plugins +{ + class QvPluginHost : public QObject + { + Q_OBJECT + public: + explicit QvPluginHost(QObject *parent = nullptr); + bool LoadPlugin(); + }; + + inline QvPluginHost pluginHost; +} // namespace Qv2ray::components::plugins diff --git a/src/components/plugins/host b/src/components/plugins/host deleted file mode 160000 index 7d127d14..00000000 --- a/src/components/plugins/host +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 7d127d14d9c4868ad8bc70527f6a342e417be73c diff --git a/src/components/plugins/interface b/src/components/plugins/interface new file mode 160000 index 00000000..9f4d5580 --- /dev/null +++ b/src/components/plugins/interface @@ -0,0 +1 @@ +Subproject commit 9f4d558076f0c40f45a2fcab17c0ee35cb7b4ace From 57f8e5df313a76df42fb56f9d200fdbc4ee587db Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Fri, 20 Mar 2020 22:04:42 +0800 Subject: [PATCH 03/92] update: updated plugin interface submodule --- makespec/BUILDVERSION | 2 +- src/components/plugins/interface | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index a7df46a4..71009c06 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -4743 \ No newline at end of file +4745 \ No newline at end of file diff --git a/src/components/plugins/interface b/src/components/plugins/interface index 9f4d5580..b743c8a3 160000 --- a/src/components/plugins/interface +++ b/src/components/plugins/interface @@ -1 +1 @@ -Subproject commit 9f4d558076f0c40f45a2fcab17c0ee35cb7b4ace +Subproject commit b743c8a3b9c97f2dc605917d929261a9f31746aa From b3f8f4a4824cfb6d70fe2784191e07c1ab0182b3 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Sun, 22 Mar 2020 01:44:36 +0800 Subject: [PATCH 04/92] update interface, made the demo plugin work --- makespec/BUILDVERSION | 2 +- src/components/plugins/QvPluginHost.cpp | 11 ++++++----- src/components/plugins/QvPluginHost.hpp | 2 -- src/components/plugins/interface | 2 +- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 71009c06..176f918e 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -4745 \ No newline at end of file +4747 \ No newline at end of file diff --git a/src/components/plugins/QvPluginHost.cpp b/src/components/plugins/QvPluginHost.cpp index 5e327db0..95a509be 100644 --- a/src/components/plugins/QvPluginHost.cpp +++ b/src/components/plugins/QvPluginHost.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include using namespace Qv2ray::base; @@ -37,19 +38,19 @@ namespace Qv2ray::components::plugins if (plugin) { auto echoInterface = qobject_cast(plugin); - - // if (echoInterface) { QString name = echoInterface->Name(); QString author = echoInterface->Author(); - // - std::cout << name.toStdString() << std::endl; - std::cout << author.toStdString() << std::endl; + LOG(MODULE_PLUGIN, "Name: " + name) return true; } pluginLoader.unload(); } + else + { + LOG(MODULE_PLUGIN, pluginLoader.errorString()); + } } return false; diff --git a/src/components/plugins/QvPluginHost.hpp b/src/components/plugins/QvPluginHost.hpp index 3e7294ac..7a118c00 100644 --- a/src/components/plugins/QvPluginHost.hpp +++ b/src/components/plugins/QvPluginHost.hpp @@ -10,6 +10,4 @@ namespace Qv2ray::components::plugins explicit QvPluginHost(QObject *parent = nullptr); bool LoadPlugin(); }; - - inline QvPluginHost pluginHost; } // namespace Qv2ray::components::plugins diff --git a/src/components/plugins/interface b/src/components/plugins/interface index b743c8a3..3e0feac4 160000 --- a/src/components/plugins/interface +++ b/src/components/plugins/interface @@ -1 +1 @@ -Subproject commit b743c8a3b9c97f2dc605917d929261a9f31746aa +Subproject commit 3e0feac4d9717eab58f00642d615464c99d6ca7f From 1d2f81088cdccbc7404c14cc33972422bd0fc825 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Sun, 22 Mar 2020 17:17:47 +0800 Subject: [PATCH 05/92] add: added plugin management window, load plugin support --- CMakeLists.txt | 3 + makespec/BUILDVERSION | 2 +- src/base/Qv2rayBase.hpp | 23 +++ src/base/models/QvSettingsObject.hpp | 14 +- src/common/QvTranslator.cpp | 21 +-- src/components/plugins/QvPluginHost.cpp | 118 +++++++++------ src/components/plugins/QvPluginHost.hpp | 31 +++- src/components/plugins/interface | 2 +- src/main.cpp | 5 +- src/ui/w_MainWindow.cpp | 6 + src/ui/w_MainWindow.hpp | 2 + src/ui/w_MainWindow.ui | 10 +- src/ui/w_PluginManager.cpp | 39 +++++ src/ui/w_PluginManager.hpp | 19 +++ src/ui/w_PluginManager.ui | 181 ++++++++++++++++++++++++ 15 files changed, 402 insertions(+), 74 deletions(-) create mode 100644 src/ui/w_PluginManager.cpp create mode 100644 src/ui/w_PluginManager.hpp create mode 100644 src/ui/w_PluginManager.ui diff --git a/CMakeLists.txt b/CMakeLists.txt index ca66834c..87c4a0cb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -196,6 +196,7 @@ set(QV2RAY_SOURCES src/ui/w_MainWindow.cpp src/ui/w_MainWindow_extra.cpp src/ui/w_PreferencesWindow.cpp + src/ui/w_PluginManager.cpp src/ui/w_ScreenShot_Core.cpp src/ui/w_SubscriptionManager.cpp # ui files @@ -211,6 +212,7 @@ set(QV2RAY_SOURCES src/ui/widgets/RouteSettingsMatrix.ui src/ui/w_MainWindow.ui src/ui/w_PreferencesWindow.ui + src/ui/w_PluginManager.ui src/ui/w_ScreenShot_Core.ui # headers src/base/JsonHelpers.hpp @@ -267,6 +269,7 @@ set(QV2RAY_SOURCES src/ui/w_ImportConfig.hpp src/ui/w_MainWindow.hpp src/ui/w_PreferencesWindow.hpp + src/ui/w_PluginManager.hpp src/ui/w_ScreenShot_Core.hpp src/ui/w_SubscriptionManager.hpp assets/qv2ray.rc diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 176f918e..b072e3cf 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -4747 \ No newline at end of file +4752 \ No newline at end of file diff --git a/src/base/Qv2rayBase.hpp b/src/base/Qv2rayBase.hpp index 66814bbb..f92c464a 100644 --- a/src/base/Qv2rayBase.hpp +++ b/src/base/Qv2rayBase.hpp @@ -46,6 +46,7 @@ using namespace Qv2ray::base::objects::transfer; #define QV2RAY_CONFIG_FILE (QV2RAY_CONFIG_DIR + "Qv2ray.conf") #define QV2RAY_CONNECTIONS_DIR (QV2RAY_CONFIG_DIR + "connections/") #define QV2RAY_SUBSCRIPTION_DIR (QV2RAY_CONFIG_DIR + "subscriptions/") +#define QV2RAY_PLUGIN_SETTINGS_DIR (QV2RAY_CONFIG_DIR + "plugin_settings/") // Get GFWList and PAC file path. #define QV2RAY_RULES_DIR (QV2RAY_CONFIG_DIR + "rules/") @@ -127,4 +128,26 @@ namespace Qv2ray isExiting = true; QApplication::quit(); } + + inline QStringList Qv2rayAssetsPaths(const QString &dirName) + { + // Configuration Path + QStringList list; + list << QV2RAY_CONFIG_DIR + dirName; + // +#ifdef Q_OS_LINUX + // Linux platform directories. + list << QString("/usr/share/qv2ray/" + dirName); + list << QString("/usr/local/share/qv2ray/" + dirName); + list << QStandardPaths::locateAll(QStandardPaths::AppDataLocation, dirName, QStandardPaths::LocateDirectory); + list << QStandardPaths::locateAll(QStandardPaths::AppConfigLocation, dirName, QStandardPaths::LocateDirectory); +#elif defined(Q_OS_MAC) + // macOS platform directories. + list << QDir(QApplication::applicationDirPath() + "/../Resources/" + dirName).absolutePath(); +#endif + // This is the default behavior on Windows + list << QApplication::applicationDirPath() + "/" + dirName; + return list; + }; + } // namespace Qv2ray diff --git a/src/base/models/QvSettingsObject.hpp b/src/base/models/QvSettingsObject.hpp index 0e86cc91..9bef4ba9 100644 --- a/src/base/models/QvSettingsObject.hpp +++ b/src/base/models/QvSettingsObject.hpp @@ -126,6 +126,12 @@ namespace Qv2ray::base::config XTOSTRUCT(O(domains, ips)) }; + struct Qv2rayPluginConfig + { + QMap pluginStates; + XTOSTRUCT(O(pluginStates)) + }; + struct Qv2rayConnectionConfig { bool bypassCN; @@ -209,6 +215,7 @@ namespace Qv2ray::base::config // Qv2rayUIConfig uiConfig; Qv2rayAPIConfig apiConfig; + Qv2rayPluginConfig pluginConfig; Qv2rayKernelConfig kernelConfig; Qv2rayToolBarConfig toolBarConfig; Qv2rayInboundsConfig inboundConfig; @@ -216,11 +223,12 @@ namespace Qv2ray::base::config Qv2rayConfig() : config_version(QV2RAY_CONFIG_VERSION), tProxySupport(false), logLevel(), ignoredVersion(), autoStartId("null"), groups(), - subscriptions(), connections(), uiConfig(), apiConfig(), kernelConfig(), toolBarConfig(), inboundConfig(), connectionConfig() + subscriptions(), connections(), uiConfig(), apiConfig(), pluginConfig(), kernelConfig(), toolBarConfig(), inboundConfig(), + connectionConfig() { } - XTOSTRUCT(O(config_version, ignoredVersion, tProxySupport, logLevel, uiConfig, kernelConfig, groups, connections, subscriptions, - autoStartId, inboundConfig, connectionConfig, toolBarConfig, apiConfig)) + XTOSTRUCT(O(config_version, ignoredVersion, tProxySupport, logLevel, uiConfig, pluginConfig, kernelConfig, groups, connections, + subscriptions, autoStartId, inboundConfig, connectionConfig, toolBarConfig, apiConfig)) }; } // namespace Qv2ray::base::config diff --git a/src/common/QvTranslator.cpp b/src/common/QvTranslator.cpp index 25057b92..2c5f5c63 100644 --- a/src/common/QvTranslator.cpp +++ b/src/common/QvTranslator.cpp @@ -17,33 +17,14 @@ using namespace Qv2ray::base; QStringList getLanguageSearchPaths() { // Configuration Path - QStringList list; - list << QV2RAY_CONFIG_DIR + "lang"; -// + QStringList list = Qv2rayAssetsPaths("lang"); #ifdef EMBED_TRANSLATIONS // If the translations have been embedded. list << QString(":/translations/"); #endif - // - // #ifdef QV2RAY_TRANSLATION_PATH // Platform-specific dir, if specified. list << QString(QV2RAY_TRANSLATION_PATH); -#endif - // - // -#ifdef Q_OS_LINUX - // Linux platform directories. - list << QString("/usr/share/qv2ray/lang/"); - list << QString("/usr/local/share/qv2ray/lang/"); - list << QStandardPaths::locateAll(QStandardPaths::AppDataLocation, "lang", QStandardPaths::LocateDirectory); - list << QStandardPaths::locateAll(QStandardPaths::AppConfigLocation, "lang", QStandardPaths::LocateDirectory); -#elif defined(Q_OS_MAC) - // macOS platform directories. - list << QDir(QApplication::applicationDirPath() + "/../Resources/lang").absolutePath(); -#else - // This is the default behavior on Windows - list << QApplication::applicationDirPath() + "/lang"; #endif return list; }; diff --git a/src/components/plugins/QvPluginHost.cpp b/src/components/plugins/QvPluginHost.cpp index 95a509be..e5b7b785 100644 --- a/src/components/plugins/QvPluginHost.cpp +++ b/src/components/plugins/QvPluginHost.cpp @@ -1,58 +1,86 @@ #include "QvPluginHost.hpp" +#include "base/Qv2rayBase.hpp" #include "base/Qv2rayLog.hpp" -#include "components/plugins/interface/QvPluginInterface.hpp" -#include -#include -#include #include -#include -using namespace Qv2ray::base; namespace Qv2ray::components::plugins { QvPluginHost::QvPluginHost(QObject *parent) : QObject(parent) { - LoadPlugin(); + RefreshPluginList(); } - bool QvPluginHost::LoadPlugin() - { - QDir pluginsDir(QCoreApplication::applicationDirPath()); -#if defined(Q_OS_WIN) - if (pluginsDir.dirName().toLower() == "debug" || pluginsDir.dirName().toLower() == "release") - pluginsDir.cdUp(); -#elif defined(Q_OS_MAC) - if (pluginsDir.dirName() == "MacOS") - { - pluginsDir.cdUp(); - pluginsDir.cdUp(); - pluginsDir.cdUp(); - } -#endif - pluginsDir.cd("plugins"); - const QStringList entries = pluginsDir.entryList(QDir::Files); - for (const QString &fileName : entries) - { - QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName)); - QObject *plugin = pluginLoader.instance(); - if (plugin) - { - auto echoInterface = qobject_cast(plugin); - if (echoInterface) - { - QString name = echoInterface->Name(); - QString author = echoInterface->Author(); - LOG(MODULE_PLUGIN, "Name: " + name) - return true; - } - pluginLoader.unload(); - } - else - { - LOG(MODULE_PLUGIN, pluginLoader.errorString()); - } - } - return false; + int QvPluginHost::RefreshPluginList() + { + ClearPlugins(); + for (const auto &pluginDirPath : Qv2rayAssetsPaths("plugins")) + { + const QStringList entries = QDir(pluginDirPath).entryList(QDir::Files); + for (const auto &fileName : entries) + { + DEBUG(MODULE_PLUGIN, "Loading plugin: " + fileName + " from: " + pluginDirPath) + // + QvPluginInfo info; + auto pluginFullPath = QDir(pluginDirPath).absoluteFilePath(fileName); + info.libraryPath = pluginFullPath; + info.pluginLoader = new QPluginLoader(pluginFullPath, this); + // auto meta = pluginLoader.metaData(); + // You should not call "delete" on this object, it's handled by the QPluginLoader + // + QObject *plugin = info.pluginLoader->instance(); + if (plugin == nullptr) + { + LOG(MODULE_PLUGIN, info.pluginLoader->errorString()); + continue; + } + // + info.interface = qobject_cast(plugin); + if (info.interface == nullptr) + { + LOG(MODULE_PLUGIN, "Failed to cast from QObject to Qv2rayPluginInterface") + info.pluginLoader->unload(); + continue; + } + // + if (plugins.contains(info.interface->InternalName())) + { + LOG(MODULE_PLUGIN, "Found another plugin with the same internal name: " + info.interface->InternalName() + ". Skipped") + continue; + } + // + if (info.interface->QvPluginInterfaceVersion != QV2RAY_PLUGIN_INTERFACE_VERSION) + { + // The plugin was built for a not-compactable version of Qv2ray. Don't load the plugin by default. + LOG(MODULE_PLUGIN, "The plugin " + info.interface->InternalName() + + " is not loaded since it was built against a different version of interface") + info.errorMessage = tr("This plugin was built against an incompactable version of Qv2ray Plugin Interface.") // + + NEWLINE + tr("Please contact the plugin provider or report the issue to Qv2ray Workgroup."); // + } + // + LOG(MODULE_PLUGIN, "Loaded plugin: \"" + info.interface->Name() + "\" made by: \"" + info.interface->Author() + "\"") + plugins.insert(info.interface->InternalName(), info); + } + } + return plugins.count(); } + + QStringList QvPluginHost::AvailablePlugins() + { + return plugins.keys(); + } + QvPluginHost::~QvPluginHost() + { + } + void QvPluginHost::ClearPlugins() + { + for (auto &&plugin : plugins) + { + DEBUG(MODULE_PLUGIN, "Unloading: \"" + plugin.interface->Name() + "\"") + plugin.pluginLoader->unload(); + plugin.pluginLoader->deleteLater(); + } + plugins.clear(); + } + } // namespace Qv2ray::components::plugins diff --git a/src/components/plugins/QvPluginHost.hpp b/src/components/plugins/QvPluginHost.hpp index 7a118c00..af4be288 100644 --- a/src/components/plugins/QvPluginHost.hpp +++ b/src/components/plugins/QvPluginHost.hpp @@ -1,13 +1,42 @@ #pragma once +#include "components/plugins/interface/QvPluginInterface.hpp" + +#include #include +#include + +class QPluginLoader; namespace Qv2ray::components::plugins { + struct QvPluginInfo + { + QString libraryPath; + QString errorMessage; + QPluginLoader *pluginLoader; + Qv2rayInterface *interface; + }; + class QvPluginHost : public QObject { Q_OBJECT public: explicit QvPluginHost(QObject *parent = nullptr); - bool LoadPlugin(); + ~QvPluginHost(); + int RefreshPluginList(); + QStringList AvailablePlugins(); + const inline QvPluginInfo GetPluginInfo(const QString &internalName) + { + return plugins.value(internalName); + } + + private: + // Internal name, plugin info + QMap plugins; + void ClearPlugins(); }; + + inline ::Qv2ray::components::plugins::QvPluginHost *PluginHost = nullptr; } // namespace Qv2ray::components::plugins + +using namespace Qv2ray::components::plugins; diff --git a/src/components/plugins/interface b/src/components/plugins/interface index 3e0feac4..4e566a1b 160000 --- a/src/components/plugins/interface +++ b/src/components/plugins/interface @@ -1 +1 @@ -Subproject commit 3e0feac4d9717eab58f00642d615464c99d6ca7f +Subproject commit 4e566a1b019e60e649031f963109516076e20252 diff --git a/src/main.cpp b/src/main.cpp index b56a7427..b9841747 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,6 +4,7 @@ #include "common/QvTranslator.hpp" #include "core/handler/ConfigHandler.hpp" #include "core/settings/SettingsBackend.hpp" +#include "src/components/plugins/QvPluginHost.hpp" #include "ui/w_MainWindow.hpp" #include @@ -401,8 +402,8 @@ int main(int argc, char *argv[]) if (themes.contains(confObject.uiConfig.theme)) { - qApp->setStyle(confObject.uiConfig.theme); LOG(MODULE_INIT + " " + MODULE_UI, "Setting Qv2ray UI themes: " + confObject.uiConfig.theme) + qApp->setStyle(confObject.uiConfig.theme); } #endif @@ -414,6 +415,7 @@ int main(int argc, char *argv[]) //_qApp.setAttribute(Qt::AA_DontUseNativeMenuBar); // Initialise Connection Handler ConnectionManager = new QvConfigHandler(); + PluginHost = new QvPluginHost(); // Handler for session logout, shutdown, etc. // Will not block. QGuiApplication::setFallbackSessionManagementEnabled(false); @@ -431,6 +433,7 @@ int main(int argc, char *argv[]) signal(SIGUSR2, [](int) { emit MainWindow::mwInstance->StopConnection(); }); #endif auto rcode = _qApp.exec(); + delete PluginHost; delete ConnectionManager; LOG(MODULE_INIT, "Quitting normally") return rcode; diff --git a/src/ui/w_MainWindow.cpp b/src/ui/w_MainWindow.cpp index ad971c14..f969a3f4 100644 --- a/src/ui/w_MainWindow.cpp +++ b/src/ui/w_MainWindow.cpp @@ -8,6 +8,7 @@ #include "ui/editors/w_OutboundEditor.hpp" #include "ui/editors/w_RoutesEditor.hpp" #include "ui/w_ImportConfig.hpp" +#include "ui/w_PluginManager.hpp" #include "ui/w_PreferencesWindow.hpp" #include "ui/w_SubscriptionManager.hpp" #include "ui/widgets/ConnectionInfoWidget.hpp" @@ -994,3 +995,8 @@ void MainWindow::on_action_RCM_ClearUsage_triggered() ConnectionManager->ClearConnectionUsage(get<1>(widget->Identifier())); } } + +void MainWindow::on_pluginsBtn_clicked() +{ + w_PluginManager(this).exec(); +} diff --git a/src/ui/w_MainWindow.hpp b/src/ui/w_MainWindow.hpp index 40978636..23c53046 100644 --- a/src/ui/w_MainWindow.hpp +++ b/src/ui/w_MainWindow.hpp @@ -109,6 +109,8 @@ class MainWindow void on_masterLogBrowser_textChanged(); + void on_pluginsBtn_clicked(); + protected: void timerEvent(QTimerEvent *event) override; diff --git a/src/ui/w_MainWindow.ui b/src/ui/w_MainWindow.ui index 6203d962..be5e7f4a 100644 --- a/src/ui/w_MainWindow.ui +++ b/src/ui/w_MainWindow.ui @@ -25,7 +25,7 @@ - + 5 @@ -36,6 +36,13 @@ + + + + Plugins + + + @@ -543,7 +550,6 @@ subsButton - preferencesBtn connectionListWidget importConfigButton diff --git a/src/ui/w_PluginManager.cpp b/src/ui/w_PluginManager.cpp new file mode 100644 index 00000000..ad79b253 --- /dev/null +++ b/src/ui/w_PluginManager.cpp @@ -0,0 +1,39 @@ +#include "w_PluginManager.hpp" + +#include "components/plugins/QvPluginHost.hpp" + +w_PluginManager::w_PluginManager(QWidget *parent) : QDialog(parent) +{ + setupUi(this); + for (auto &plugin : PluginHost->AvailablePlugins()) + { + auto item = new QListWidgetItem(pluginListWidget); + item->setCheckState(Qt::CheckState::Unchecked); + item->setData(Qt::UserRole, PluginHost->GetPluginInfo(plugin).interface->InternalName()); + item->setText(PluginHost->GetPluginInfo(plugin).interface->Name()); + pluginListWidget->addItem(item); + } +} + +w_PluginManager::~w_PluginManager() +{ +} + +void w_PluginManager::on_pluginListWidget_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous) +{ + Q_UNUSED(previous) + auto &info = PluginHost->GetPluginInfo(current->data(Qt::UserRole).toString()); + if (info.interface != nullptr) + { + pluginNameLabel->setText(info.interface->Name()); + pluginAuthorLabel->setText(info.interface->Author()); + pluginDescriptionLabel->setText(info.interface->Description()); + pluginIconLabel->setPixmap(info.interface->Icon().pixmap(pluginIconLabel->size() * devicePixelRatio())); + pluginTypeLabel->setText("No impl"); + } +} + +void w_PluginManager::on_pluginListWidget_itemClicked(QListWidgetItem *item) +{ + on_pluginListWidget_currentItemChanged(item, nullptr); +} diff --git a/src/ui/w_PluginManager.hpp b/src/ui/w_PluginManager.hpp new file mode 100644 index 00000000..ccb27a7b --- /dev/null +++ b/src/ui/w_PluginManager.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include "ui_w_PluginManager.h" + +#include + +class w_PluginManager + : public QDialog + , private Ui::w_PluginManager +{ + Q_OBJECT + + public: + explicit w_PluginManager(QWidget *parent = nullptr); + ~w_PluginManager(); + private slots: + void on_pluginListWidget_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous); + void on_pluginListWidget_itemClicked(QListWidgetItem *item); +}; diff --git a/src/ui/w_PluginManager.ui b/src/ui/w_PluginManager.ui new file mode 100644 index 00000000..3297a0ab --- /dev/null +++ b/src/ui/w_PluginManager.ui @@ -0,0 +1,181 @@ + + + w_PluginManager + + + + 0 + 0 + 687 + 492 + + + + Plugin Manager + + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + Plugins + + + + + + + Plugin Info + + + + + + + + + 64 + 64 + + + + ICON! + + + Qt::AlignCenter + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + Name + + + + + + + + + + + + + + Author + + + + + + + + + + + + + + Description + + + + + + + + + + + + + + Type + + + + + + + + + + + + + + + + + + + + + buttonBox + accepted() + w_PluginManager + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + w_PluginManager + reject() + + + 316 + 260 + + + 286 + 274 + + + + + From 48d3472faf7d8f79282eda23252799e089576eb6 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Sun, 22 Mar 2020 18:27:58 +0800 Subject: [PATCH 06/92] merge: merge dev --- src/base/Qv2rayLog.hpp | 3 +- src/base/models/QvStartupConfig.hpp | 2 + src/common/CommandArgs.cpp | 8 +++ src/common/CommandArgs.hpp | 1 + src/components/plugins/QvPluginHost.cpp | 43 +++++++++----- src/components/plugins/QvPluginHost.hpp | 5 ++ .../plugins/toolbar/QvToolbar_linux.cpp | 8 +-- src/ui/w_MainWindow.cpp | 6 ++ src/ui/w_MainWindow.ui | 3 - src/ui/w_PluginManager.cpp | 3 +- src/ui/w_PluginManager.ui | 59 ++++++++++++++++--- 11 files changed, 107 insertions(+), 34 deletions(-) diff --git a/src/base/Qv2rayLog.hpp b/src/base/Qv2rayLog.hpp index 28e2a1bf..c01499c0 100644 --- a/src/base/Qv2rayLog.hpp +++ b/src/base/Qv2rayLog.hpp @@ -41,6 +41,7 @@ const inline QString MODULE_FILEIO = "COMMON-FILEIO"; // const inline QString MODULE_PROXY = "COMPONENT-PROXY"; const inline QString MODULE_UPDATE = "COMPONENT-UPDATE"; -const inline QString MODULE_PLUGIN = "COMPONENT-PLUGIN"; +const inline QString MODULE_PLUGINHOST = "COMPONENT-PLUGINHOST"; +const inline QString MODULE_PLUGINCLIENT = "PLUGIN-CLIENT"; // ================================================================ const inline QString MODULE_CORE_HANDLER = "QV2RAY-CORE"; diff --git a/src/base/models/QvStartupConfig.hpp b/src/base/models/QvStartupConfig.hpp index 258a1631..8f88f79f 100644 --- a/src/base/models/QvStartupConfig.hpp +++ b/src/base/models/QvStartupConfig.hpp @@ -16,6 +16,8 @@ namespace Qv2ray bool enableToolbarPlguin; /// Disable Qt scale factors support. bool noScaleFactors; + /// Disable all plugin features. + bool noPlugins; }; } // namespace base inline base::QvStartupOptions StartupOption = base::QvStartupOptions(); diff --git a/src/common/CommandArgs.cpp b/src/common/CommandArgs.cpp index 2a2c8077..51f24736 100644 --- a/src/common/CommandArgs.cpp +++ b/src/common/CommandArgs.cpp @@ -9,6 +9,7 @@ namespace Qv2ray::common runAsRootOption("I-just-wanna-run-with-root", tr("Explicitly run Qv2ray as root.")), // debugOption("debug", tr("Enable Debug Output")), // noScaleFactorOption("noScaleFactor", tr("Disable manually set QT_SCALE_FACTOR")), // + noPluginsOption("noPlugin", tr("Disable plugin feature")), // withToolbarOption("withToolbarPlugin", tr("Enable Qv2ray network toolbar plugin")), // // helpOption("FAKE"), versionOption("FAKE") @@ -20,6 +21,7 @@ namespace Qv2ray::common parser.addOption(runAsRootOption); parser.addOption(debugOption); parser.addOption(noScaleFactorOption); + parser.addOption(noPluginsOption); parser.addOption(withToolbarOption); helpOption = parser.addHelpOption(); versionOption = parser.addVersionOption(); @@ -63,6 +65,12 @@ namespace Qv2ray::common StartupOption.noScaleFactors = true; } + if (parser.isSet(noPluginsOption)) + { + DEBUG(MODULE_INIT, "noPluginOption is set.") + StartupOption.noPlugins = true; + } + if (parser.isSet(withToolbarOption)) { DEBUG(MODULE_INIT, "withToolbarOption is set.") diff --git a/src/common/CommandArgs.hpp b/src/common/CommandArgs.hpp index 61483122..dbab70cc 100644 --- a/src/common/CommandArgs.hpp +++ b/src/common/CommandArgs.hpp @@ -28,6 +28,7 @@ namespace Qv2ray::common QCommandLineOption runAsRootOption; QCommandLineOption debugOption; QCommandLineOption noScaleFactorOption; + QCommandLineOption noPluginsOption; QCommandLineOption withToolbarOption; QCommandLineOption helpOption; QCommandLineOption versionOption; diff --git a/src/components/plugins/QvPluginHost.cpp b/src/components/plugins/QvPluginHost.cpp index e5b7b785..7d758127 100644 --- a/src/components/plugins/QvPluginHost.cpp +++ b/src/components/plugins/QvPluginHost.cpp @@ -6,20 +6,22 @@ #include namespace Qv2ray::components::plugins { + const QString errorMessageSuffix = NEWLINE + QObject::tr("Please contact the plugin provider or report the issue to Qv2ray Workgroup."); + QvPluginHost::QvPluginHost(QObject *parent) : QObject(parent) { - RefreshPluginList(); } int QvPluginHost::RefreshPluginList() { ClearPlugins(); + LOG(MODULE_PLUGINHOST, "Reloading plugin list") for (const auto &pluginDirPath : Qv2rayAssetsPaths("plugins")) { const QStringList entries = QDir(pluginDirPath).entryList(QDir::Files); for (const auto &fileName : entries) { - DEBUG(MODULE_PLUGIN, "Loading plugin: " + fileName + " from: " + pluginDirPath) + DEBUG(MODULE_PLUGINHOST, "Loading plugin: " + fileName + " from: " + pluginDirPath) // QvPluginInfo info; auto pluginFullPath = QDir(pluginDirPath).absoluteFilePath(fileName); @@ -27,44 +29,53 @@ namespace Qv2ray::components::plugins info.pluginLoader = new QPluginLoader(pluginFullPath, this); // auto meta = pluginLoader.metaData(); // You should not call "delete" on this object, it's handled by the QPluginLoader - // QObject *plugin = info.pluginLoader->instance(); if (plugin == nullptr) { - LOG(MODULE_PLUGIN, info.pluginLoader->errorString()); + LOG(MODULE_PLUGINHOST, info.pluginLoader->errorString()); continue; } - // info.interface = qobject_cast(plugin); if (info.interface == nullptr) { - LOG(MODULE_PLUGIN, "Failed to cast from QObject to Qv2rayPluginInterface") + // info.errorMessage = tr("Failed to cast from QObject to Qv2rayPluginInterface"); + LOG(MODULE_PLUGINHOST, "Failed to cast from QObject to Qv2rayPluginInterface") info.pluginLoader->unload(); continue; } - // if (plugins.contains(info.interface->InternalName())) { - LOG(MODULE_PLUGIN, "Found another plugin with the same internal name: " + info.interface->InternalName() + ". Skipped") + LOG(MODULE_PLUGINHOST, "Found another plugin with the same internal name: " + info.interface->InternalName() + ". Skipped") continue; } - // + if (info.interface->QvPluginInterfaceVersion != QV2RAY_PLUGIN_INTERFACE_VERSION) { // The plugin was built for a not-compactable version of Qv2ray. Don't load the plugin by default. - LOG(MODULE_PLUGIN, "The plugin " + info.interface->InternalName() + - " is not loaded since it was built against a different version of interface") - info.errorMessage = tr("This plugin was built against an incompactable version of Qv2ray Plugin Interface.") // - + NEWLINE + tr("Please contact the plugin provider or report the issue to Qv2ray Workgroup."); // + LOG(MODULE_PLUGINHOST, "The plugin " + info.interface->InternalName() + + " is not loaded since it was built against a different version of interface") + info.errorMessage = tr("This plugin was built against an incompactable version of Qv2ray Plugin Interface.") // + + errorMessageSuffix; // } - // - LOG(MODULE_PLUGIN, "Loaded plugin: \"" + info.interface->Name() + "\" made by: \"" + info.interface->Author() + "\"") + + connect(info.interface->GetQObject(), SIGNAL(PluginLog(const QString &)), this, SLOT(QvPluginLog(const QString &))); + LOG(MODULE_PLUGINHOST, "Loaded plugin: \"" + info.interface->Name() + "\" made by: \"" + info.interface->Author() + "\"") plugins.insert(info.interface->InternalName(), info); } } return plugins.count(); } + void QvPluginHost::QvPluginLog(const QString &log) + { + LOG(MODULE_PLUGINCLIENT, log) + } + + void QvPluginHost::InitializePluginHost() + { + RefreshPluginList(); + } + QStringList QvPluginHost::AvailablePlugins() { return plugins.keys(); @@ -76,7 +87,7 @@ namespace Qv2ray::components::plugins { for (auto &&plugin : plugins) { - DEBUG(MODULE_PLUGIN, "Unloading: \"" + plugin.interface->Name() + "\"") + DEBUG(MODULE_PLUGINHOST, "Unloading: \"" + plugin.interface->Name() + "\"") plugin.pluginLoader->unload(); plugin.pluginLoader->deleteLater(); } diff --git a/src/components/plugins/QvPluginHost.hpp b/src/components/plugins/QvPluginHost.hpp index af4be288..cc0ac296 100644 --- a/src/components/plugins/QvPluginHost.hpp +++ b/src/components/plugins/QvPluginHost.hpp @@ -11,6 +11,7 @@ namespace Qv2ray::components::plugins { struct QvPluginInfo { + bool canLoadPlugin; QString libraryPath; QString errorMessage; QPluginLoader *pluginLoader; @@ -25,11 +26,15 @@ namespace Qv2ray::components::plugins ~QvPluginHost(); int RefreshPluginList(); QStringList AvailablePlugins(); + void InitializePluginHost(); const inline QvPluginInfo GetPluginInfo(const QString &internalName) { return plugins.value(internalName); } + private slots: + void QvPluginLog(const QString &log); + private: // Internal name, plugin info QMap plugins; diff --git a/src/components/plugins/toolbar/QvToolbar_linux.cpp b/src/components/plugins/toolbar/QvToolbar_linux.cpp index 1fa19908..9fdd2a4b 100644 --- a/src/components/plugins/toolbar/QvToolbar_linux.cpp +++ b/src/components/plugins/toolbar/QvToolbar_linux.cpp @@ -43,7 +43,7 @@ namespace Qv2ray::components::plugins::Toolbar } catch (...) { - LOG(MODULE_PLUGIN, "Closing a broken socket.") + LOG(MODULE_PLUGINHOST, "Closing a broken socket.") } } void DataMessageQThread() @@ -66,8 +66,8 @@ namespace Qv2ray::components::plugins::Toolbar while (!isExiting) { bool result = server->waitForNewConnection(5000, &timeOut); - DEBUG(MODULE_PLUGIN, "Plugin thread listening failed: " + server->errorString()) - DEBUG(MODULE_PLUGIN, "waitForNewConnection: " + QString(result ? "true" : "false") + ", " + QString(timeOut ? "true" : "false")) + DEBUG(MODULE_PLUGINHOST, "Plugin thread listening failed: " + server->errorString()) + DEBUG(MODULE_PLUGINHOST, "waitForNewConnection: " + QString(result ? "true" : "false") + ", " + QString(timeOut ? "true" : "false")) } server->close(); @@ -85,7 +85,7 @@ namespace Qv2ray::components::plugins::Toolbar if (linuxWorkerThread->isRunning()) { - LOG(MODULE_PLUGIN, "Waiting for linuxWorkerThread to stop.") + LOG(MODULE_PLUGINHOST, "Waiting for linuxWorkerThread to stop.") linuxWorkerThread->wait(); } diff --git a/src/ui/w_MainWindow.cpp b/src/ui/w_MainWindow.cpp index 35b4e5e0..b4d29134 100644 --- a/src/ui/w_MainWindow.cpp +++ b/src/ui/w_MainWindow.cpp @@ -1,6 +1,7 @@ #include "w_MainWindow.hpp" #include "components/pac/QvPACHandler.hpp" +#include "components/plugins/QvPluginHost.hpp" #include "components/plugins/toolbar/QvToolbar.hpp" #include "components/proxy/QvProxyConfigurator.hpp" #include "core/settings/SettingsBackend.hpp" @@ -299,6 +300,11 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) StartProcessingPlugins(); } + if (!StartupOption.noPlugins) + { + PluginHost->InitializePluginHost(); + } + CheckSubscriptionsUpdate(); // splitter->setSizes(QList() << 100 << 300); diff --git a/src/ui/w_MainWindow.ui b/src/ui/w_MainWindow.ui index 375d087a..7651f71e 100644 --- a/src/ui/w_MainWindow.ui +++ b/src/ui/w_MainWindow.ui @@ -65,9 +65,6 @@ Qt::Horizontal - - QSizePolicy::Maximum - 0 diff --git a/src/ui/w_PluginManager.cpp b/src/ui/w_PluginManager.cpp index ad79b253..6a0e231c 100644 --- a/src/ui/w_PluginManager.cpp +++ b/src/ui/w_PluginManager.cpp @@ -28,8 +28,9 @@ void w_PluginManager::on_pluginListWidget_currentItemChanged(QListWidgetItem *cu pluginNameLabel->setText(info.interface->Name()); pluginAuthorLabel->setText(info.interface->Author()); pluginDescriptionLabel->setText(info.interface->Description()); - pluginIconLabel->setPixmap(info.interface->Icon().pixmap(pluginIconLabel->size() * devicePixelRatio())); pluginTypeLabel->setText("No impl"); + pluginStateTxt->setPlainText(info.errorMessage.isEmpty() ? "OK" : info.errorMessage); + pluginIconLabel->setPixmap(info.interface->Icon().pixmap(pluginIconLabel->size() * devicePixelRatio())); } } diff --git a/src/ui/w_PluginManager.ui b/src/ui/w_PluginManager.ui index 3297a0ab..c7ad9b9a 100644 --- a/src/ui/w_PluginManager.ui +++ b/src/ui/w_PluginManager.ui @@ -6,8 +6,8 @@ 0 0 - 687 - 492 + 684 + 493 @@ -17,11 +17,6 @@ - - - - - @@ -39,7 +34,7 @@ - + Plugin Info @@ -123,19 +118,65 @@ + + + State + + + + Type - + + + + + Message + + + + + + + true + + + + + + + + + + + + + + + + Reload + + + + + + + Unload + + + + + From de91c349b246a443161ba29af9084456cc089db5 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Sun, 22 Mar 2020 18:31:03 +0800 Subject: [PATCH 07/92] add: added qt signal system --- src/components/plugins/interface | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/plugins/interface b/src/components/plugins/interface index 4e566a1b..8bf71715 160000 --- a/src/components/plugins/interface +++ b/src/components/plugins/interface @@ -1 +1 @@ -Subproject commit 4e566a1b019e60e649031f963109516076e20252 +Subproject commit 8bf7171582a96c10c17edb424379a049a387db37 From 322734d9c9cc8cfa0da3b08810e91229912e239f Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Sun, 22 Mar 2020 18:51:45 +0800 Subject: [PATCH 08/92] add: added more plugin UI functions --- makespec/BUILDVERSION | 2 +- src/components/plugins/QvPluginHost.hpp | 2 +- src/ui/w_MainWindow.cpp | 2 +- src/ui/w_MainWindow.hpp | 4 +- src/ui/w_MainWindow.ui | 2 +- src/ui/w_PluginManager.cpp | 16 ++- src/ui/w_PluginManager.hpp | 6 +- src/ui/w_PluginManager.ui | 183 +++++++++++++----------- 8 files changed, 121 insertions(+), 96 deletions(-) diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 2b3bcc61..39d2b3f6 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5052 +5054 \ No newline at end of file diff --git a/src/components/plugins/QvPluginHost.hpp b/src/components/plugins/QvPluginHost.hpp index cc0ac296..72d20acc 100644 --- a/src/components/plugins/QvPluginHost.hpp +++ b/src/components/plugins/QvPluginHost.hpp @@ -11,7 +11,7 @@ namespace Qv2ray::components::plugins { struct QvPluginInfo { - bool canLoadPlugin; + bool isLoaded; QString libraryPath; QString errorMessage; QPluginLoader *pluginLoader; diff --git a/src/ui/w_MainWindow.cpp b/src/ui/w_MainWindow.cpp index b4d29134..dee105e6 100644 --- a/src/ui/w_MainWindow.cpp +++ b/src/ui/w_MainWindow.cpp @@ -1008,5 +1008,5 @@ void MainWindow::on_action_RCM_ClearUsage_triggered() void MainWindow::on_pluginsBtn_clicked() { - w_PluginManager(this).exec(); + PluginManageWindow(this).exec(); } diff --git a/src/ui/w_MainWindow.hpp b/src/ui/w_MainWindow.hpp index fc3515e5..3fa79288 100644 --- a/src/ui/w_MainWindow.hpp +++ b/src/ui/w_MainWindow.hpp @@ -64,6 +64,8 @@ class MainWindow void on_connectionListWidget_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous); void on_masterLogBrowser_textChanged(); + void on_pluginsBtn_clicked(); + private: void on_actionExit_triggered(); void on_action_StartThis_triggered(); @@ -99,8 +101,6 @@ class MainWindow // void SortConnectionList(MW_ITEM_COL byCol, bool asending); - void on_pluginsBtn_clicked(); - protected: void timerEvent(QTimerEvent *event) override; void keyPressEvent(QKeyEvent *e) override; diff --git a/src/ui/w_MainWindow.ui b/src/ui/w_MainWindow.ui index 075cbecf..de8685e8 100644 --- a/src/ui/w_MainWindow.ui +++ b/src/ui/w_MainWindow.ui @@ -22,7 +22,7 @@ - + 5 diff --git a/src/ui/w_PluginManager.cpp b/src/ui/w_PluginManager.cpp index 6a0e231c..6aa33737 100644 --- a/src/ui/w_PluginManager.cpp +++ b/src/ui/w_PluginManager.cpp @@ -2,7 +2,7 @@ #include "components/plugins/QvPluginHost.hpp" -w_PluginManager::w_PluginManager(QWidget *parent) : QDialog(parent) +PluginManageWindow::PluginManageWindow(QWidget *parent) : QDialog(parent) { setupUi(this); for (auto &plugin : PluginHost->AvailablePlugins()) @@ -15,26 +15,30 @@ w_PluginManager::w_PluginManager(QWidget *parent) : QDialog(parent) } } -w_PluginManager::~w_PluginManager() +PluginManageWindow::~PluginManageWindow() { } -void w_PluginManager::on_pluginListWidget_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous) +void PluginManageWindow::on_pluginListWidget_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous) { Q_UNUSED(previous) auto &info = PluginHost->GetPluginInfo(current->data(Qt::UserRole).toString()); if (info.interface != nullptr) { + pluginIconLabel->setPixmap(info.interface->Icon().pixmap(pluginIconLabel->size() * devicePixelRatio())); + // pluginNameLabel->setText(info.interface->Name()); pluginAuthorLabel->setText(info.interface->Author()); pluginDescriptionLabel->setText(info.interface->Description()); + pluginLibPathLabel->setText(info.libraryPath); + pluginStateLabel->setText(info.isLoaded ? tr("Loaded") : tr("Not loaded")); pluginTypeLabel->setText("No impl"); - pluginStateTxt->setPlainText(info.errorMessage.isEmpty() ? "OK" : info.errorMessage); - pluginIconLabel->setPixmap(info.interface->Icon().pixmap(pluginIconLabel->size() * devicePixelRatio())); + pluginHookTypeLabel->setText("No impl"); + pluginErrMessageTxt->setPlainText(info.errorMessage.isEmpty() ? "OK" : info.errorMessage); } } -void w_PluginManager::on_pluginListWidget_itemClicked(QListWidgetItem *item) +void PluginManageWindow::on_pluginListWidget_itemClicked(QListWidgetItem *item) { on_pluginListWidget_currentItemChanged(item, nullptr); } diff --git a/src/ui/w_PluginManager.hpp b/src/ui/w_PluginManager.hpp index ccb27a7b..c2c425e8 100644 --- a/src/ui/w_PluginManager.hpp +++ b/src/ui/w_PluginManager.hpp @@ -4,15 +4,15 @@ #include -class w_PluginManager +class PluginManageWindow : public QDialog , private Ui::w_PluginManager { Q_OBJECT public: - explicit w_PluginManager(QWidget *parent = nullptr); - ~w_PluginManager(); + explicit PluginManageWindow(QWidget *parent = nullptr); + ~PluginManageWindow(); private slots: void on_pluginListWidget_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous); void on_pluginListWidget_itemClicked(QListWidgetItem *item); diff --git a/src/ui/w_PluginManager.ui b/src/ui/w_PluginManager.ui index c7ad9b9a..0b76aa1b 100644 --- a/src/ui/w_PluginManager.ui +++ b/src/ui/w_PluginManager.ui @@ -39,41 +39,8 @@ Plugin Info - - - - - - - - 64 - 64 - - - - ICON! - - - Qt::AlignCenter - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - + + @@ -117,65 +84,119 @@ - + State - - - - Type - - - - - - - - - - - - - Message - - - - - - - true - - - - - - - - - - Reload - - - - - - - Unload - - - - + + + + Type + + + + + + + + + + + + + + Hook Type + + + + + + + Message + + + + + + + true + + + + + + + + + + + + + + Library Path + + + + + + + + + + + + + + + + + + + 64 + 64 + + + + ICON! + + + Qt::AlignCenter + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + Reload + + From 7f7b00751d8c361b6f3af8276d4143b2363a8279 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Sun, 22 Mar 2020 19:51:00 +0800 Subject: [PATCH 09/92] fix: fixed windows build --- src/components/plugins/QvPluginHost.cpp | 28 +++++++++---------- src/components/plugins/QvPluginHost.hpp | 2 +- .../plugins/toolbar/QvToolbar_win.cpp | 14 +++++----- src/ui/w_PluginManager.cpp | 14 +++++----- 4 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/components/plugins/QvPluginHost.cpp b/src/components/plugins/QvPluginHost.cpp index 7d758127..31267798 100644 --- a/src/components/plugins/QvPluginHost.cpp +++ b/src/components/plugins/QvPluginHost.cpp @@ -6,8 +6,6 @@ #include namespace Qv2ray::components::plugins { - const QString errorMessageSuffix = NEWLINE + QObject::tr("Please contact the plugin provider or report the issue to Qv2ray Workgroup."); - QvPluginHost::QvPluginHost(QObject *parent) : QObject(parent) { } @@ -35,32 +33,34 @@ namespace Qv2ray::components::plugins LOG(MODULE_PLUGINHOST, info.pluginLoader->errorString()); continue; } - info.interface = qobject_cast(plugin); - if (info.interface == nullptr) + info.pluginInterface = qobject_cast(plugin); + if (info.pluginInterface == nullptr) { // info.errorMessage = tr("Failed to cast from QObject to Qv2rayPluginInterface"); LOG(MODULE_PLUGINHOST, "Failed to cast from QObject to Qv2rayPluginInterface") info.pluginLoader->unload(); continue; } - if (plugins.contains(info.interface->InternalName())) + if (plugins.contains(info.pluginInterface->InternalName())) { - LOG(MODULE_PLUGINHOST, "Found another plugin with the same internal name: " + info.interface->InternalName() + ". Skipped") + LOG(MODULE_PLUGINHOST, + "Found another plugin with the same internal name: " + info.pluginInterface->InternalName() + ". Skipped") continue; } - if (info.interface->QvPluginInterfaceVersion != QV2RAY_PLUGIN_INTERFACE_VERSION) + if (info.pluginInterface->QvPluginInterfaceVersion != QV2RAY_PLUGIN_INTERFACE_VERSION) { // The plugin was built for a not-compactable version of Qv2ray. Don't load the plugin by default. - LOG(MODULE_PLUGINHOST, "The plugin " + info.interface->InternalName() + + LOG(MODULE_PLUGINHOST, "The plugin " + info.pluginInterface->InternalName() + " is not loaded since it was built against a different version of interface") - info.errorMessage = tr("This plugin was built against an incompactable version of Qv2ray Plugin Interface.") // - + errorMessageSuffix; // + info.errorMessage = tr("This plugin was built against an incompactable version of Qv2ray Plugin Interface.") + NEWLINE + + QObject::tr("Please contact the plugin provider or report the issue to Qv2ray Workgroup."); } - connect(info.interface->GetQObject(), SIGNAL(PluginLog(const QString &)), this, SLOT(QvPluginLog(const QString &))); - LOG(MODULE_PLUGINHOST, "Loaded plugin: \"" + info.interface->Name() + "\" made by: \"" + info.interface->Author() + "\"") - plugins.insert(info.interface->InternalName(), info); + connect(info.pluginInterface->GetQObject(), SIGNAL(PluginLog(const QString &)), this, SLOT(QvPluginLog(const QString &))); + LOG(MODULE_PLUGINHOST, + "Loaded plugin: \"" + info.pluginInterface->Name() + "\" made by: \"" + info.pluginInterface->Author() + "\"") + plugins.insert(info.pluginInterface->InternalName(), info); } } return plugins.count(); @@ -87,7 +87,7 @@ namespace Qv2ray::components::plugins { for (auto &&plugin : plugins) { - DEBUG(MODULE_PLUGINHOST, "Unloading: \"" + plugin.interface->Name() + "\"") + DEBUG(MODULE_PLUGINHOST, "Unloading: \"" + plugin.pluginInterface->Name() + "\"") plugin.pluginLoader->unload(); plugin.pluginLoader->deleteLater(); } diff --git a/src/components/plugins/QvPluginHost.hpp b/src/components/plugins/QvPluginHost.hpp index 72d20acc..94a814bf 100644 --- a/src/components/plugins/QvPluginHost.hpp +++ b/src/components/plugins/QvPluginHost.hpp @@ -15,7 +15,7 @@ namespace Qv2ray::components::plugins QString libraryPath; QString errorMessage; QPluginLoader *pluginLoader; - Qv2rayInterface *interface; + Qv2rayInterface *pluginInterface; }; class QvPluginHost : public QObject diff --git a/src/components/plugins/toolbar/QvToolbar_win.cpp b/src/components/plugins/toolbar/QvToolbar_win.cpp index 718c625e..495a8660 100644 --- a/src/components/plugins/toolbar/QvToolbar_win.cpp +++ b/src/components/plugins/toolbar/QvToolbar_win.cpp @@ -28,7 +28,7 @@ namespace Qv2ray::components::plugins::Toolbar if (hThread == nullptr) { - LOG(MODULE_PLUGIN, "CreateThread failed, GLE=" + QSTRN(GetLastError())) + LOG(MODULE_PLUGINHOST, "CreateThread failed, GLE=" + QSTRN(GetLastError())) return; } else @@ -52,7 +52,7 @@ namespace Qv2ray::components::plugins::Toolbar if (hPipe == INVALID_HANDLE_VALUE) { - LOG(MODULE_PLUGIN, "CreateNamedPipe failed, GLE=" + QSTRN(GetLastError())) + LOG(MODULE_PLUGINHOST, "CreateNamedPipe failed, GLE=" + QSTRN(GetLastError())) return static_cast(-1); } @@ -60,12 +60,12 @@ namespace Qv2ray::components::plugins::Toolbar if (fConnected) { - LOG(MODULE_PLUGIN, "Client connected, creating a processing thread") + LOG(MODULE_PLUGINHOST, "Client connected, creating a processing thread") ThreadHandle = CreateThread(nullptr, 0, InstanceThread, hPipe, 0, &dwThreadId); if (ThreadHandle == nullptr) { - LOG(MODULE_PLUGIN, "CreateThread failed, GLE=" + QSTRN(GetLastError())) + LOG(MODULE_PLUGINHOST, "CreateThread failed, GLE=" + QSTRN(GetLastError())) return static_cast(-1); } else @@ -93,11 +93,11 @@ namespace Qv2ray::components::plugins::Toolbar { if (GetLastError() == ERROR_BROKEN_PIPE) { - LOG(MODULE_PLUGIN, "InstanceThread: client disconnected, GLE=" + QSTRN(GetLastError())) + LOG(MODULE_PLUGINHOST, "InstanceThread: client disconnected, GLE=" + QSTRN(GetLastError())) } else { - LOG(MODULE_PLUGIN, "InstanceThread ReadFile failed, GLE=" + QSTRN(GetLastError())) + LOG(MODULE_PLUGINHOST, "InstanceThread ReadFile failed, GLE=" + QSTRN(GetLastError())) } break; @@ -120,7 +120,7 @@ namespace Qv2ray::components::plugins::Toolbar if (!fSuccess || cbReplyBytes != cbWritten) { - LOG(MODULE_PLUGIN, "InstanceThread WriteFile failed, GLE=" + QSTRN(GetLastError())) + LOG(MODULE_PLUGINHOST, "InstanceThread WriteFile failed, GLE=" + QSTRN(GetLastError())) break; } } diff --git a/src/ui/w_PluginManager.cpp b/src/ui/w_PluginManager.cpp index 6aa33737..e3ac7d11 100644 --- a/src/ui/w_PluginManager.cpp +++ b/src/ui/w_PluginManager.cpp @@ -9,8 +9,8 @@ PluginManageWindow::PluginManageWindow(QWidget *parent) : QDialog(parent) { auto item = new QListWidgetItem(pluginListWidget); item->setCheckState(Qt::CheckState::Unchecked); - item->setData(Qt::UserRole, PluginHost->GetPluginInfo(plugin).interface->InternalName()); - item->setText(PluginHost->GetPluginInfo(plugin).interface->Name()); + item->setData(Qt::UserRole, PluginHost->GetPluginInfo(plugin).pluginInterface->InternalName()); + item->setText(PluginHost->GetPluginInfo(plugin).pluginInterface->Name()); pluginListWidget->addItem(item); } } @@ -23,13 +23,13 @@ void PluginManageWindow::on_pluginListWidget_currentItemChanged(QListWidgetItem { Q_UNUSED(previous) auto &info = PluginHost->GetPluginInfo(current->data(Qt::UserRole).toString()); - if (info.interface != nullptr) + if (info.pluginInterface != nullptr) { - pluginIconLabel->setPixmap(info.interface->Icon().pixmap(pluginIconLabel->size() * devicePixelRatio())); + pluginIconLabel->setPixmap(info.pluginInterface->Icon().pixmap(pluginIconLabel->size() * devicePixelRatio())); // - pluginNameLabel->setText(info.interface->Name()); - pluginAuthorLabel->setText(info.interface->Author()); - pluginDescriptionLabel->setText(info.interface->Description()); + pluginNameLabel->setText(info.pluginInterface->Name()); + pluginAuthorLabel->setText(info.pluginInterface->Author()); + pluginDescriptionLabel->setText(info.pluginInterface->Description()); pluginLibPathLabel->setText(info.libraryPath); pluginStateLabel->setText(info.isLoaded ? tr("Loaded") : tr("Not loaded")); pluginTypeLabel->setText("No impl"); From d52d54a38f43ed27418ad0c343bd6d57b37218d8 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Mon, 23 Mar 2020 07:47:23 +0800 Subject: [PATCH 10/92] fix: remove duplicates --- makespec/BUILDVERSION | 2 +- src/base/Qv2rayBase.hpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 39d2b3f6..45249ac6 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5054 \ No newline at end of file +5055 \ No newline at end of file diff --git a/src/base/Qv2rayBase.hpp b/src/base/Qv2rayBase.hpp index f92c464a..3d5349d1 100644 --- a/src/base/Qv2rayBase.hpp +++ b/src/base/Qv2rayBase.hpp @@ -147,6 +147,7 @@ namespace Qv2ray #endif // This is the default behavior on Windows list << QApplication::applicationDirPath() + "/" + dirName; + list.removeDuplicates(); return list; }; From f865eb3587415050b0a8065639ac09d4ac406c74 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Tue, 24 Mar 2020 16:29:23 +0800 Subject: [PATCH 11/92] submodule: updated submodule --- 3rdparty/cpp-httplib | 2 +- 3rdparty/qzxing | 2 +- makespec/BUILDVERSION | 2 +- src/components/plugins/interface | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/3rdparty/cpp-httplib b/3rdparty/cpp-httplib index e1acb949..dc13cde8 160000 --- a/3rdparty/cpp-httplib +++ b/3rdparty/cpp-httplib @@ -1 +1 @@ -Subproject commit e1acb949e74c663dc9dedc04e41d8bb0dfafb7c7 +Subproject commit dc13cde820621806e415034ae45163ebcc39b66e diff --git a/3rdparty/qzxing b/3rdparty/qzxing index dac9480a..cf339102 160000 --- a/3rdparty/qzxing +++ b/3rdparty/qzxing @@ -1 +1 @@ -Subproject commit dac9480a652d591c41ec9ef6f6c966c59a252142 +Subproject commit cf3391027b7e422dd7b1f9a8678da731273835dc diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 45249ac6..64c6aa56 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5055 \ No newline at end of file +5057 \ No newline at end of file diff --git a/src/components/plugins/interface b/src/components/plugins/interface index 8bf71715..d7ed40da 160000 --- a/src/components/plugins/interface +++ b/src/components/plugins/interface @@ -1 +1 @@ -Subproject commit 8bf7171582a96c10c17edb424379a049a387db37 +Subproject commit d7ed40da47351932b219200876fe10aca433486e From c016ee6d4bd7cd0adc4effc97e4284e4567be4e8 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Wed, 25 Mar 2020 00:04:09 +0800 Subject: [PATCH 12/92] plugin: added plugin enable/disable, load state and plugin type display --- src/components/plugins/QvPluginHost.cpp | 90 ++++++++++- src/components/plugins/QvPluginHost.hpp | 18 ++- src/components/plugins/interface | 2 +- src/ui/w_PluginManager.cpp | 51 +++++- src/ui/w_PluginManager.hpp | 6 + src/ui/w_PluginManager.ui | 198 +++++++++++++++--------- 6 files changed, 277 insertions(+), 88 deletions(-) diff --git a/src/components/plugins/QvPluginHost.cpp b/src/components/plugins/QvPluginHost.cpp index 31267798..38e73449 100644 --- a/src/components/plugins/QvPluginHost.cpp +++ b/src/components/plugins/QvPluginHost.cpp @@ -2,6 +2,7 @@ #include "base/Qv2rayBase.hpp" #include "base/Qv2rayLog.hpp" +#include "core/settings/SettingsBackend.hpp" #include namespace Qv2ray::components::plugins @@ -66,23 +67,65 @@ namespace Qv2ray::components::plugins return plugins.count(); } + const QString QvPluginHost::GetPluginTypeString(const QString &internalName) const + { + switch (plugins.value(internalName).pluginInterface->SpecialPluginType()) + { + case SPECIAL_TYPE_NONE: return tr("No Special Type"); + case SPECIAL_TYPE_KERNEL: return tr("Connection Kernel"); + case SPECIAL_TYPE_GENERATION: return tr("Final Configuration Parser"); + case SPECIAL_TYPE_SERIALIZATION: return tr("Connection String Serializer/Deserializer"); + default: return tr("Unknown/unsupported plugin type."); + } + } + + const QString QvPluginHost::GetPluginHookTypeString(const QString &internalName) const + { + switch (plugins.value(internalName).pluginInterface->PluginHooks()) + { + // + } + return ""; + } + void QvPluginHost::QvPluginLog(const QString &log) { - LOG(MODULE_PLUGINCLIENT, log) + auto _sender = sender(); + if (auto _interface = qobject_cast(_sender); _interface) + { + LOG(MODULE_PLUGINCLIENT + "-" + _interface->InternalName(), log) + } + else + { + LOG(MODULE_PLUGINHOST, "UNKNOWN CLIENT: " + log) + } + } + + bool QvPluginHost::GetPluginEnableState(const QString &internalName) const + { + return GlobalConfig.pluginConfig.pluginStates[internalName]; + } + + void QvPluginHost::SetPluginEnableState(const QString &internalName, bool isEnabled) + { + LOG(MODULE_PLUGINHOST, "Set plugin: \"" + internalName + "\" enable state: " + (isEnabled ? "true" : "false")) + GlobalConfig.pluginConfig.pluginStates[internalName] = isEnabled; + if (isEnabled && !plugins[internalName].isLoaded) + { + // Load plugin if it haven't been loaded. + InitializePlugin(internalName); + } } void QvPluginHost::InitializePluginHost() { RefreshPluginList(); + for (auto &plugin : plugins.keys()) + { + InitializePlugin(plugin); + } } - QStringList QvPluginHost::AvailablePlugins() - { - return plugins.keys(); - } - QvPluginHost::~QvPluginHost() - { - } void QvPluginHost::ClearPlugins() { for (auto &&plugin : plugins) @@ -94,4 +137,35 @@ namespace Qv2ray::components::plugins plugins.clear(); } + bool QvPluginHost::InitializePlugin(const QString &internalName) + { + auto &plugin = plugins[internalName]; + if (plugin.isLoaded) + { + LOG(MODULE_PLUGINHOST, "The plugin: \"" + internalName + "\" has already been loaded.") + return true; + } + if (!GlobalConfig.pluginConfig.pluginStates.contains(internalName)) + { + // If not contained, default to enable. + GlobalConfig.pluginConfig.pluginStates[internalName] = true; + } + // If the plugin is disabled + if (!GlobalConfig.pluginConfig.pluginStates[internalName]) + { + LOG(MODULE_PLUGINHOST, "Cannot load a plugin that's been disabled.") + return false; + } + + // TODO: Load plugin settings. + plugins[internalName].pluginInterface->InitializePlugin({}); + plugins[internalName].isLoaded = true; + return true; + } + + QvPluginHost::~QvPluginHost() + { + ClearPlugins(); + } + } // namespace Qv2ray::components::plugins diff --git a/src/components/plugins/QvPluginHost.hpp b/src/components/plugins/QvPluginHost.hpp index 94a814bf..8dc7a02c 100644 --- a/src/components/plugins/QvPluginHost.hpp +++ b/src/components/plugins/QvPluginHost.hpp @@ -11,7 +11,7 @@ namespace Qv2ray::components::plugins { struct QvPluginInfo { - bool isLoaded; + bool isLoaded = false; QString libraryPath; QString errorMessage; QPluginLoader *pluginLoader; @@ -24,10 +24,16 @@ namespace Qv2ray::components::plugins public: explicit QvPluginHost(QObject *parent = nullptr); ~QvPluginHost(); - int RefreshPluginList(); - QStringList AvailablePlugins(); void InitializePluginHost(); - const inline QvPluginInfo GetPluginInfo(const QString &internalName) + bool GetPluginEnableState(const QString &internalName) const; + void SetPluginEnableState(const QString &internalName, bool isEnabled); + const QString GetPluginTypeString(const QString &internalName) const; + const QString GetPluginHookTypeString(const QString &internalName) const; + const QStringList AvailablePlugins() const + { + return plugins.keys(); + } + const inline QvPluginInfo GetPluginInfo(const QString &internalName) const { return plugins.value(internalName); } @@ -36,9 +42,11 @@ namespace Qv2ray::components::plugins void QvPluginLog(const QString &log); private: + int RefreshPluginList(); + bool InitializePlugin(const QString &internalName); + void ClearPlugins(); // Internal name, plugin info QMap plugins; - void ClearPlugins(); }; inline ::Qv2ray::components::plugins::QvPluginHost *PluginHost = nullptr; diff --git a/src/components/plugins/interface b/src/components/plugins/interface index d7ed40da..79e2f220 160000 --- a/src/components/plugins/interface +++ b/src/components/plugins/interface @@ -1 +1 @@ -Subproject commit d7ed40da47351932b219200876fe10aca433486e +Subproject commit 79e2f220833491d03e7cd397f5ca3b2cfb5b0eca diff --git a/src/ui/w_PluginManager.cpp b/src/ui/w_PluginManager.cpp index e3ac7d11..c656fadc 100644 --- a/src/ui/w_PluginManager.cpp +++ b/src/ui/w_PluginManager.cpp @@ -1,18 +1,22 @@ #include "w_PluginManager.hpp" +#include "common/QvHelpers.hpp" #include "components/plugins/QvPluginHost.hpp" +#include "core/settings/SettingsBackend.hpp" PluginManageWindow::PluginManageWindow(QWidget *parent) : QDialog(parent) { setupUi(this); for (auto &plugin : PluginHost->AvailablePlugins()) { + const auto &info = PluginHost->GetPluginInfo(plugin); auto item = new QListWidgetItem(pluginListWidget); - item->setCheckState(Qt::CheckState::Unchecked); - item->setData(Qt::UserRole, PluginHost->GetPluginInfo(plugin).pluginInterface->InternalName()); - item->setText(PluginHost->GetPluginInfo(plugin).pluginInterface->Name()); + item->setCheckState(PluginHost->GetPluginEnableState(info.pluginInterface->InternalName()) ? Qt::Checked : Qt::Unchecked); + item->setData(Qt::UserRole, info.pluginInterface->InternalName()); + item->setText(info.pluginInterface->Name() + " (" + (info.isLoaded ? tr("Loaded") : tr("Not loaded")) + ")"); pluginListWidget->addItem(item); } + isLoading = false; } PluginManageWindow::~PluginManageWindow() @@ -23,7 +27,7 @@ void PluginManageWindow::on_pluginListWidget_currentItemChanged(QListWidgetItem { Q_UNUSED(previous) auto &info = PluginHost->GetPluginInfo(current->data(Qt::UserRole).toString()); - if (info.pluginInterface != nullptr) + if (info.pluginInterface) { pluginIconLabel->setPixmap(info.pluginInterface->Icon().pixmap(pluginIconLabel->size() * devicePixelRatio())); // @@ -32,7 +36,7 @@ void PluginManageWindow::on_pluginListWidget_currentItemChanged(QListWidgetItem pluginDescriptionLabel->setText(info.pluginInterface->Description()); pluginLibPathLabel->setText(info.libraryPath); pluginStateLabel->setText(info.isLoaded ? tr("Loaded") : tr("Not loaded")); - pluginTypeLabel->setText("No impl"); + pluginTypeLabel->setText(PluginHost->GetPluginTypeString(info.pluginInterface->InternalName())); pluginHookTypeLabel->setText("No impl"); pluginErrMessageTxt->setPlainText(info.errorMessage.isEmpty() ? "OK" : info.errorMessage); } @@ -42,3 +46,40 @@ void PluginManageWindow::on_pluginListWidget_itemClicked(QListWidgetItem *item) { on_pluginListWidget_currentItemChanged(item, nullptr); } + +void PluginManageWindow::on_pluginListWidget_itemChanged(QListWidgetItem *item) +{ + if (isLoading) + return; + bool isEnabled = item->checkState() == Qt::Checked; + auto pluginInternalName = item->data(Qt::UserRole).toString(); + PluginHost->SetPluginEnableState(pluginInternalName, isEnabled); + auto &info = PluginHost->GetPluginInfo(pluginInternalName); + item->setText(info.pluginInterface->Name() + " (" + (info.isLoaded ? tr("Loaded") : tr("Not loaded")) + ")"); + // + if (!isEnabled) + { + QvMessageBoxInfo(this, tr("Disabling a plugin"), tr("This plugin will keep loaded until the next time Qv2ray starts.")); + } +} + +void PluginManageWindow::on_pluginSettingsBtn_clicked() +{ + if (auto current = pluginListWidget->currentItem(); current != nullptr) + { + auto &info = PluginHost->GetPluginInfo(current->data(Qt::UserRole).toString()); + if (!info.isLoaded) + { + QvMessageBoxWarn(this, tr("Plugin not loaded"), + tr("This plugin has been unloaded or has been disabled, please enable or reload the plugin to continue.")); + return; + } + if (auto widget = info.pluginInterface->GetUIWidgets(UI_TYPE_PREFERENCE_WINDOW); widget != nullptr) + { + QDialog d; + widget->setParent(&d); + d.exec(); + widget->setParent(nullptr); + } + } +} diff --git a/src/ui/w_PluginManager.hpp b/src/ui/w_PluginManager.hpp index c2c425e8..156c9ef2 100644 --- a/src/ui/w_PluginManager.hpp +++ b/src/ui/w_PluginManager.hpp @@ -16,4 +16,10 @@ class PluginManageWindow private slots: void on_pluginListWidget_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous); void on_pluginListWidget_itemClicked(QListWidgetItem *item); + void on_pluginListWidget_itemChanged(QListWidgetItem *item); + + void on_pluginSettingsBtn_clicked(); + + private: + bool isLoading = true; }; diff --git a/src/ui/w_PluginManager.ui b/src/ui/w_PluginManager.ui index 0b76aa1b..7ecffa0e 100644 --- a/src/ui/w_PluginManager.ui +++ b/src/ui/w_PluginManager.ui @@ -6,8 +6,8 @@ 0 0 - 684 - 493 + 757 + 518 @@ -23,7 +23,7 @@ Qt::Horizontal - QDialogButtonBox::Cancel|QDialogButtonBox::Ok + QDialogButtonBox::Ok @@ -40,7 +40,7 @@ Plugin Info - + @@ -54,6 +54,9 @@ + + true + @@ -65,9 +68,18 @@ + + IBeamCursor + + + true + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + @@ -79,64 +91,17 @@ + + IBeamCursor + - - - - - - State - - - - - - - - - - - - - - Type - - - - - - - - - - - - - - Hook Type - - - - - - - Message - - - - - - + true - - - - - - + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse @@ -149,14 +114,120 @@ + + IBeamCursor + + + true + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + State + + + + + + + IBeamCursor + + + + + + true + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + Hook Type + + + + + + + IBeamCursor + + + + + + true + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + Special Plugin Type + + + + + + + IBeamCursor + + + + + + true + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + Error Message + + + + + + + true + + + + + + + + + Open settings page for current plugin + + + Open plugin settings + + + + + - + @@ -189,17 +260,6 @@ - - - - - - Reload - - - - - From 422edb4a98e881f1bc22286d3a53654b27ef69db Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Wed, 25 Mar 2020 20:51:00 +0800 Subject: [PATCH 13/92] plugin: update plugin settings subsystem --- assets/icons/designs/Applogo_Bird.svg | 238 ++++++++++++++++++++++++ src/components/plugins/QvPluginHost.cpp | 15 +- src/components/plugins/interface | 2 +- src/ui/w_PluginManager.cpp | 25 ++- src/ui/w_PluginManager.hpp | 2 + src/ui/w_PluginManager.ui | 35 ++-- 6 files changed, 298 insertions(+), 19 deletions(-) create mode 100644 assets/icons/designs/Applogo_Bird.svg diff --git a/assets/icons/designs/Applogo_Bird.svg b/assets/icons/designs/Applogo_Bird.svg new file mode 100644 index 00000000..b3578563 --- /dev/null +++ b/assets/icons/designs/Applogo_Bird.svg @@ -0,0 +1,238 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/src/components/plugins/QvPluginHost.cpp b/src/components/plugins/QvPluginHost.cpp index 38e73449..39c24a4d 100644 --- a/src/components/plugins/QvPluginHost.cpp +++ b/src/components/plugins/QvPluginHost.cpp @@ -2,6 +2,7 @@ #include "base/Qv2rayBase.hpp" #include "base/Qv2rayLog.hpp" +#include "common/QvHelpers.hpp" #include "core/settings/SettingsBackend.hpp" #include @@ -9,6 +10,10 @@ namespace Qv2ray::components::plugins { QvPluginHost::QvPluginHost(QObject *parent) : QObject(parent) { + if (auto dir = QDir(QV2RAY_PLUGIN_SETTINGS_DIR); !dir.exists()) + { + dir.mkpath(QV2RAY_PLUGIN_SETTINGS_DIR); + } } int QvPluginHost::RefreshPluginList() @@ -157,14 +162,20 @@ namespace Qv2ray::components::plugins return false; } - // TODO: Load plugin settings. - plugins[internalName].pluginInterface->InitializePlugin({}); + auto conf = JsonFromString(StringFromFile(QV2RAY_PLUGIN_SETTINGS_DIR + internalName + ".conf")); + plugins[internalName].pluginInterface->InitializePlugin(conf); plugins[internalName].isLoaded = true; return true; } QvPluginHost::~QvPluginHost() { + for (auto name : plugins.keys()) + { + LOG(MODULE_PLUGINHOST, "Saving plugin settings for: \"" + name + "\"") + auto &conf = plugins[name].pluginInterface->GetPluginSettngs(); + StringToFile(JsonToString(conf), QV2RAY_PLUGIN_SETTINGS_DIR + name + ".conf"); + } ClearPlugins(); } diff --git a/src/components/plugins/interface b/src/components/plugins/interface index 79e2f220..ff56e968 160000 --- a/src/components/plugins/interface +++ b/src/components/plugins/interface @@ -1 +1 @@ -Subproject commit 79e2f220833491d03e7cd397f5ca3b2cfb5b0eca +Subproject commit ff56e9680480609d6b4f23e9513a7b06f877822a diff --git a/src/ui/w_PluginManager.cpp b/src/ui/w_PluginManager.cpp index c656fadc..dbc545df 100644 --- a/src/ui/w_PluginManager.cpp +++ b/src/ui/w_PluginManager.cpp @@ -3,6 +3,7 @@ #include "common/QvHelpers.hpp" #include "components/plugins/QvPluginHost.hpp" #include "core/settings/SettingsBackend.hpp" +#include "ui/editors/w_JsonEditor.hpp" PluginManageWindow::PluginManageWindow(QWidget *parent) : QDialog(parent) { @@ -65,7 +66,7 @@ void PluginManageWindow::on_pluginListWidget_itemChanged(QListWidgetItem *item) void PluginManageWindow::on_pluginSettingsBtn_clicked() { - if (auto current = pluginListWidget->currentItem(); current != nullptr) + if (const auto ¤t = pluginListWidget->currentItem(); current != nullptr) { auto &info = PluginHost->GetPluginInfo(current->data(Qt::UserRole).toString()); if (!info.isLoaded) @@ -74,7 +75,7 @@ void PluginManageWindow::on_pluginSettingsBtn_clicked() tr("This plugin has been unloaded or has been disabled, please enable or reload the plugin to continue.")); return; } - if (auto widget = info.pluginInterface->GetUIWidgets(UI_TYPE_PREFERENCE_WINDOW); widget != nullptr) + if (auto widget = info.pluginInterface->GetSettingsWidget(); widget != nullptr) { QDialog d; widget->setParent(&d); @@ -83,3 +84,23 @@ void PluginManageWindow::on_pluginSettingsBtn_clicked() } } } + +void PluginManageWindow::on_pluginEditSettingsJsonBtn_clicked() +{ + if (const auto ¤t = pluginListWidget->currentItem(); current != nullptr) + { + const auto &info = PluginHost->GetPluginInfo(current->data(Qt::UserRole).toString()); + if (!info.isLoaded) + { + QvMessageBoxWarn(this, tr("Plugin not loaded"), + tr("This plugin has been unloaded or has been disabled, please enable or reload the plugin to continue.")); + return; + } + JsonEditor w(info.pluginInterface->GetPluginSettngs()); + auto newConf = w.OpenEditor(); + if (w.result() == QDialog::Accepted) + { + info.pluginInterface->UpdatePluginSettings(newConf); + } + } +} diff --git a/src/ui/w_PluginManager.hpp b/src/ui/w_PluginManager.hpp index 156c9ef2..8cef05d1 100644 --- a/src/ui/w_PluginManager.hpp +++ b/src/ui/w_PluginManager.hpp @@ -20,6 +20,8 @@ class PluginManageWindow void on_pluginSettingsBtn_clicked(); + void on_pluginEditSettingsJsonBtn_clicked(); + private: bool isLoading = true; }; diff --git a/src/ui/w_PluginManager.ui b/src/ui/w_PluginManager.ui index 7ecffa0e..16ba253c 100644 --- a/src/ui/w_PluginManager.ui +++ b/src/ui/w_PluginManager.ui @@ -211,20 +211,6 @@ - - - - - - Open settings page for current plugin - - - Open plugin settings - - - - - @@ -260,6 +246,27 @@ + + + + + + Open settings page for current plugin + + + Open plugin settings + + + + + + + Manually Edit Settings + + + + + From 492813139dd36479d03c251a591124e2746ff9f7 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Wed, 25 Mar 2020 22:54:23 +0800 Subject: [PATCH 14/92] plugin: simple plugin integration supported --- src/components/plugins/QvPluginHost.cpp | 76 ++++++++++++++++++++--- src/components/plugins/QvPluginHost.hpp | 1 + src/components/plugins/interface | 2 +- src/core/handler/ConfigHandler.cpp | 24 +++++++ src/core/handler/V2rayInstanceHandler.cpp | 12 ++++ src/ui/w_PluginManager.cpp | 2 +- 6 files changed, 106 insertions(+), 11 deletions(-) diff --git a/src/components/plugins/QvPluginHost.cpp b/src/components/plugins/QvPluginHost.cpp index 39c24a4d..4e38f330 100644 --- a/src/components/plugins/QvPluginHost.cpp +++ b/src/components/plugins/QvPluginHost.cpp @@ -74,23 +74,36 @@ namespace Qv2ray::components::plugins const QString QvPluginHost::GetPluginTypeString(const QString &internalName) const { - switch (plugins.value(internalName).pluginInterface->SpecialPluginType()) + QStringList types; + for (auto type : plugins.value(internalName).pluginInterface->SpecialPluginType()) { - case SPECIAL_TYPE_NONE: return tr("No Special Type"); - case SPECIAL_TYPE_KERNEL: return tr("Connection Kernel"); - case SPECIAL_TYPE_GENERATION: return tr("Final Configuration Parser"); - case SPECIAL_TYPE_SERIALIZATION: return tr("Connection String Serializer/Deserializer"); - default: return tr("Unknown/unsupported plugin type."); + switch (type) + { + case SPECIAL_TYPE_NONE: types << tr("No Special Type"); break; + case SPECIAL_TYPE_KERNEL: types << tr("Connection Kernel"); break; + case SPECIAL_TYPE_GENERATION: types << tr("Final Configuration Parser"); break; + case SPECIAL_TYPE_SERIALIZATION: types << tr("Connection String Serializer/Deserializer"); break; + default: types << tr("Unknown/unsupported plugin type."); break; + } } + return types.join("; "); } const QString QvPluginHost::GetPluginHookTypeString(const QString &internalName) const { - switch (plugins.value(internalName).pluginInterface->PluginHooks()) + QStringList hooks; + for (auto hook : plugins.value(internalName).pluginInterface->PluginHooks()) { - // + switch (hook) + { + case HOOK_TYPE_NONE: hooks << tr("No hook"); break; + case HOOK_TYPE_STATE_EVENTS: hooks << tr("Connection State Change"); break; + case HOOK_TYPE_CONFIG_EVENTS: hooks << tr("Connection Change"); break; + case HOOK_TYPE_STATS_EVENTS: hooks << tr("Statistics Event"); break; + default: hooks << tr("Unknown/unsupported hook type."); break; + } } - return ""; + return hooks.join("; "); } void QvPluginHost::QvPluginLog(const QString &log) @@ -142,6 +155,51 @@ namespace Qv2ray::components::plugins plugins.clear(); } + void QvPluginHost::SendHook(QV2RAY_PLUGIN_HOOK_TYPE type, QV2RAY_PLUGIN_HOOK_SUBTYPE subtype, QVariant &data) + { + for (auto name : plugins.keys()) + { + const auto info = plugins[name]; + if (!info.isLoaded) + { + DEBUG(MODULE_PLUGINHOST, "The plugin has not been loaded.") + continue; + } + + auto types = info.pluginInterface->SpecialPluginType(); + if (types.contains(SPECIAL_TYPE_KERNEL)) + { + info.pluginInterface->PluginHooks(); + // A kernel will only listens on pre-connection, post-connection, pre-disconnection, post-disconnection + if (type == HOOK_TYPE_STATE_EVENTS && (subtype == HOOK_STYPE_PRE_CONNECTING || subtype == HOOK_STYPE_PRE_DISCONNECTING)) + { + info.pluginInterface->ProcessHook(type, subtype, &data); + } + } + if (types.contains(SPECIAL_TYPE_GENERATION)) + { + // TODO + } + if (types.contains(SPECIAL_TYPE_SERIALIZATION)) + { + // TODO + } + if (types.contains(SPECIAL_TYPE_NONE)) + { + // This plugin has no special type. + for (auto hook : info.pluginInterface->PluginHooks()) + { + if (hook == HOOK_TYPE_NONE) + continue; + if (hook == type) + { + info.pluginInterface->ProcessHook(type, subtype, &data); + } + } + } + } + } + bool QvPluginHost::InitializePlugin(const QString &internalName) { auto &plugin = plugins[internalName]; diff --git a/src/components/plugins/QvPluginHost.hpp b/src/components/plugins/QvPluginHost.hpp index 8dc7a02c..84bd6404 100644 --- a/src/components/plugins/QvPluginHost.hpp +++ b/src/components/plugins/QvPluginHost.hpp @@ -37,6 +37,7 @@ namespace Qv2ray::components::plugins { return plugins.value(internalName); } + void SendHook(QV2RAY_PLUGIN_HOOK_TYPE type, QV2RAY_PLUGIN_HOOK_SUBTYPE subtype, QVariant &data); private slots: void QvPluginLog(const QString &log); diff --git a/src/components/plugins/interface b/src/components/plugins/interface index ff56e968..36fe6fc0 160000 --- a/src/components/plugins/interface +++ b/src/components/plugins/interface @@ -1 +1 @@ -Subproject commit ff56e9680480609d6b4f23e9513a7b06f877822a +Subproject commit 36fe6fc0668a42f5ad84a5377ef95288fadb2768 diff --git a/src/core/handler/ConfigHandler.cpp b/src/core/handler/ConfigHandler.cpp index 4ddca513..0de782e0 100644 --- a/src/core/handler/ConfigHandler.cpp +++ b/src/core/handler/ConfigHandler.cpp @@ -1,6 +1,7 @@ #include "ConfigHandler.hpp" #include "common/QvHelpers.hpp" +#include "components/plugins/QvPluginHost.hpp" #include "core/connection/Serialization.hpp" #include "core/settings/SettingsBackend.hpp" @@ -214,6 +215,8 @@ namespace Qv2ray::core::handlers connections[id].upLinkData = 0; connections[id].downLinkData = 0; emit OnStatsAvailable(id, 0, 0, 0, 0); + auto v = QVariant::fromValue(QList{ 0, 0, 0, 0 }); + PluginHost->SendHook(HOOK_TYPE_STATS_EVENTS, HOOK_STYPE_STATS_CHANGED, v); return {}; } @@ -221,6 +224,8 @@ namespace Qv2ray::core::handlers { CheckConnectionExistance(id); OnConnectionRenamed(id, connections[id].displayName, newName); + auto v = QVariant::fromValue(QList{ connections[id].displayName, newName }); + PluginHost->SendHook(HOOK_TYPE_CONFIG_EVENTS, HOOK_STYPE_RENAMED, v); connections[id].displayName = newName; return {}; } @@ -231,6 +236,8 @@ namespace Qv2ray::core::handlers QFile connectionFile((groups[groupId].isSubscription ? QV2RAY_SUBSCRIPTION_DIR : QV2RAY_CONNECTIONS_DIR) + groupId.toString() + "/" + id.toString() + QV2RAY_CONFIG_FILE_EXTENSION); // + auto v = QVariant::fromValue(QList{ connections[id].displayName }); + PluginHost->SendHook(HOOK_TYPE_CONFIG_EVENTS, HOOK_STYPE_REMOVED, v); connections.remove(id); groups[groupId].connections.removeAll(id); emit OnConnectionDeleted(id, groupId); @@ -272,6 +279,9 @@ namespace Qv2ray::core::handlers groups[newGroupId].connections.append(id); connections[id].groupId = newGroupId; // + auto v = QVariant::fromValue(QList{ connections[id].displayName }); + PluginHost->SendHook(HOOK_TYPE_CONFIG_EVENTS, HOOK_STYPE_UPDATED, v); + // emit OnConnectionGroupChanged(id, oldgid, newGroupId); // return {}; @@ -301,6 +311,9 @@ namespace Qv2ray::core::handlers QDir(QV2RAY_CONNECTIONS_DIR + id.toString()).removeRecursively(); } // + auto v = QVariant::fromValue(QList{ groups[id].displayName }); + PluginHost->SendHook(HOOK_TYPE_CONFIG_EVENTS, HOOK_STYPE_REMOVED, v); + // groups.remove(id); emit OnGroupDeleted(id, list); if (id == DefaultGroupId) @@ -375,6 +388,9 @@ namespace Qv2ray::core::handlers CheckConnectionExistanceEx(result.connectionId, nothing); connections[result.connectionId].latency = result.avg; emit OnLatencyTestFinished(result.connectionId, result.avg); + // + auto v = QVariant::fromValue(QList{ result.avg, result.best, result.worst }); + PluginHost->SendHook(HOOK_TYPE_STATS_EVENTS, HOOK_STYPE_LATENCY_UPDATED, v); } bool QvConfigHandler::UpdateConnection(const ConnectionId &id, const CONFIGROOT &root, bool skipRestart) @@ -391,6 +407,8 @@ namespace Qv2ray::core::handlers connectionRootCache[id] = root; // emit OnConnectionModified(id); + auto v = QVariant::fromValue(QList{ connections[id].displayName }); + PluginHost->SendHook(HOOK_TYPE_CONFIG_EVENTS, HOOK_STYPE_MODIFIED, v); if (!skipRestart && id == currentConnectionId) { emit RestartConnection(); @@ -404,6 +422,8 @@ namespace Qv2ray::core::handlers groups[id].displayName = displayName; groups[id].isSubscription = isSubscription; groups[id].importDate = system_clock::to_time_t(system_clock::now()); + auto v = QVariant::fromValue(QList{ groups[id].displayName }); + PluginHost->SendHook(HOOK_TYPE_CONFIG_EVENTS, HOOK_STYPE_CREATED, v); emit OnGroupCreated(id, displayName); return id; } @@ -416,6 +436,8 @@ namespace Qv2ray::core::handlers return tr("Group does not exist"); } OnGroupRenamed(id, groups[id].displayName, newName); + auto v = QVariant::fromValue(QList{ groups[id].displayName, newName }); + PluginHost->SendHook(HOOK_TYPE_CONFIG_EVENTS, HOOK_STYPE_RENAMED, v); groups[id].displayName = newName; return {}; } @@ -571,6 +593,8 @@ namespace Qv2ray::core::handlers connections[newId].importDate = system_clock::to_time_t(system_clock::now()); connections[newId].displayName = displayName; emit OnConnectionCreated(newId, displayName); + auto v = QVariant::fromValue(QList{ displayName }); + PluginHost->SendHook(HOOK_TYPE_CONFIG_EVENTS, HOOK_STYPE_CREATED, v); UpdateConnection(newId, root); return newId; } diff --git a/src/core/handler/V2rayInstanceHandler.cpp b/src/core/handler/V2rayInstanceHandler.cpp index e0c43045..66c2bb9c 100644 --- a/src/core/handler/V2rayInstanceHandler.cpp +++ b/src/core/handler/V2rayInstanceHandler.cpp @@ -1,4 +1,5 @@ #include "ConfigHandler.hpp" +#include "components/plugins/QvPluginHost.hpp" #include "core/connection/Generation.hpp" optional QvConfigHandler::CHStartConnection_p(const ConnectionId &id, const CONFIGROOT &root) @@ -6,13 +7,19 @@ optional QvConfigHandler::CHStartConnection_p(const ConnectionId &id, c connections[id].lastConnected = system_clock::to_time_t(system_clock::now()); // auto fullConfig = GenerateRuntimeConfig(root); + + // + auto v = QVariant::fromValue(QList{ connections[id].displayName }); + PluginHost->SendHook(HOOK_TYPE_STATE_EVENTS, HOOK_STYPE_PRE_CONNECTING, v); auto result = vCoreInstance->StartConnection(id, fullConfig); if (!result.has_value()) { currentConnectionId = id; emit OnConnected(currentConnectionId); + PluginHost->SendHook(HOOK_TYPE_STATE_EVENTS, HOOK_STYPE_POST_CONNECTED, v); } + PluginHost->SendHook(HOOK_TYPE_STATE_EVENTS, HOOK_STYPE_POST_DISCONNECTED, v); return result; } @@ -21,11 +28,14 @@ void QvConfigHandler::CHStopConnection_p() { if (vCoreInstance->KernelStarted) { + auto v = QVariant::fromValue(QList{ connections[currentConnectionId].displayName }); + PluginHost->SendHook(HOOK_TYPE_STATE_EVENTS, HOOK_STYPE_PRE_DISCONNECTING, v); vCoreInstance->StopConnection(); // Copy ConnectionId id = currentConnectionId; currentConnectionId = NullConnectionId; emit OnDisconnected(id); + PluginHost->SendHook(HOOK_TYPE_STATE_EVENTS, HOOK_STYPE_POST_DISCONNECTED, v); } else { @@ -38,6 +48,8 @@ void QvConfigHandler::OnStatsDataArrived(const ConnectionId &id, const quint64 u connections[id].upLinkData += uploadSpeed; connections[id].downLinkData += downloadSpeed; emit OnStatsAvailable(id, uploadSpeed, downloadSpeed, connections[id].upLinkData, connections[id].downLinkData); + auto v = QVariant::fromValue(QList() << uploadSpeed << downloadSpeed << connections[id].upLinkData << connections[id].downLinkData); + PluginHost->SendHook(HOOK_TYPE_STATS_EVENTS, HOOK_STYPE_STATS_CHANGED, v); } void QvConfigHandler::OnVCoreCrashed(const ConnectionId &id) diff --git a/src/ui/w_PluginManager.cpp b/src/ui/w_PluginManager.cpp index dbc545df..a4f40689 100644 --- a/src/ui/w_PluginManager.cpp +++ b/src/ui/w_PluginManager.cpp @@ -38,7 +38,7 @@ void PluginManageWindow::on_pluginListWidget_currentItemChanged(QListWidgetItem pluginLibPathLabel->setText(info.libraryPath); pluginStateLabel->setText(info.isLoaded ? tr("Loaded") : tr("Not loaded")); pluginTypeLabel->setText(PluginHost->GetPluginTypeString(info.pluginInterface->InternalName())); - pluginHookTypeLabel->setText("No impl"); + pluginHookTypeLabel->setText(PluginHost->GetPluginHookTypeString(info.pluginInterface->InternalName())); pluginErrMessageTxt->setPlainText(info.errorMessage.isEmpty() ? "OK" : info.errorMessage); } } From cf390b3792b8fd92ded17c6ca283744d8353fd26 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Thu, 26 Mar 2020 10:53:47 +0800 Subject: [PATCH 15/92] PAC: "See you in the plugin manager" --- CMakeLists.txt | 3 - makespec/BUILDVERSION | 2 +- src/base/models/QvSettingsObject.hpp | 19 +- src/components/pac/QvGFWPACConverter.cpp | 137 ----- src/components/pac/QvPACHandler.cpp | 77 --- src/components/pac/QvPACHandler.hpp | 41 -- src/components/plugins/QvPluginHost.cpp | 17 +- src/components/plugins/QvPluginHost.hpp | 1 + src/components/plugins/interface | 2 +- src/components/proxy/QvProxyConfigurator.cpp | 157 +++-- src/components/proxy/QvProxyConfigurator.hpp | 2 +- src/core/handler/V2rayInstanceHandler.cpp | 6 +- src/ui/w_MainWindow.cpp | 70 --- src/ui/w_MainWindow.hpp | 2 - src/ui/w_MainWindow_extra.cpp | 45 +- src/ui/w_PreferencesWindow.cpp | 142 +---- src/ui/w_PreferencesWindow.hpp | 12 - src/ui/w_PreferencesWindow.ui | 597 +++++++------------ 18 files changed, 312 insertions(+), 1020 deletions(-) delete mode 100644 src/components/pac/QvGFWPACConverter.cpp delete mode 100644 src/components/pac/QvPACHandler.cpp delete mode 100644 src/components/pac/QvPACHandler.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 87c4a0cb..9b65ae8c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -153,8 +153,6 @@ set(QV2RAY_SOURCES src/components/autolaunch/QvAutoLaunch.cpp src/components/geosite/QvGeositeReader.cpp src/components/icmping/win/ICMPPinger.cpp - src/components/pac/QvGFWPACConverter.cpp - src/components/pac/QvPACHandler.cpp src/components/plugins/toolbar/QvToolbar.cpp src/components/plugins/toolbar/QvToolbar_linux.cpp src/components/plugins/toolbar/QvToolbar_win.cpp @@ -236,7 +234,6 @@ set(QV2RAY_SOURCES src/components/darkmode/DarkmodeDetector.hpp src/components/geosite/QvGeositeReader.hpp src/components/icmping/win/ICMPPinger.hpp - src/components/pac/QvPACHandler.hpp src/components/plugins/toolbar/QvToolbar.hpp src/components/plugins/interface/QvPluginInterface.hpp src/components/plugins/QvPluginHost.hpp diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 64c6aa56..85972ca5 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5057 \ No newline at end of file +5060 \ No newline at end of file diff --git a/src/base/models/QvSettingsObject.hpp b/src/base/models/QvSettingsObject.hpp index 9bef4ba9..2b8a0f00 100644 --- a/src/base/models/QvSettingsObject.hpp +++ b/src/base/models/QvSettingsObject.hpp @@ -41,18 +41,6 @@ namespace Qv2ray::base::config XTOSTRUCT(O(Pages)) }; - struct Qv2rayPACConfig - { - bool enablePAC; - int port; - QString localIP; - bool useSocksProxy; - Qv2rayPACConfig() : enablePAC(false), port(8989), useSocksProxy(false) - { - } - XTOSTRUCT(O(enablePAC, port, localIP, useSocksProxy)) - }; - struct Qv2rayForwardProxyConfig { bool enableForwardProxy; @@ -73,7 +61,6 @@ namespace Qv2ray::base::config { QString listenip; bool setSystemProxy; - Qv2rayPACConfig pacConfig; // SOCKS bool useSocks; @@ -89,13 +76,13 @@ namespace Qv2ray::base::config objects::AccountObject httpAccount; Qv2rayInboundsConfig() - : listenip("127.0.0.1"), setSystemProxy(true), pacConfig(), useSocks(true), socks_port(1088), socks_useAuth(false), socksUDP(true), + : listenip("127.0.0.1"), setSystemProxy(true), useSocks(true), socks_port(1088), socks_useAuth(false), socksUDP(true), socksLocalIP("127.0.0.1"), socksAccount(), useHTTP(true), http_port(8888), http_useAuth(false), httpAccount() { } - XTOSTRUCT(O(setSystemProxy, pacConfig, listenip, useSocks, useHTTP, socks_port, socks_useAuth, socksAccount, socksUDP, socksLocalIP, - http_port, http_useAuth, httpAccount)) + XTOSTRUCT(O(setSystemProxy, listenip, useSocks, useHTTP, socks_port, socks_useAuth, socksAccount, socksUDP, socksLocalIP, http_port, + http_useAuth, httpAccount)) }; struct Qv2rayUIConfig diff --git a/src/components/pac/QvGFWPACConverter.cpp b/src/components/pac/QvGFWPACConverter.cpp deleted file mode 100644 index 30e0e9e8..00000000 --- a/src/components/pac/QvGFWPACConverter.cpp +++ /dev/null @@ -1,137 +0,0 @@ -/* ORIGINAL LICENSE: Do What The F*ck You Want To Public License - * AUTHOR: LBYPatrick - * - * MODIFIED BY Leroy.H.Y @lhy0403 re-licenced under GPLv3 - */ - -#include "common/QvHelpers.hpp" - -namespace Qv2ray::components::pac -{ - // Private function - string getRawDomain(string originLine) - { - size_t startPosition = 0; - size_t endPosition = originLine.size(); - string returnBuffer; - bool skipRule1 = originLine.find("[") != string::npos; // [Auto xxxx... - bool skipRule2 = originLine.find("!") != string::npos; // Comments - bool skipRule3 = originLine.find("@") != string::npos; // Non-proxy Lines - bool skipRule4 = originLine.find("*") != string::npos; - bool passRule1 = originLine.find("|") != string::npos; // Proxy Lines - bool passRule2 = originLine.find(".") != string::npos; // Link-Contained Lines - - if (originLine[endPosition] == '\n') - { - endPosition -= 1; - } - - if (originLine.find("http://") != string::npos) - { - startPosition += 8; - } - else if (originLine.find("https://") != string::npos) - { - startPosition += 9; - } - - // Skip unrelated lines - if (skipRule1 || skipRule2 || skipRule3 || skipRule4) - { - return ""; - } - else if (passRule2) - { - if (passRule1) - { - startPosition += originLine.find_last_of("|") + 1; - } - - if (originLine[startPosition] == '\n') - startPosition += 1; - - for (size_t i = startPosition; i < endPosition; ++i) - { - returnBuffer += originLine[i]; - } - } - - return returnBuffer; - } - - QString ConvertGFWToPAC(const QString &rawContent, const QString &customProxyString) - { - auto rawFileContent = Base64Decode(rawContent).toStdString(); - string readBuffer = ""; // cleanup - string writeBuffer; - string domainListCache = ""; - - for (size_t i = 0; i < rawFileContent.size(); ++i) - { - readBuffer += rawFileContent[i]; - - if (rawFileContent[i + 1] == '\n') - { - writeBuffer = getRawDomain(readBuffer); - - if (writeBuffer != "") - { - domainListCache += writeBuffer + "\n"; - } - - readBuffer = ""; - i += 1; - } - } - - size_t rotatorTwo = 0; - string readDomainBuffer = ""; - bool isFirstLine = true; - string outputContent = ""; - // Header - outputContent += "var domains = {\n"; - - // Read and process output content line by line - while (rotatorTwo < domainListCache.size()) - { - while (true) - { - // Get Domain - readDomainBuffer += domainListCache[rotatorTwo]; - - if (domainListCache[rotatorTwo + 1] == '\n') - { - rotatorTwo += 2; - break; - } - - rotatorTwo++; - } - - // Format - if (!isFirstLine) - outputContent += ",\n"; - else - isFirstLine = false; - - outputContent += "\t\""; - outputContent += readDomainBuffer; - outputContent += "\" : 1"; - readDomainBuffer = ""; - } - - // End Message - outputContent += NEWLINE "};" NEWLINE "" NEWLINE " var proxy = \"" + customProxyString.toStdString() + ";\";" + - NEWLINE " var direct = 'DIRECT;';" NEWLINE " function FindProxyForURL(url, host) {" NEWLINE - " var suffix;" NEWLINE " var pos = host.lastIndexOf('.');" NEWLINE - " pos = host.lastIndexOf('.', pos - 1);" NEWLINE " //" NEWLINE " while (1) {" NEWLINE - " if (domains[host] != undefined) {" NEWLINE " return proxy;" NEWLINE - " }" NEWLINE " else if (pos <= 0) {" NEWLINE - " return domains['.' + host] != undefined ? proxy : direct;" NEWLINE " }" NEWLINE - " suffix = host.substring(pos);" NEWLINE " if (domains[suffix] != undefined) {" NEWLINE - " return proxy;" NEWLINE " }" NEWLINE - " pos = host.lastIndexOf('.', pos - 1);" NEWLINE " }" NEWLINE " }"; - // - return QString::fromStdString(outputContent); - } -} // namespace Qv2ray::components::pac diff --git a/src/components/pac/QvPACHandler.cpp b/src/components/pac/QvPACHandler.cpp deleted file mode 100644 index 7b727fbf..00000000 --- a/src/components/pac/QvPACHandler.cpp +++ /dev/null @@ -1,77 +0,0 @@ -#include "QvPACHandler.hpp" - -#include "3rdparty/cpp-httplib/httplib.h" -#include "common/QvHelpers.hpp" -#include "core/CoreUtils.hpp" - -namespace Qv2ray::components::pac -{ - PACServer::PACServer(QObject *parent) : QThread(parent) - { - server = new httplib::Server(); - connect(this, &QThread::finished, this, &QThread::deleteLater); - } - PACServer::~PACServer() - { - wait(); - DEBUG(MODULE_PROXY, "~PACServer") - delete server; - } - void PACServer::stopServer() - { - if (server->is_running()) - server->stop(); - quit(); - LOG(MODULE_UI, "Stopping PAC server") - } - void PACServer::run() - { - LOG(MODULE_PROXY, "Starting PAC listener") - // - auto address = GlobalConfig.inboundConfig.listenip; - auto port = GlobalConfig.inboundConfig.pacConfig.port; - // - DEBUG(MODULE_PROXY, "PAC Listening local endpoint: " + address + ":" + QSTRN(port)) - // - QString gfwContent = StringFromFile(QV2RAY_RULES_GFWLIST_PATH); - pacContent = ConvertGFWToPAC(gfwContent, proxyString); - // - server->Get("/pac", pacRequestHandler); - auto result = server->listen(address.toStdString().c_str(), static_cast(port)); - if (result) - { - DEBUG(MODULE_PROXY, "PAC handler stopped.") - } - else - { - LOG(MODULE_PROXY, "Failed to listen on port " + QSTRN(port) + ", possible permission denied.") - QvMessageBoxWarn(nullptr, tr("PAC Handler"), tr("Failed to listen PAC request on this port, please verify the permissions")); - } - } - - void PACServer::pacRequestHandler(const httplib::Request &req, httplib::Response &rsp) - { - rsp.set_header("Server", "Qv2ray/" QV2RAY_VERSION_STRING " PAC_Handler"); - if (req.method == "GET") - { - if (req.path == "/pac") - { - DEBUG(MODULE_PROXY, "Serving PAC file request.") - // - rsp.status = 200; - rsp.set_content(pacContent.toStdString(), "application/javascript; charset=utf-8"); - DEBUG(MODULE_PROXY, "Serving a pac file...") - } - else - { - rsp.status = 404; - rsp.set_content("NOT FOUND", "text/plain; charset=utf-8"); - } - } - else - { - rsp.status = 405; - rsp.set_content("PAC ONLY SUPPORT GET", "text/plain; charset=utf-8"); - } - } -} // namespace Qv2ray::components::pac diff --git a/src/components/pac/QvPACHandler.hpp b/src/components/pac/QvPACHandler.hpp deleted file mode 100644 index cf4b238a..00000000 --- a/src/components/pac/QvPACHandler.hpp +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace httplib -{ - class Server; - struct Request; - struct Response; -} // namespace httplib - -namespace Qv2ray::components::pac -{ - QString ConvertGFWToPAC(const QString &rawContent, const QString &customProxyString); - class PACServer : public QThread - { - Q_OBJECT - public: - explicit PACServer(QObject *parent = nullptr); - ~PACServer(); - inline void setPACProxyString(const QString &proxyStr) - { - proxyString = proxyStr; - } - void stopServer(); - - private: - void run() override; - QString proxyString; - - private: - httplib::Server *server; - static void pacRequestHandler(const httplib::Request &req, httplib::Response &rsp); - static inline QString pacContent; - }; -} // namespace Qv2ray::components::pac - -using namespace Qv2ray::components; -using namespace Qv2ray::components::pac; diff --git a/src/components/plugins/QvPluginHost.cpp b/src/components/plugins/QvPluginHost.cpp index 4e38f330..3b780b1c 100644 --- a/src/components/plugins/QvPluginHost.cpp +++ b/src/components/plugins/QvPluginHost.cpp @@ -64,6 +64,8 @@ namespace Qv2ray::components::plugins } connect(info.pluginInterface->GetQObject(), SIGNAL(PluginLog(const QString &)), this, SLOT(QvPluginLog(const QString &))); + connect(info.pluginInterface->GetQObject(), SIGNAL(PluginErrorMessageBox(const QString &)), this, + SLOT(QvPluginMessageBox(const QString &))); LOG(MODULE_PLUGINHOST, "Loaded plugin: \"" + info.pluginInterface->Name() + "\" made by: \"" + info.pluginInterface->Author() + "\"") plugins.insert(info.pluginInterface->InternalName(), info); @@ -119,6 +121,19 @@ namespace Qv2ray::components::plugins } } + void QvPluginHost::QvPluginMessageBox(const QString &msg) + { + auto _sender = sender(); + if (auto _interface = qobject_cast(_sender); _interface) + { + QvMessageBoxWarn(nullptr, _interface->InternalName(), msg); + } + else + { + QvMessageBoxWarn(nullptr, "Unknown Plugin", msg); + } + } + bool QvPluginHost::GetPluginEnableState(const QString &internalName) const { return GlobalConfig.pluginConfig.pluginStates[internalName]; @@ -221,7 +236,7 @@ namespace Qv2ray::components::plugins } auto conf = JsonFromString(StringFromFile(QV2RAY_PLUGIN_SETTINGS_DIR + internalName + ".conf")); - plugins[internalName].pluginInterface->InitializePlugin(conf); + plugins[internalName].pluginInterface->InitializePlugin(QV2RAY_PLUGIN_SETTINGS_DIR + internalName + "/", conf); plugins[internalName].isLoaded = true; return true; } diff --git a/src/components/plugins/QvPluginHost.hpp b/src/components/plugins/QvPluginHost.hpp index 84bd6404..ffef96b0 100644 --- a/src/components/plugins/QvPluginHost.hpp +++ b/src/components/plugins/QvPluginHost.hpp @@ -41,6 +41,7 @@ namespace Qv2ray::components::plugins private slots: void QvPluginLog(const QString &log); + void QvPluginMessageBox(const QString &message); private: int RefreshPluginList(); diff --git a/src/components/plugins/interface b/src/components/plugins/interface index 36fe6fc0..8e594c28 160000 --- a/src/components/plugins/interface +++ b/src/components/plugins/interface @@ -1 +1 @@ -Subproject commit 36fe6fc0668a42f5ad84a5377ef95288fadb2768 +Subproject commit 8e594c281f7c1a24a4a50d9e181200f3552a3263 diff --git a/src/components/proxy/QvProxyConfigurator.cpp b/src/components/proxy/QvProxyConfigurator.cpp index b9edd8f9..c1bb628b 100644 --- a/src/components/proxy/QvProxyConfigurator.cpp +++ b/src/components/proxy/QvProxyConfigurator.cpp @@ -188,46 +188,30 @@ namespace Qv2ray::components::proxy } #endif - void SetSystemProxy(const QString &address, int httpPort, int socksPort, bool usePAC) + void SetSystemProxy(const QString &address, int httpPort, int socksPort) { LOG(MODULE_PROXY, "Setting up System Proxy") bool hasHTTP = (httpPort != 0); bool hasSOCKS = (socksPort != 0); - if (!(hasHTTP || hasSOCKS || usePAC)) + if (!(hasHTTP || hasSOCKS)) { LOG(MODULE_PROXY, "Nothing?") return; } - if (usePAC) + if (hasHTTP) { - LOG(MODULE_PROXY, "Qv2ray will set system proxy to use PAC file") + LOG(MODULE_PROXY, "Qv2ray will set system proxy to use HTTP") } - else - { - if (hasHTTP) - { - LOG(MODULE_PROXY, "Qv2ray will set system proxy to use HTTP") - } - if (hasSOCKS) - { - LOG(MODULE_PROXY, "Qv2ray will set system proxy to use SOCKS") - } + if (hasSOCKS) + { + LOG(MODULE_PROXY, "Qv2ray will set system proxy to use SOCKS") } #ifdef Q_OS_WIN - QString __a; - - if (usePAC) - { - __a = address; - } - else - { - __a = (hasHTTP ? "http://" : "socks5://") + address + ":" + QSTRN(httpPort); - } + QString __a = (hasHTTP ? "http://" : "socks5://") + address + ":" + QSTRN(httpPort); LOG(MODULE_PROXY, "Windows proxy string: " + __a) auto proxyStrW = new WCHAR[__a.length() + 1]; @@ -235,7 +219,7 @@ namespace Qv2ray::components::proxy // __QueryProxyOptions(); - if (!__SetProxyOptions(proxyStrW, usePAC)) + if (!__SetProxyOptions(proxyStrW, false)) { LOG(MODULE_PROXY, "Failed to set proxy.") } @@ -243,7 +227,7 @@ namespace Qv2ray::components::proxy __QueryProxyOptions(); #elif defined(Q_OS_LINUX) QStringList actions; - auto proxyMode = usePAC ? "auto" : "manual"; + auto proxyMode = "manual"; actions << QString("gsettings set org.gnome.system.proxy mode '%1'").arg(proxyMode); bool isKDE = qEnvironmentVariable("XDG_SESSION_DESKTOP") == "KDE"; if (isKDE) @@ -251,58 +235,57 @@ namespace Qv2ray::components::proxy LOG(MODULE_PROXY, "KDE detected") } // - if (usePAC) + // if (usePAC) + // { + // actions << QString("gsettings set org.gnome.system.proxy autoconfig-url '%1'").arg(address); + // if (isKDE) + // { + // actions << QString("kwriteconfig5 --file " + QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + + // "/kioslaverc --group \"Proxy Settings\" --key ProxyType 2"); + + // actions << QString("kwriteconfig5 --file " + QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + + // "/kioslaverc --group \"Proxy Settings\" --key \"Proxy Config Script\" " + address); + // } + // } + // else + // { + if (isKDE) { - actions << QString("gsettings set org.gnome.system.proxy autoconfig-url '%1'").arg(address); + actions << QString("kwriteconfig5 --file " + QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + + "/kioslaverc --group \"Proxy Settings\" --key ProxyType 1"); + } + if (hasHTTP) + { + actions << QString("gsettings set org.gnome.system.proxy.http host '%1'").arg(address); + actions << QString("gsettings set org.gnome.system.proxy.http port %1").arg(httpPort); + // + actions << QString("gsettings set org.gnome.system.proxy.https host '%1'").arg(address); + actions << QString("gsettings set org.gnome.system.proxy.https port %1").arg(httpPort); if (isKDE) { - actions << QString("kwriteconfig5 --file " + QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + - "/kioslaverc --group \"Proxy Settings\" --key ProxyType 2"); - - actions << QString("kwriteconfig5 --file " + QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + - "/kioslaverc --group \"Proxy Settings\" --key \"Proxy Config Script\" " + address); + // FTP here should be scheme: ftp:// + for (auto protocol : { "http", "ftp", "https" }) + { + auto str = QString("kwriteconfig5 --file " + QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + + "/kioslaverc --group \"Proxy Settings\" --key %1Proxy \"http://%2 %3\"") + .arg(protocol) + .arg(address) + .arg(QSTRN(httpPort)); + actions << str; + } } } - else + + if (hasSOCKS) { + actions << QString("gsettings set org.gnome.system.proxy.socks host '%1'").arg(address); + actions << QString("gsettings set org.gnome.system.proxy.socks port %1").arg(socksPort); if (isKDE) { - actions << QString("kwriteconfig5 --file " + QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + - "/kioslaverc --group \"Proxy Settings\" --key ProxyType 1"); - } - if (hasHTTP) - { - actions << QString("gsettings set org.gnome.system.proxy.http host '%1'").arg(address); - actions << QString("gsettings set org.gnome.system.proxy.http port %1").arg(httpPort); - // - actions << QString("gsettings set org.gnome.system.proxy.https host '%1'").arg(address); - actions << QString("gsettings set org.gnome.system.proxy.https port %1").arg(httpPort); - if (isKDE) - { - // FTP here should be scheme: ftp:// - for (auto protocol : { "http", "ftp", "https" }) - { - auto str = QString("kwriteconfig5 --file " + QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + - "/kioslaverc --group \"Proxy Settings\" --key %1Proxy \"http://%2 %3\"") - .arg(protocol) - .arg(address) - .arg(QSTRN(httpPort)); - actions << str; - } - } - } - - if (hasSOCKS) - { - actions << QString("gsettings set org.gnome.system.proxy.socks host '%1'").arg(address); - actions << QString("gsettings set org.gnome.system.proxy.socks port %1").arg(socksPort); - if (isKDE) - { - actions << QString("kwriteconfig5 --file " + QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + - "/kioslaverc --group \"Proxy Settings\" --key socksProxy \"socks://%1 %2\"") - .arg(address) - .arg(QSTRN(socksPort)); - } + actions << QString("kwriteconfig5 --file " + QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + + "/kioslaverc --group \"Proxy Settings\" --key socksProxy \"socks://%1 %2\"") + .arg(address) + .arg(QSTRN(socksPort)); } } @@ -326,27 +309,27 @@ namespace Qv2ray::components::proxy { LOG(MODULE_PROXY, "Setting proxy for interface: " + service) - if (usePAC) + // if (usePAC) + // { + // QProcess::execute("/usr/sbin/networksetup -setautoproxystate " + service + " on"); + // QProcess::execute("/usr/sbin/networksetup -setautoproxyurl " + service + " " + address); + // } + // else + // { + if (hasHTTP) { - QProcess::execute("/usr/sbin/networksetup -setautoproxystate " + service + " on"); - QProcess::execute("/usr/sbin/networksetup -setautoproxyurl " + service + " " + address); + QProcess::execute("/usr/sbin/networksetup -setwebproxystate " + service + " on"); + QProcess::execute("/usr/sbin/networksetup -setsecurewebproxystate " + service + " on"); + QProcess::execute("/usr/sbin/networksetup -setwebproxy " + service + " " + address + " " + QSTRN(httpPort)); + QProcess::execute("/usr/sbin/networksetup -setsecurewebproxy " + service + " " + address + " " + QSTRN(httpPort)); } - else - { - if (hasHTTP) - { - QProcess::execute("/usr/sbin/networksetup -setwebproxystate " + service + " on"); - QProcess::execute("/usr/sbin/networksetup -setsecurewebproxystate " + service + " on"); - QProcess::execute("/usr/sbin/networksetup -setwebproxy " + service + " " + address + " " + QSTRN(httpPort)); - QProcess::execute("/usr/sbin/networksetup -setsecurewebproxy " + service + " " + address + " " + QSTRN(httpPort)); - } - if (hasSOCKS) - { - QProcess::execute("/usr/sbin/networksetup -setsocksfirewallproxystate " + service + " on"); - QProcess::execute("/usr/sbin/networksetup -setsocksfirewallproxy " + service + " " + address + " " + QSTRN(socksPort)); - } + if (hasSOCKS) + { + QProcess::execute("/usr/sbin/networksetup -setsocksfirewallproxystate " + service + " on"); + QProcess::execute("/usr/sbin/networksetup -setsocksfirewallproxy " + service + " " + address + " " + QSTRN(socksPort)); } + // } } #endif diff --git a/src/components/proxy/QvProxyConfigurator.hpp b/src/components/proxy/QvProxyConfigurator.hpp index f8e071d8..67bc3df8 100644 --- a/src/components/proxy/QvProxyConfigurator.hpp +++ b/src/components/proxy/QvProxyConfigurator.hpp @@ -5,7 +5,7 @@ namespace Qv2ray::components::proxy { void ClearSystemProxy(); - void SetSystemProxy(const QString &address, int http_port, int socks_port, bool usePAC); + void SetSystemProxy(const QString &address, int http_port, int socks_port); } // namespace Qv2ray::components::proxy using namespace Qv2ray::components; diff --git a/src/core/handler/V2rayInstanceHandler.cpp b/src/core/handler/V2rayInstanceHandler.cpp index 66c2bb9c..d5990503 100644 --- a/src/core/handler/V2rayInstanceHandler.cpp +++ b/src/core/handler/V2rayInstanceHandler.cpp @@ -19,8 +19,10 @@ optional QvConfigHandler::CHStartConnection_p(const ConnectionId &id, c emit OnConnected(currentConnectionId); PluginHost->SendHook(HOOK_TYPE_STATE_EVENTS, HOOK_STYPE_POST_CONNECTED, v); } - PluginHost->SendHook(HOOK_TYPE_STATE_EVENTS, HOOK_STYPE_POST_DISCONNECTED, v); - + else + { + PluginHost->SendHook(HOOK_TYPE_STATE_EVENTS, HOOK_STYPE_POST_DISCONNECTED, v); + } return result; } diff --git a/src/ui/w_MainWindow.cpp b/src/ui/w_MainWindow.cpp index dee105e6..e83e8804 100644 --- a/src/ui/w_MainWindow.cpp +++ b/src/ui/w_MainWindow.cpp @@ -1,6 +1,5 @@ #include "w_MainWindow.hpp" -#include "components/pac/QvPACHandler.hpp" #include "components/plugins/QvPluginHost.hpp" #include "components/plugins/toolbar/QvToolbar.hpp" #include "components/proxy/QvProxyConfigurator.hpp" @@ -411,11 +410,6 @@ void MainWindow::VersionUpdate(QByteArray &data) MainWindow::~MainWindow() { - if (GlobalConfig.inboundConfig.pacConfig.enablePAC && pacServer != nullptr && pacServer->isRunning()) - { - // Wait for PAC server to finish. - pacServer->wait(); - } hTray.hide(); } @@ -610,11 +604,6 @@ void MainWindow::OnDisconnected(const ConnectionId &id) { ClearSystemProxy(); } - - if (GlobalConfig.inboundConfig.pacConfig.enablePAC) - { - pacServer->stopServer(); - } } void MainWindow::OnConnected(const ConnectionId &id) @@ -633,65 +622,6 @@ void MainWindow::OnConnected(const ConnectionId &id) connetionStatusLabel->setText(tr("Connected: ") + name); // ConnectionManager->StartLatencyTest(id); - bool usePAC = GlobalConfig.inboundConfig.pacConfig.enablePAC; - bool pacUseSocks = GlobalConfig.inboundConfig.pacConfig.useSocksProxy; - bool httpEnabled = GlobalConfig.inboundConfig.useHTTP; - bool socksEnabled = GlobalConfig.inboundConfig.useSocks; - - if (usePAC) - { - bool canStartPAC = true; - QString pacProxyString; // Something like this --> SOCKS5 127.0.0.1:1080; SOCKS - // 127.0.0.1:1080; DIRECT; http://proxy:8080 - auto pacIP = GlobalConfig.inboundConfig.pacConfig.localIP; - - if (pacIP.isEmpty()) - { - LOG(MODULE_PROXY, "PAC Local IP is empty, default to 127.0.0.1") - pacIP = "127.0.0.1"; - } - - if (pacUseSocks) - { - if (socksEnabled) - { - pacProxyString = "SOCKS5 " + pacIP + ":" + QSTRN(GlobalConfig.inboundConfig.socks_port); - } - else - { - LOG(MODULE_UI, "PAC is using SOCKS, but it is not enabled") - QvMessageBoxWarn(this, tr("Configuring PAC"), - tr("Could not start PAC server as it is configured to use SOCKS, but it is not enabled")); - canStartPAC = false; - } - } - else - { - if (httpEnabled) - { - pacProxyString = "PROXY " + pacIP + ":" + QSTRN(GlobalConfig.inboundConfig.http_port); - } - else - { - LOG(MODULE_UI, "PAC is using HTTP, but it is not enabled") - QvMessageBoxWarn(this, tr("Configuring PAC"), - tr("Could not start PAC server as it is configured to use HTTP, but it is not enabled")); - canStartPAC = false; - } - } - - if (canStartPAC) - { - pacServer = new PACServer(this); - pacServer->setPACProxyString(pacProxyString); - pacServer->start(); - } - else - { - LOG(MODULE_PROXY, "Not starting PAC due to previous error.") - } - } - if (GlobalConfig.inboundConfig.setSystemProxy) { MWSetSystemProxy(); diff --git a/src/ui/w_MainWindow.hpp b/src/ui/w_MainWindow.hpp index 3fa79288..4ee8368c 100644 --- a/src/ui/w_MainWindow.hpp +++ b/src/ui/w_MainWindow.hpp @@ -2,7 +2,6 @@ #include "common/HTTPRequestHelper.hpp" #include "common/LogHighlighter.hpp" -#include "components/pac/QvPACHandler.hpp" #include "components/speedchart/speedwidget.hpp" #include "core/handler/ConfigHandler.hpp" #include "ui/messaging/QvMessageBus.hpp" @@ -116,7 +115,6 @@ class MainWindow QvHttpRequestHelper *requestHelper; #endif QSystemTrayIcon hTray; - PACServer *pacServer; SyntaxHighlighter *vCoreLogHighlighter; ConnectionInfoWidget *infoWidget; // diff --git a/src/ui/w_MainWindow_extra.cpp b/src/ui/w_MainWindow_extra.cpp index 140f8819..f8207215 100644 --- a/src/ui/w_MainWindow_extra.cpp +++ b/src/ui/w_MainWindow_extra.cpp @@ -4,8 +4,6 @@ void MainWindow::MWSetSystemProxy() { - bool usePAC = GlobalConfig.inboundConfig.pacConfig.enablePAC; - bool pacUseSocks = GlobalConfig.inboundConfig.pacConfig.useSocksProxy; bool httpEnabled = GlobalConfig.inboundConfig.useHTTP; bool socksEnabled = GlobalConfig.inboundConfig.useSocks; // @@ -19,42 +17,19 @@ void MainWindow::MWSetSystemProxy() QString proxyAddress; bool canSetSystemProxy = true; - if (usePAC) + // Not using PAC + if (httpEnabled || socksEnabled) { - if ((httpEnabled && !pacUseSocks) || (socksEnabled && pacUseSocks)) - { - // If we use PAC and socks/http are properly configured for PAC - LOG(MODULE_PROXY, "System proxy uses PAC") - proxyAddress = "http://" + GlobalConfig.inboundConfig.listenip + ":" + QSTRN(GlobalConfig.inboundConfig.pacConfig.port) + "/pac"; - } - else - { - // Not properly configured - LOG(MODULE_PROXY, "Failed to process pac due to following reasons:") - LOG(MODULE_PROXY, " --> PAC is configured to use socks but socks is not enabled.") - LOG(MODULE_PROXY, " --> PAC is configuted to use http but http is not enabled.") - QvMessageBoxWarn(this, tr("PAC Processing Failed"), - tr("HTTP or SOCKS inbound is not properly configured for PAC") + NEWLINE + - tr("Qv2ray will continue, but will not set system proxy.")); - canSetSystemProxy = false; - } + // Not use PAC, System proxy should use HTTP or SOCKS + LOG(MODULE_PROXY, "Setting up system proxy.") + // A 'proxy host' should be a host WITHOUT `http://` uri scheme + proxyAddress = "localhost"; } else { - // Not using PAC - if (httpEnabled || socksEnabled) - { - // Not use PAC, System proxy should use HTTP or SOCKS - LOG(MODULE_PROXY, "Setting up system proxy.") - // A 'proxy host' should be a host WITHOUT `http://` uri scheme - proxyAddress = "localhost"; - } - else - { - LOG(MODULE_PROXY, "Neither of HTTP nor SOCKS is enabled, cannot set system proxy.") - QvMessageBoxWarn(this, tr("Cannot set system proxy"), tr("Both HTTP and SOCKS inbounds are not enabled")); - canSetSystemProxy = false; - } + LOG(MODULE_PROXY, "Neither of HTTP nor SOCKS is enabled, cannot set system proxy.") + QvMessageBoxWarn(this, tr("Cannot set system proxy"), tr("Both HTTP and SOCKS inbounds are not enabled")); + canSetSystemProxy = false; } if (canSetSystemProxy) @@ -63,7 +38,7 @@ void MainWindow::MWSetSystemProxy() auto httpPort = GlobalConfig.inboundConfig.useHTTP ? GlobalConfig.inboundConfig.http_port : 0; auto socksPort = GlobalConfig.inboundConfig.useSocks ? GlobalConfig.inboundConfig.socks_port : 0; // - SetSystemProxy(proxyAddress, httpPort, socksPort, usePAC); + SetSystemProxy(proxyAddress, httpPort, socksPort); } } else diff --git a/src/ui/w_PreferencesWindow.cpp b/src/ui/w_PreferencesWindow.cpp index f73ef239..a6090b72 100644 --- a/src/ui/w_PreferencesWindow.cpp +++ b/src/ui/w_PreferencesWindow.cpp @@ -86,14 +86,6 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent), Current // // listenIPTxt->setText(CurrentConfig.inboundConfig.listenip); - bool pacEnabled = CurrentConfig.inboundConfig.pacConfig.enablePAC; - pacGroupBox->setChecked(pacEnabled); - setSysProxyCB->setChecked(CurrentConfig.inboundConfig.setSystemProxy); - // - // PAC - pacPortSB->setValue(CurrentConfig.inboundConfig.pacConfig.port); - pacProxyTxt->setText(CurrentConfig.inboundConfig.pacConfig.localIP); - pacProxyCB->setCurrentIndex(CurrentConfig.inboundConfig.pacConfig.useSocksProxy ? 1 : 0); // bool have_http = CurrentConfig.inboundConfig.useHTTP; httpGroupBox->setChecked(have_http); @@ -207,8 +199,7 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent), Current // maxLogLinesSB->setValue(CurrentConfig.uiConfig.maximumLogLines); // - pacListenAddrLabel->setText("http://" + (pacProxyTxt->text().isEmpty() ? "127.0.0.1" : pacProxyTxt->text()) + ":" + - QSTRN(pacPortSB->value()) + "/pac"); + setSysProxyCB->setChecked(CurrentConfig.inboundConfig.setSystemProxy); // finishedLoading = true; routeSettingsWidget = new RouteSettingsMatrixWidget(CurrentConfig.kernelConfig.AssetsPath(), this); @@ -250,12 +241,6 @@ void PreferencesWindow::on_buttonBox_accepted() ports << CurrentConfig.inboundConfig.socks_port; } - if (CurrentConfig.inboundConfig.pacConfig.enablePAC) - { - size++; - ports << CurrentConfig.inboundConfig.pacConfig.port; - } - if (!StartupOption.noAPI) { size++; @@ -918,84 +903,6 @@ void PreferencesWindow::on_darkTrayCB_stateChanged(int arg1) CurrentConfig.uiConfig.useDarkTrayIcon = arg1 == Qt::Checked; } -void PreferencesWindow::on_pacGoBtn_clicked() -{ - LOADINGCHECK - QString gfwLocation; - QString fileContent; - pacGoBtn->setEnabled(false); - gfwListCB->setEnabled(false); - QvHttpRequestHelper request; - LOG(MODULE_PROXY, "Downloading GFWList file.") - bool withProxy = getGFWListWithProxyCB->isChecked(); - - switch (gfwListCB->currentIndex()) - { - case 0: - gfwLocation = "https://gitlab.com/gfwlist/gfwlist/raw/master/gfwlist.txt"; - fileContent = QString::fromUtf8(request.syncget(gfwLocation, withProxy)); - break; - - case 1: - gfwLocation = "https://pagure.io/gfwlist/raw/master/f/gfwlist.txt"; - fileContent = QString::fromUtf8(request.syncget(gfwLocation, withProxy)); - break; - - case 2: - gfwLocation = "http://repo.or.cz/gfwlist.git/blob_plain/HEAD:/gfwlist.txt"; - fileContent = QString::fromUtf8(request.syncget(gfwLocation, withProxy)); - break; - - case 3: - gfwLocation = "https://bitbucket.org/gfwlist/gfwlist/raw/HEAD/gfwlist.txt"; - fileContent = QString::fromUtf8(request.syncget(gfwLocation, withProxy)); - break; - - case 4: - gfwLocation = "https://raw.githubusercontent.com/gfwlist/gfwlist/master/gfwlist.txt"; - fileContent = QString::fromUtf8(request.syncget(gfwLocation, withProxy)); - break; - - case 5: - gfwLocation = "https://git.tuxfamily.org/gfwlist/gfwlist.git/plain/gfwlist.txt"; - fileContent = QString::fromUtf8(request.syncget(gfwLocation, withProxy)); - break; - - case 6: - auto file = QFileDialog::getOpenFileName(this, tr("Select GFWList in base64")); - - if (file.isEmpty()) - { - QvMessageBoxWarn(this, tr("Download GFWList"), tr("Operation is cancelled.")); - return; - } - - fileContent = StringFromFile(file); - break; - } - - LOG(MODULE_NETWORK, "Fetched: " + gfwLocation) - QvMessageBoxInfo(this, tr("Download GFWList"), tr("Successfully downloaded GFWList.")); - pacGoBtn->setEnabled(true); - gfwListCB->setEnabled(true); - - if (!QDir(QV2RAY_RULES_DIR).exists()) - { - QDir(QV2RAY_RULES_DIR).mkpath(QV2RAY_RULES_DIR); - } - - StringToFile(fileContent, QV2RAY_RULES_GFWLIST_PATH); -} - -void PreferencesWindow::on_pacPortSB_valueChanged(int arg1) -{ - LOADINGCHECK - NEEDRESTART - CurrentConfig.inboundConfig.pacConfig.port = arg1; - pacListenAddrLabel->setText("http://" + (pacProxyTxt->text().isEmpty() ? "127.0.0.1" : pacProxyTxt->text()) + ":" + - QSTRN(pacPortSB->value()) + "/pac"); -} - void PreferencesWindow::on_setSysProxyCB_stateChanged(int arg1) { LOADINGCHECK @@ -1003,28 +910,12 @@ void PreferencesWindow::on_setSysProxyCB_stateChanged(int arg1) CurrentConfig.inboundConfig.setSystemProxy = arg1 == Qt::Checked; } -void PreferencesWindow::on_pacProxyCB_currentIndexChanged(int index) -{ - LOADINGCHECK - NEEDRESTART - // 0 -> http - // 1 -> socks - CurrentConfig.inboundConfig.pacConfig.useSocksProxy = index == 1; -} - void PreferencesWindow::on_pushButton_clicked() { LOADINGCHECK QDesktopServices::openUrl(QUrl::fromUserInput(QV2RAY_RULES_DIR)); } -void PreferencesWindow::on_pacProxyTxt_textEdited(const QString &arg1) -{ - LOADINGCHECK - NEEDRESTART - CurrentConfig.inboundConfig.pacConfig.localIP = arg1; -} - void PreferencesWindow::on_autoStartSubsCombo_currentIndexChanged(const QString &arg1) { LOADINGCHECK if (arg1.isEmpty()) @@ -1133,23 +1024,6 @@ void PreferencesWindow::on_fpPortSB_valueChanged(int arg1) CurrentConfig.connectionConfig.forwardProxyConfig.port = arg1; } -void PreferencesWindow::on_pacProxyTxt_textChanged(const QString &arg1) -{ - Q_UNUSED(arg1) - - if (IsValidIPAddress(arg1)) - { - BLACK(pacProxyTxt) - } - else - { - RED(pacProxyTxt) - } - - pacListenAddrLabel->setText("http://" + (pacProxyTxt->text().isEmpty() ? "127.0.0.1" : pacProxyTxt->text()) + ":" + - QSTRN(pacPortSB->value()) + "/pac"); -} - void PreferencesWindow::on_checkVCoreSettings_clicked() { auto vcorePath = vCorePathTxt->text(); @@ -1182,20 +1056,6 @@ void PreferencesWindow::on_socksGroupBox_clicked(bool checked) CurrentConfig.inboundConfig.useSocks = checked; } -void PreferencesWindow::on_pacGroupBox_clicked(bool checked) -{ - LOADINGCHECK - NEEDRESTART - CurrentConfig.inboundConfig.pacConfig.enablePAC = checked; - if (checked) - { - QvMessageBoxWarn(this, QObject::tr("Deprecated"), - QObject::tr("PAC is now deprecated and is not encouraged to be used anymore.") + NEWLINE + - QObject::tr("It will be removed or be provided as a plugin in the future.") + NEWLINE + NEWLINE + - QObject::tr("PAC will still work currently, but please switch to the V2ray built-in routing as soon as possible.")); - } -} - void PreferencesWindow::on_fpGroupBox_clicked(bool checked) { LOADINGCHECK diff --git a/src/ui/w_PreferencesWindow.hpp b/src/ui/w_PreferencesWindow.hpp index 442ca246..260693f4 100644 --- a/src/ui/w_PreferencesWindow.hpp +++ b/src/ui/w_PreferencesWindow.hpp @@ -118,18 +118,10 @@ class PreferencesWindow void on_darkTrayCB_stateChanged(int arg1); - void on_pacGoBtn_clicked(); - - void on_pacPortSB_valueChanged(int arg1); - void on_setSysProxyCB_stateChanged(int arg1); - void on_pacProxyCB_currentIndexChanged(int index); - void on_pushButton_clicked(); - void on_pacProxyTxt_textEdited(const QString &arg1); - void on_autoStartSubsCombo_currentIndexChanged(const QString &arg1); void on_autoStartConnCombo_currentIndexChanged(const QString &arg1); @@ -148,16 +140,12 @@ class PreferencesWindow void on_fpPortSB_valueChanged(int arg1); - void on_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); diff --git a/src/ui/w_PreferencesWindow.ui b/src/ui/w_PreferencesWindow.ui index 945a4d7e..a884a2f1 100644 --- a/src/ui/w_PreferencesWindow.ui +++ b/src/ui/w_PreferencesWindow.ui @@ -10,7 +10,7 @@ 0 0 1010 - 629 + 622 @@ -23,7 +23,7 @@ - 0 + 1 @@ -490,8 +490,8 @@ 0 0 - 1016 - 604 + 964 + 603 @@ -529,83 +529,6 @@ - - - - HTTP Settings - - - true - - - false - - - - - - Port - - - - - - - 1 - - - 65535 - - - 18001 - - - - - - - Authentication - - - - - - - Enabled - - - - - - - Username - - - - - - - user - - - - - - - Password - - - - - - - pass - - - - - - @@ -711,10 +634,10 @@ - - + + - Transparent Proxy Settings + HTTP Settings true @@ -722,199 +645,16 @@ false - - - - - - 75 - true - true - - - - All settings below will only be applied onto simple connection. - - - - - - - Add Docodemo-door inbound - - - - - - - - - Address - - - - - - - 127.0.0.1 - - - - - - - Port - - - - - - - 1 - - - 65535 - - - 12345 - - - - - - - TCP - - - - - - - UDP - - - - - - - Follow Redirect - - - - - - - - - Override Connection SockOpt Settings - - - true - - - false - - - - - - Enabled - - - - - - - TCP Fast Open - - - - - - - Mark - - - - - - - Match Contains - - - - - - - - - - TProxy Mode - - - - - - - - - - - 75 - true - true - - - - Settings will be added to the StreamSettings for matched connections. - - - - - - - - - - - - - - - - Qt::PreventContextMenu - - - PAC Settings - - - true - - - false - - - - - - - true - - - - The system proxy will be configured to use the PAC instead of HTTP and SOCKS. - - - - - + + + Port - - + + 1 @@ -922,140 +662,49 @@ 65535 - 8088 + 18001 + + + + + + + Authentication + + + + + + + Enabled - + - Local IP for PAC + Username - + - 127.0.0.1 + user - + - Use Proxy + Password - - - - HTTP - - - - - SOCKS - - - - - - - - Import GFWList - - - - - - - - - - Mirror: Gitlab - - - - - Github - - - - - Mirror: Pagure - - - - - Mirror: Repo.or.cz - - - - - Mirror: Bitbucket - - - - - Mirror: TuxFamily - - - - - GFWList File - - - - - - - - Download with System Proxy - - - - - - - Go - - - - - - - - - Edit PAC - - - - - - - Open PAC Folder - - - - - - - PAC Access Path - - - - - - - IBeamCursor - - - - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + pass @@ -1064,6 +713,174 @@ + + + + Transparent Proxy Settings + + + true + + + false + + + + + + + 75 + true + true + + + + All settings below will only be applied onto simple connection. + + + + + + + Add Docodemo-door inbound + + + + + + + + + Address + + + + + + + 127.0.0.1 + + + + + + + Port + + + + + + + 1 + + + 65535 + + + 12345 + + + + + + + TCP + + + + + + + UDP + + + + + + + Follow Redirect + + + + + + + + + Override Connection SockOpt Settings + + + true + + + false + + + + + + Enabled + + + + + + + TCP Fast Open + + + + + + + Mark + + + + + + + Match Contains + + + + + + + + + + TProxy Mode + + + + + + + + + + + 75 + true + true + + + + Settings will be added to the StreamSettings for matched connections. + + + + + + + + + + + + @@ -1999,12 +1816,6 @@ p, li { white-space: pre-wrap; } httpAuthCB httpAuthUsernameTxt httpAuthPasswordTxt - pacPortSB - pacProxyTxt - pacProxyCB - gfwListCB - pacGoBtn - pushButton proxyDefaultCb bypassCNCb localDNSCb From 5088347b9ac42fdcdd0c7fd3477511f9697e091f Mon Sep 17 00:00:00 2001 From: ymshenyu <373318510@qq.com> Date: Wed, 1 Apr 2020 17:30:29 +0800 Subject: [PATCH 16/92] Make dmg cool again --- assets/CMakeDMGBackground.tif | Bin 0 -> 95690 bytes cmake/CMakeDMGSetup.scpt | 57 ++++++++++++++++++++++++++++++++++ cmake/deployment.cmake | 2 ++ 3 files changed, 59 insertions(+) create mode 100644 assets/CMakeDMGBackground.tif create mode 100644 cmake/CMakeDMGSetup.scpt diff --git a/assets/CMakeDMGBackground.tif b/assets/CMakeDMGBackground.tif new file mode 100644 index 0000000000000000000000000000000000000000..91c4b1309be02edcd057985b52355b2ed1168424 GIT binary patch literal 95690 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 D+gpN` literal 0 HcmV?d00001 diff --git a/cmake/CMakeDMGSetup.scpt b/cmake/CMakeDMGSetup.scpt new file mode 100644 index 00000000..3789fb69 --- /dev/null +++ b/cmake/CMakeDMGSetup.scpt @@ -0,0 +1,57 @@ +on run argv + set image_name to item 1 of argv + + tell application "Finder" + tell disk image_name + + -- wait for the image to finish mounting + set open_attempts to 0 + repeat while open_attempts < 4 + try + open + delay 1 + set open_attempts to 5 + close + on error errStr number errorNumber + set open_attempts to open_attempts + 1 + delay 10 + end try + end repeat + delay 5 + + -- open the image the first time and save a DS_Store with just + -- background and icon setup + open + set current view of container window to icon view + set theViewOptions to the icon view options of container window + set background picture of theViewOptions to file ".background:background.tif" + set arrangement of theViewOptions to not arranged + set icon size of theViewOptions to 128 + delay 5 + close + + -- next setup the position of the app and Applications symlink + -- plus hide all the window decoration + open + update without registering applications + tell container window + set sidebar width to 0 + set statusbar visible to false + set toolbar visible to false + set the bounds to { 400, 100, 900, 465 } + set position of item "qv2ray.app" to { 133, 200 } + set position of item "Applications" to { 378, 200 } + end tell + update without registering applications + delay 5 + close + + -- one last open and close so you can see everything looks correct + open + delay 5 + close + + end tell + delay 1 +end tell +end run \ No newline at end of file diff --git a/cmake/deployment.cmake b/cmake/deployment.cmake index ec9b2ce5..04f10c3d 100644 --- a/cmake/deployment.cmake +++ b/cmake/deployment.cmake @@ -61,6 +61,8 @@ endif() if(APPLE) set(CPACK_GENERATOR "DragNDrop") + set(CPACK_DMG_DS_STORE_SETUP_SCRIPT "${CMAKE_SOURCE_DIR}/cmake/CMakeDMGSetup.scpt") + set(CPACK_DMG_BACKGROUND_IMAGE "${CMAKE_SOURCE_DIR}/assets/CMakeDMGBackground.tif") endif() include(CPack) From 76c177fa596f39403a8e3c0a40e1f452a3a06f49 Mon Sep 17 00:00:00 2001 From: ymshenyu <373318510@qq.com> Date: Wed, 1 Apr 2020 18:15:12 +0800 Subject: [PATCH 17/92] fix accessibility access --- .github/workflows/build-qv2ray-cmake.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-qv2ray-cmake.yml b/.github/workflows/build-qv2ray-cmake.yml index 623b8996..e5a1bc9e 100644 --- a/.github/workflows/build-qv2ray-cmake.yml +++ b/.github/workflows/build-qv2ray-cmake.yml @@ -81,7 +81,7 @@ jobs: - name: macOS - ${{ matrix.qt_version }} - Build preparation - Install Packages if: matrix.platform == 'macos-latest' run: | - brew install protobuf grpc ninja wget + brew install protobuf grpc ninja wget tccutil wget https://github.com/phracker/MacOSX-SDKs/releases/download/10.15/MacOSX10.14.sdk.tar.xz tar -xf MacOSX10.14.sdk.tar.xz sudo mv -v ./MacOSX10.14.sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk @@ -103,6 +103,7 @@ jobs: shell: bash if: matrix.platform == 'macos-latest' run: | + sudo tccutil --enable com.apple.finder mkdir build cd build cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_SYSROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk -DCMAKE_OSX_DEPLOYMENT_TARGET=10.14 From caa1d8ce2dfd5bda8afeeeee204b827f6f6d456d Mon Sep 17 00:00:00 2001 From: ymshenyu <373318510@qq.com> Date: Wed, 1 Apr 2020 18:43:07 +0800 Subject: [PATCH 18/92] Revert "fix accessibility access" This reverts commit 76c177fa596f39403a8e3c0a40e1f452a3a06f49. --- .github/workflows/build-qv2ray-cmake.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/build-qv2ray-cmake.yml b/.github/workflows/build-qv2ray-cmake.yml index e5a1bc9e..623b8996 100644 --- a/.github/workflows/build-qv2ray-cmake.yml +++ b/.github/workflows/build-qv2ray-cmake.yml @@ -81,7 +81,7 @@ jobs: - name: macOS - ${{ matrix.qt_version }} - Build preparation - Install Packages if: matrix.platform == 'macos-latest' run: | - brew install protobuf grpc ninja wget tccutil + brew install protobuf grpc ninja wget wget https://github.com/phracker/MacOSX-SDKs/releases/download/10.15/MacOSX10.14.sdk.tar.xz tar -xf MacOSX10.14.sdk.tar.xz sudo mv -v ./MacOSX10.14.sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk @@ -103,7 +103,6 @@ jobs: shell: bash if: matrix.platform == 'macos-latest' run: | - sudo tccutil --enable com.apple.finder mkdir build cd build cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_SYSROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk -DCMAKE_OSX_DEPLOYMENT_TARGET=10.14 From 6a947ff0e4a5ce28d72af361cae408520f2a4da8 Mon Sep 17 00:00:00 2001 From: ymshenyu <373318510@qq.com> Date: Wed, 1 Apr 2020 20:20:19 +0800 Subject: [PATCH 19/92] using pre-built ds_store --- assets/DS_Store | Bin 0 -> 10244 bytes cmake/deployment.cmake | 7 ++++++- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 assets/DS_Store diff --git a/assets/DS_Store b/assets/DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..b71b7d79642407c6cd181598404f70c56c6beb08 GIT binary patch literal 10244 zcmeHMU2Ggz6+W}ElikFz$3KOHSZE{{2x?|${abelbUj`>jg;cF4*5~tu4ld1_Tcr* zc6QdT8wCsqB~?+C$Ep>mLQtxT2ZSob13#wpfd>$I=tC7%z+)efsD%20P~kgs@7TK= zQwoX@h&l3^^W8i5o_o%@_nv!aO+=dAs0){=(lZqq)Nd%GzBoRm=kVN3WAp$tFnbJq3OrA*uk_aRb*o^?^2LsHqpyalc zQR=`W`UHUNKy^gGIs-^U$%2yGQsNCrlU*TYS519lXtF!%4b`roI+4SMu&bQYED`~%H)|uAc?@85s=^R+45$+R<1UGzvpsqX{iGpod*x4Glw&oOm`;p z`24EBIUhP(IRk0zwx!{}=ir_JBe8KmtXdtmuzUYe0 zl0Go)d!gfcB8c=!9SfISui|goe#5KOqxMR9gwH$SD)gj>3jSi`Lpq%<>Bp{K9n0lT z8Dr!3Upr-79cMf`F@EhD-X8zd?4wUKzWUq?FMj8{FMsd#UrNt4l|87pEfQ~_tWnlo z+E8KWT$Dd!3waz9u<`*_Np#y)zD^OuOiZXCJ@4V zY$b3jbIZ$h5ngV>R~G6B`PopcpY@h~=-)uN=W3zrd-aPVs6$?Hg23@sguT_G`s}*1 zBBrXYQ!nW~krLQ`J`k)R8eqQWEV{xc#EIPra>%~28zxwUML-$c9%GbO1T+W8>a@^!!kvE|4 zb8&~ifryT(^CGULBiX%i_{_rjar@Qb9lY;V_-DZKc)AYzt$obTH|faJo<3=JH+`JC zsZ0(n(HgB#Kt46dqY7myB$t+{hfY#IJxLohg0~KBkqMNgG4L>O5p@A=qOC`Lbi!&H z&T1d6p9ZaqezmbK>id~W1F2}D@I)j|JQB@?QN)I=`n7f8g~l1I-S{ znCBW=3CvoER%g`A*MJ|6R>NvEqXL?ri9EHWr`BEZ)RLZBk*8LhrP?qCoC*RkQacj1nQwPtjZR~&M9lUxUONG03q1*%6fqUI5BvU8w zPK2;=f!|TM0Q{E1h&4UW`2>_Q&_>-8JArj&lMal(hV>f7SE5rlf&%Byp!n-G`dWEz*R;~i66 zz9EZ|KGwU9@=fqd;2(gw9XmGl`o|VZfXBdmqldW>@r<{VhFIK-N-aaQ>{dnIvOXx` zS+Om&c!*7J8FLZkNEZeinVGVSlMhUqld}`k=IF%aj5#%GkD2#RPEAfu4Nut*4Ci9| z9A)F55ct?u)(uypIeH0(^P=OpM>`vsr<==ij}y(P@?F{NP5R}w-y1*L|9H=_Y?RHZG${a)P8z+e_>bVfg1cbgAovvc zG`M+vE~bAwAH|_*-5Dw_GUodnqYpbvtGMCr#$fTw_Z>%{UA@2f&7YSy4*oP=gA?|2 zabkLO!kiq#${Q_?kDK=N$gDY4oE^1iW@cyY;n8>veuc*dx3592U(@);)Y!`g}XovcLEo;KqtEQ`M4L<&sO~hw=HGV1&`=r0m-K%7x<=gk}>LXESZ1{N^VPu8qk0Ip8;)m;pF?D Rf8j~K|K)0=|BLVczXQ*%Og#Vq literal 0 HcmV?d00001 diff --git a/cmake/deployment.cmake b/cmake/deployment.cmake index 04f10c3d..49a4e2cf 100644 --- a/cmake/deployment.cmake +++ b/cmake/deployment.cmake @@ -61,7 +61,12 @@ endif() if(APPLE) set(CPACK_GENERATOR "DragNDrop") - set(CPACK_DMG_DS_STORE_SETUP_SCRIPT "${CMAKE_SOURCE_DIR}/cmake/CMakeDMGSetup.scpt") + if(DS_STORE_SCRIPT) + set(CPACK_DMG_DS_STORE_SETUP_SCRIPT "${CMAKE_SOURCE_DIR}/cmake/CMakeDMGSetup.scpt") + else() + set(CPACK_DMG_DS_STORE "${CMAKE_SOURCE_DIR}/assets/DS_Store") + endif() + set(CPACK_DMG_BACKGROUND_IMAGE "${CMAKE_SOURCE_DIR}/assets/CMakeDMGBackground.tif") endif() From fcdd607bac20262ea5363b5e4e524c7664d99351 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Wed, 1 Apr 2020 22:40:59 +0800 Subject: [PATCH 20/92] Update main.cpp --- src/main.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index f0da90a2..586c76d3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -229,7 +229,7 @@ int main(int argc, char *argv[]) #endif if (StartupOption.noScaleFactors) { - LOG(MODULE_INIT, "Force set QT_SCALE_FACTOR to 0.") + LOG(MODULE_INIT, "Force set QT_SCALE_FACTOR to 1.") LOG(MODULE_UI, "Original QT_SCALE_FACTOR was: " + qEnvironmentVariable("QT_SCALE_FACTOR")) qputenv("QT_SCALE_FACTOR", "1"); } @@ -237,6 +237,9 @@ int main(int argc, char *argv[]) { LOG(MODULE_INIT, "High DPI scaling is enabled.") QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + QCoreApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor); +#endif } SingleApplication _qApp(argc, argv, false, SingleApplication::User | SingleApplication::ExcludeAppPath | SingleApplication::ExcludeAppVersion); From 37dc46bd1335261aaa7cc9723f49148eb023ddc0 Mon Sep 17 00:00:00 2001 From: DuckSoft Date: Thu, 2 Apr 2020 02:03:26 +0800 Subject: [PATCH 21/92] fixing up --- src/main.cpp | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 586c76d3..3fade15a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -238,7 +238,7 @@ int main(int argc, char *argv[]) LOG(MODULE_INIT, "High DPI scaling is enabled.") QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) - QCoreApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor); + QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); #endif } SingleApplication _qApp(argc, argv, false, @@ -253,25 +253,25 @@ int main(int argc, char *argv[]) bool _result_ = Qv2rayTranslator->InstallTranslation(_lang); LOG(MODULE_UI, "Installing a tranlator from OS: " + _lang + " -- " + (_result_ ? "OK" : "Failed")) // - LOG("LICENCE", NEWLINE // - "This program comes with ABSOLUTELY NO WARRANTY." NEWLINE // - "This is free software, and you are welcome to redistribute it" NEWLINE // - "under certain conditions." NEWLINE // - NEWLINE // - "Copyright (c) 2019-2020 Qv2ray Development Group." NEWLINE // - NEWLINE // - "Libraries that have been used in Qv2ray are listed below (Sorted by date added):" NEWLINE // - "Copyright (c) 2020 xyz347 (@xyz347): X2Struct (Apache)" NEWLINE // - "Copyright (c) 2011 SCHUTZ Sacha (@dridk): QJsonModel (MIT)" NEWLINE // - "Copyright (c) 2020 Nikolaos Ftylitakis (@ftylitak): QZXing (Apache2)" NEWLINE // - "Copyright (c) 2016 Singein (@Singein): ScreenShot (MIT)" NEWLINE // - "Copyright (c) 2020 Itay Grudev (@itay-grudev): SingleApplication (MIT)" NEWLINE // - "Copyright (c) 2020 paceholder (@paceholder): nodeeditor (Qv2ray group modified version) (BSD-3-Clause)" NEWLINE // - "Copyright (c) 2019 TheWanderingCoel (@TheWanderingCoel): ShadowClash (launchatlogin) (GPLv3)" NEWLINE // - "Copyright (c) 2020 Ram Pani (@DuckSoft): QvRPCBridge (WTFPL)" NEWLINE // - "Copyright (c) 2019 ShadowSocks (@shadowsocks): libQtShadowsocks (LGPLv3)" NEWLINE // - "Copyright (c) 2015-2020 qBittorrent (Anton Lashkov) (@qBittorrent): speedplotview (GPLv2)" NEWLINE // - "Copyright (c) 2020 yhirose (@yhirose): cpp-httplib (MIT)" NEWLINE NEWLINE) // + LOG("LICENCE", NEWLINE // + "This program comes with ABSOLUTELY NO WARRANTY." NEWLINE // + "This is free software, and you are welcome to redistribute it" NEWLINE // + "under certain conditions." NEWLINE // + NEWLINE // + "Copyright (c) 2019-2020 Qv2ray Development Group." NEWLINE // + NEWLINE // + "Libraries that have been used in Qv2ray are listed below (Sorted by date added):" NEWLINE // + "Copyright (c) 2020 xyz347 (@xyz347): X2Struct (Apache)" NEWLINE // + "Copyright (c) 2011 SCHUTZ Sacha (@dridk): QJsonModel (MIT)" NEWLINE // + "Copyright (c) 2020 Nikolaos Ftylitakis (@ftylitak): QZXing (Apache2)" NEWLINE // + "Copyright (c) 2016 Singein (@Singein): ScreenShot (MIT)" NEWLINE // + "Copyright (c) 2020 Itay Grudev (@itay-grudev): SingleApplication (MIT)" NEWLINE // + "Copyright (c) 2020 paceholder (@paceholder): nodeeditor (Qv2ray group modified version) (BSD-3-Clause)" NEWLINE // + "Copyright (c) 2019 TheWanderingCoel (@TheWanderingCoel): ShadowClash (launchatlogin) (GPLv3)" NEWLINE // + "Copyright (c) 2020 Ram Pani (@DuckSoft): QvRPCBridge (WTFPL)" NEWLINE // + "Copyright (c) 2019 ShadowSocks (@shadowsocks): libQtShadowsocks (LGPLv3)" NEWLINE // + "Copyright (c) 2015-2020 qBittorrent (Anton Lashkov) (@qBittorrent): speedplotview (GPLv2)" NEWLINE // + "Copyright (c) 2020 yhirose (@yhirose): cpp-httplib (MIT)" NEWLINE NEWLINE) // // LOG(MODULE_INIT, "Qv2ray Start Time: " + QSTRN(QTime::currentTime().msecsSinceStartOfDay())) // From 70b27169bd44ea57e72ab8b271daee4bcb1ad0ed Mon Sep 17 00:00:00 2001 From: ymshenyu <373318510@qq.com> Date: Thu, 2 Apr 2020 15:06:30 +0800 Subject: [PATCH 22/92] Revert "bye" This reverts commit 0380ce8d543a2a4c7e60cf8124c91e911fe74853. --- azure-pipelines.yml | 49 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 azure-pipelines.yml diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 00000000..c9cedf1e --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,49 @@ +# Starter pipeline + +trigger: + branches: + include: + - master + - dev + - dev-azure-pipelines + tags: + include: + - v* + +pool: + vmImage: 'macOS-10.14' + +steps: +- checkout: self + submodules: recursive +- script: | + brew install protobuf grpc ninja qt5 + displayName: Prepare dependencies +- script: | + mkdir build + cd build + cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_DEPLOYMENT_TARGET=10.13 + cmake --build . --parallel $(sysctl -n hw.logicalcpu) + sudo cmake --install . + sudo chmod -Rv a+rw ./ + displayName: Build Qv2ray + env: + Qt5_DIR: /usr/local/opt/qt5/lib/cmake/Qt5 +- script: | + cd build + tar czvf Qv2ray.macOS.tar.gz qv2ray.app + displayName: Create Tarball +- task: PublishBuildArtifacts@1 + inputs: + PathtoPublish: 'build/Qv2ray.macOS.tar.gz' + ArtifactName: 'Qv2ray-macOS' + publishLocation: 'Container' +- task: GitHubRelease@0 + condition: startsWith(variables['Build.SourceBranch'], 'refs/tags/v') + inputs: + gitHubConnection: 'github.com_DuckSoft' + assets: 'build/Qv2ray.macOS.tar.gz' + action: edit + tag: '$(Build.SourceBranchName)' + isPreRelease: true + addChangeLog: false \ No newline at end of file From 70818d85a1e4d69676856f235432b11572943bef Mon Sep 17 00:00:00 2001 From: ymshenyu <373318510@qq.com> Date: Thu, 2 Apr 2020 15:17:24 +0800 Subject: [PATCH 23/92] Revert "test against CMAKE_OSX_DEPLOYMENT_TARGET" This reverts commit 81b24ebdef2b1d0d4f89e555635bf302bb0e39fd. --- azure-pipelines.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index c9cedf1e..3435a8cc 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -19,10 +19,13 @@ steps: - script: | brew install protobuf grpc ninja qt5 displayName: Prepare dependencies +- script: | + xcversion select 10.1 --symlink + displayName: Select Xcode 10.1 as active - script: | mkdir build cd build - cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_DEPLOYMENT_TARGET=10.13 + cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release cmake --build . --parallel $(sysctl -n hw.logicalcpu) sudo cmake --install . sudo chmod -Rv a+rw ./ From 286655631e6f1fbdd6b5493f2552aa325eb11c61 Mon Sep 17 00:00:00 2001 From: ymshenyu <373318510@qq.com> Date: Thu, 2 Apr 2020 15:34:38 +0800 Subject: [PATCH 24/92] Fix macdeployqt --- .github/workflows/build-qv2ray-cmake.yml | 2 +- azure-pipelines.yml | 17 +++++++---------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build-qv2ray-cmake.yml b/.github/workflows/build-qv2ray-cmake.yml index 623b8996..023d16d4 100644 --- a/.github/workflows/build-qv2ray-cmake.yml +++ b/.github/workflows/build-qv2ray-cmake.yml @@ -105,7 +105,7 @@ jobs: run: | mkdir build cd build - cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_SYSROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk -DCMAKE_OSX_DEPLOYMENT_TARGET=10.14 + cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release sudo cmake --build . --target package --parallel $(sysctl -n hw.logicalcpu) cp qv2ray-*.dmg ../ - name: macOS - Get package name diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 3435a8cc..7775a400 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -5,7 +5,6 @@ trigger: include: - master - dev - - dev-azure-pipelines tags: include: - v* @@ -26,26 +25,24 @@ steps: mkdir build cd build cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release - cmake --build . --parallel $(sysctl -n hw.logicalcpu) - sudo cmake --install . - sudo chmod -Rv a+rw ./ + sudo cmake --build . --target package --parallel $(sysctl -n hw.logicalcpu) displayName: Build Qv2ray env: Qt5_DIR: /usr/local/opt/qt5/lib/cmake/Qt5 + PATH: /usr/local/opt/qt5/bin:$PATH - script: | - cd build - tar czvf Qv2ray.macOS.tar.gz qv2ray.app - displayName: Create Tarball + cp build/qv2ray-legacy.dmg ./ + displayName: Copy binary - task: PublishBuildArtifacts@1 inputs: - PathtoPublish: 'build/Qv2ray.macOS.tar.gz' - ArtifactName: 'Qv2ray-macOS' + PathtoPublish: 'qv2ray-legacy.dmg' + ArtifactName: 'qv2ray-legacy.dmg' publishLocation: 'Container' - task: GitHubRelease@0 condition: startsWith(variables['Build.SourceBranch'], 'refs/tags/v') inputs: gitHubConnection: 'github.com_DuckSoft' - assets: 'build/Qv2ray.macOS.tar.gz' + assets: 'qv2ray-legacy.dmg' action: edit tag: '$(Build.SourceBranchName)' isPreRelease: true From ccc465dd9ef393f56054c248d500dce04685ee16 Mon Sep 17 00:00:00 2001 From: ymshenyu <373318510@qq.com> Date: Thu, 2 Apr 2020 15:43:06 +0800 Subject: [PATCH 25/92] fix build --- azure-pipelines.yml | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 7775a400..5ec96069 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -31,19 +31,10 @@ steps: Qt5_DIR: /usr/local/opt/qt5/lib/cmake/Qt5 PATH: /usr/local/opt/qt5/bin:$PATH - script: | - cp build/qv2ray-legacy.dmg ./ + cp build/qv2ray-*.dmg ./qv2ray-legacy.dmg displayName: Copy binary - task: PublishBuildArtifacts@1 inputs: PathtoPublish: 'qv2ray-legacy.dmg' ArtifactName: 'qv2ray-legacy.dmg' - publishLocation: 'Container' -- task: GitHubRelease@0 - condition: startsWith(variables['Build.SourceBranch'], 'refs/tags/v') - inputs: - gitHubConnection: 'github.com_DuckSoft' - assets: 'qv2ray-legacy.dmg' - action: edit - tag: '$(Build.SourceBranchName)' - isPreRelease: true - addChangeLog: false \ No newline at end of file + publishLocation: 'Container' \ No newline at end of file From 51174e5783b724d8f5cb4c98adb5e085e7191b68 Mon Sep 17 00:00:00 2001 From: ymshenyu <373318510@qq.com> Date: Thu, 2 Apr 2020 15:49:16 +0800 Subject: [PATCH 26/92] fix path --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 5ec96069..0c8c4056 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -22,6 +22,7 @@ steps: xcversion select 10.1 --symlink displayName: Select Xcode 10.1 as active - script: | + PATH=/usr/local/opt/qt5/bin:$PATH mkdir build cd build cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release @@ -29,7 +30,6 @@ steps: displayName: Build Qv2ray env: Qt5_DIR: /usr/local/opt/qt5/lib/cmake/Qt5 - PATH: /usr/local/opt/qt5/bin:$PATH - script: | cp build/qv2ray-*.dmg ./qv2ray-legacy.dmg displayName: Copy binary From 0b7c2bd6317e8b308feaf1c274c9968878a60401 Mon Sep 17 00:00:00 2001 From: Guobang Bi <373318510@qq.com> Date: Fri, 3 Apr 2020 11:11:11 +0800 Subject: [PATCH 27/92] azure pipeline: finalizing --- azure-pipelines.yml | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 0c8c4056..8e9582ac 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -19,8 +19,8 @@ steps: brew install protobuf grpc ninja qt5 displayName: Prepare dependencies - script: | - xcversion select 10.1 --symlink - displayName: Select Xcode 10.1 as active + xcversion select 9.4.1 --symlink + displayName: Select Xcode 9.4.1 as active - script: | PATH=/usr/local/opt/qt5/bin:$PATH mkdir build @@ -37,4 +37,13 @@ steps: inputs: PathtoPublish: 'qv2ray-legacy.dmg' ArtifactName: 'qv2ray-legacy.dmg' - publishLocation: 'Container' \ No newline at end of file + publishLocation: 'Container' +- task: GitHubRelease@0 + condition: startsWith(variables['Build.SourceBranch'], 'refs/tags/v') + inputs: + gitHubConnection: 'github.com_DuckSoft' + assets: 'qv2ray-legacy.dmg' + action: edit + tag: '$(Build.SourceBranchName)' + isPreRelease: true + addChangeLog: false \ No newline at end of file From 13b96e0b3c543ab1d0446d06d48ea277a3ca05ef Mon Sep 17 00:00:00 2001 From: DuckSoft Date: Fri, 3 Apr 2020 11:15:52 +0800 Subject: [PATCH 28/92] azure: update connection --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 8e9582ac..ce6b6c92 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -41,7 +41,7 @@ steps: - task: GitHubRelease@0 condition: startsWith(variables['Build.SourceBranch'], 'refs/tags/v') inputs: - gitHubConnection: 'github.com_DuckSoft' + gitHubConnection: 'Qv2ray' assets: 'qv2ray-legacy.dmg' action: edit tag: '$(Build.SourceBranchName)' From 683770eee0bd3ab1e167b2f7143299e251f9c4b9 Mon Sep 17 00:00:00 2001 From: DuckSoft Date: Fri, 3 Apr 2020 11:38:27 +0800 Subject: [PATCH 29/92] Mag1c! --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index ce6b6c92..1d009cf5 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -41,7 +41,7 @@ steps: - task: GitHubRelease@0 condition: startsWith(variables['Build.SourceBranch'], 'refs/tags/v') inputs: - gitHubConnection: 'Qv2ray' + gitHubConnection: 'GitHub - DuckSoft' assets: 'qv2ray-legacy.dmg' action: edit tag: '$(Build.SourceBranchName)' From 981c63a3104199e6f181a4bae43320a32be0cdc1 Mon Sep 17 00:00:00 2001 From: Guobang Bi <373318510@qq.com> Date: Fri, 3 Apr 2020 11:45:31 +0800 Subject: [PATCH 30/92] good bye high sierra --- azure-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 1d009cf5..3033544f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -19,8 +19,8 @@ steps: brew install protobuf grpc ninja qt5 displayName: Prepare dependencies - script: | - xcversion select 9.4.1 --symlink - displayName: Select Xcode 9.4.1 as active + xcversion select 10.3 --symlink + displayName: Select Xcode 10.3 as active - script: | PATH=/usr/local/opt/qt5/bin:$PATH mkdir build From 18e94ba8e0d6267472489ef72d240d2e2cd78cab Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Sat, 4 Apr 2020 00:00:00 +0800 Subject: [PATCH 31/92] fix: fixed #493 --- src/common/QRCodeHelper.cpp | 22 +++++++++++----------- src/common/QRCodeHelper.hpp | 6 +++--- src/ui/w_ImportConfig.cpp | 2 ++ 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/common/QRCodeHelper.cpp b/src/common/QRCodeHelper.cpp index 096d37e1..87cc2486 100644 --- a/src/common/QRCodeHelper.cpp +++ b/src/common/QRCodeHelper.cpp @@ -7,14 +7,16 @@ #include "TextUtfEncoding.h" #include "base/Qv2rayBase.hpp" -namespace Qv2ray::components +namespace Qv2ray::common { using namespace ZXing; QString DecodeQRCode(const QImage &source) { + if (source.isNull()) + return ""; QImage img = source.copy(); - auto result = - ReadBarcode(img.width(), img.height(), img.bits(), img.width() * 4, 4, 0, 1, 2, { BarcodeFormatFromString("") }, true, true); + const auto result = + ReadBarcode(img.width(), img.height(), img.bits(), img.width() * 4, 4, 0, 1, 2, { ZXing::BarcodeFormat::QR_CODE }, true, true); if (result.isValid()) { @@ -34,17 +36,15 @@ namespace Qv2ray::components int eccLevel = 1; try { - auto barcodeFormat = BarcodeFormatFromString("QR_CODE"); - - MultiFormatWriter writer(barcodeFormat); + MultiFormatWriter writer(ZXing::BarcodeFormat::QR_CODE); writer.setMargin(1); writer.setEccLevel(eccLevel); - auto bitmap = writer.encode(content.toStdWString(), size.width(), size.height()); - auto BM = bitmap.toByteMatrix(); + const auto bitmap = writer.encode(content.toStdWString(), size.width(), size.height()); + const auto BM = bitmap.toByteMatrix(); // - const QRgb black = qRgba(0, 0, 0, 255); - const QRgb white = qRgba(255, 255, 255, 255); + const auto black = qRgba(0, 0, 0, 255); + const auto white = qRgba(255, 255, 255, 255); // auto image = QImage(bitmap.width(), bitmap.width(), QImage::Format_ARGB32); image.fill(white); @@ -67,4 +67,4 @@ namespace Qv2ray::components return {}; } } -} // namespace Qv2ray::components +} // namespace Qv2ray::common diff --git a/src/common/QRCodeHelper.hpp b/src/common/QRCodeHelper.hpp index 49576918..b69ecacc 100644 --- a/src/common/QRCodeHelper.hpp +++ b/src/common/QRCodeHelper.hpp @@ -2,9 +2,9 @@ #include #include -namespace Qv2ray::components +namespace Qv2ray::common { QString DecodeQRCode(const QImage &img); QImage EncodeQRCode(const QString &content, const QSize &size); -} // namespace Qv2ray::components -using namespace Qv2ray::components; +} // namespace Qv2ray::common +using namespace Qv2ray::common; diff --git a/src/ui/w_ImportConfig.cpp b/src/ui/w_ImportConfig.cpp index 7d82dd8b..2f4f8758 100644 --- a/src/ui/w_ImportConfig.cpp +++ b/src/ui/w_ImportConfig.cpp @@ -213,6 +213,8 @@ void ImportConfigWindow::on_selectImageBtn_clicked() imageFileEdit->setText(dir); // QFile file(dir); + if (!file.exists()) + return; file.open(QFile::OpenModeFlag::ReadOnly); auto buf = file.readAll(); file.close(); From bcf04adda4425de574cdb71372c1d06c70da9e42 Mon Sep 17 00:00:00 2001 From: ymshenyu <373318510@qq.com> Date: Fri, 3 Apr 2020 17:51:26 +0800 Subject: [PATCH 32/92] update .travis.yml --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 9e6b4b19..566071f4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -49,4 +49,4 @@ deploy: channel: beta skip_cleanup: true on: - branch: /^v\d+\.\d+(\.\d+)?(-\S*)?$/ \ No newline at end of file + tags: true \ No newline at end of file From 661145f2c49be0cbf0ce6908d7180fe0e7e62654 Mon Sep 17 00:00:00 2001 From: ymshenyu <373318510@qq.com> Date: Mon, 6 Apr 2020 09:49:09 +0800 Subject: [PATCH 33/92] update snapcraft.yaml --- snap/snapcraft.yaml | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 40127b21..a57d667b 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -83,6 +83,7 @@ parts: sed -i 's|^Icon=.*|Icon=/usr/share/icons/hicolor/256x256/apps/qv2ray.png|g' assets/qv2ray.desktop after: - desktop-qt5 + - ppa desktop-qt5: source: https://github.com/ubuntu/snapcraft-desktop-helpers.git @@ -108,8 +109,21 @@ parts: - locales-all - xdg-user-dirs - fcitx-frontend-qt5 - + after: + - ppa + qt5-gtk-platform: plugin: nil stage-packages: - qt5-gtk-platformtheme + after: + - ppa + + ppa: + plugin: nil + build-packages: + - software-properties-common + - dirmngr + override-build: + sudo add-apt-repository -y ppa:ymshenyu/qv2ray-deps + sudo apt-get dist-upgrade -y \ No newline at end of file From 96344e5e4397b80ce2be1fa426b5020d5f9af445 Mon Sep 17 00:00:00 2001 From: ymshenyu <373318510@qq.com> Date: Mon, 6 Apr 2020 10:11:43 +0800 Subject: [PATCH 34/92] update .travis.yml --- .travis.yml | 35 +++++++++++------------------------ 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/.travis.yml b/.travis.yml index 566071f4..8e65e9f9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,17 +1,6 @@ language: shell os: linux dist: bionic -arch: - - amd64 - - arm64 - -git: - depth: false - -branches: - only: - - dev - - /^v\d+\.\d+(\.\d+)?(-\S*)?$/ env: global: @@ -23,16 +12,14 @@ addons: - name: snapcraft channel: stable confinement: classic - -before_install: - - echo "deb http://ppa.launchpad.net/ymshenyu/qv2ray-deps/ubuntu bionic main" | sudo tee -a /etc/apt/sources.list - - echo "deb http://archive.neon.kde.org/unstable bionic main" | sudo tee -a /etc/apt/sources.list - - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 281F24E574404629AA3BDA1A4F10C386C55CDB04 - - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys E6D4736255751E5D - - sudo apt-get update -qq + - name: lxd + channel: stable script: - - snapcraft --destructive-mode + - sudo apt-get autoremove lxd --purge + - sudo /snap/bin/lxd waitready + - sudo /snap/bin/lxd init --auto + - sudo snapcraft --use-lxd after_failure: - sudo journalctl -u snapd @@ -44,9 +31,9 @@ deploy: skip_cleanup: true on: branch: dev - - provider: snap - snap: qv2ray_*.snap - channel: beta - skip_cleanup: true + - provider: launchpad + slug: "~ymshenyu/qv2ray/+git/trunk" + oauth_token: $LAUNCHPAD_OAUTH_TOKEN + oauth_token_secret: $LAUNCHPAD_OAUTH_TOKEN_SECRET on: - tags: true \ No newline at end of file + all_branches: true \ No newline at end of file From bed9703b357d7f3d5dc48464e9652c42fcc1e3d0 Mon Sep 17 00:00:00 2001 From: ymshenyu <373318510@qq.com> Date: Mon, 6 Apr 2020 10:16:20 +0800 Subject: [PATCH 35/92] fix typo --- snap/snapcraft.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index a57d667b..8ebebe22 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -124,6 +124,6 @@ parts: build-packages: - software-properties-common - dirmngr - override-build: + override-build: | sudo add-apt-repository -y ppa:ymshenyu/qv2ray-deps sudo apt-get dist-upgrade -y \ No newline at end of file From 9d446ff4c2c1159c7ca88837b9a63abbc5b3d9b8 Mon Sep 17 00:00:00 2001 From: ymshenyu <373318510@qq.com> Date: Mon, 6 Apr 2020 10:42:16 +0800 Subject: [PATCH 36/92] update .travis.yml --- .travis.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8e65e9f9..59026963 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,12 +25,6 @@ after_failure: - sudo journalctl -u snapd deploy: - - provider: snap - snap: qv2ray_*.snap - channel: edge - skip_cleanup: true - on: - branch: dev - provider: launchpad slug: "~ymshenyu/qv2ray/+git/trunk" oauth_token: $LAUNCHPAD_OAUTH_TOKEN From 91ce0b4499d74a05b778c8cd05f7117e85a40b9b Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Mon, 6 Apr 2020 16:51:30 +0800 Subject: [PATCH 37/92] update: updated submodules --- .gitmodules | 3 --- 3rdparty/QNodeEditor | 2 +- 3rdparty/SingleApplication | 2 +- 3rdparty/cpp-httplib | 1 - 4 files changed, 2 insertions(+), 6 deletions(-) delete mode 160000 3rdparty/cpp-httplib diff --git a/.gitmodules b/.gitmodules index f9abe3b3..acd86d16 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,9 +10,6 @@ [submodule "libs/libqvb"] path = libs/libqvb url = https://github.com/Qv2ray/QvRPCBridge -[submodule "3rdparty/cpp-httplib"] - path = 3rdparty/cpp-httplib - url = https://github.com/yhirose/cpp-httplib [submodule "libs/puresource"] path = libs/puresource url = https://github.com/Qv2ray/PureSource/ diff --git a/3rdparty/QNodeEditor b/3rdparty/QNodeEditor index f3f17e9a..db07dd4f 160000 --- a/3rdparty/QNodeEditor +++ b/3rdparty/QNodeEditor @@ -1 +1 @@ -Subproject commit f3f17e9a04e3db67e4a717fd2984754fd4555c24 +Subproject commit db07dd4ffcbfdd62431584d499928e45b5864f40 diff --git a/3rdparty/SingleApplication b/3rdparty/SingleApplication index 4abe20af..4baf2e74 160000 --- a/3rdparty/SingleApplication +++ b/3rdparty/SingleApplication @@ -1 +1 @@ -Subproject commit 4abe20afbfa5695ac7a9bce1298943b645aeffe9 +Subproject commit 4baf2e74f64c9a6ce36d456491bb41d0f2ae999e diff --git a/3rdparty/cpp-httplib b/3rdparty/cpp-httplib deleted file mode 160000 index dc13cde8..00000000 --- a/3rdparty/cpp-httplib +++ /dev/null @@ -1 +0,0 @@ -Subproject commit dc13cde820621806e415034ae45163ebcc39b66e From 71bed7992af4a0f39e95b79c4721280fa1495221 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Mon, 6 Apr 2020 22:21:32 +0800 Subject: [PATCH 38/92] update: adapt interface draft-2 --- CMakeLists.txt | 3 + makespec/BUILDVERSION | 2 +- src/components/plugins/QvPluginHost.cpp | 100 +++++++++++----------- src/components/plugins/QvPluginHost.hpp | 9 +- src/components/plugins/interface | 2 +- src/core/handler/ConfigHandler.cpp | 30 ++----- src/core/handler/V2rayInstanceHandler.cpp | 16 ++-- src/core/kernel/APIBackend.cpp | 5 +- 8 files changed, 80 insertions(+), 87 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 576ed08e..2aade55a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -249,6 +249,9 @@ set(QV2RAY_SOURCES src/components/latency/QvTCPing.hpp src/components/plugins/toolbar/QvToolbar.hpp src/components/plugins/interface/QvPluginInterface.hpp + src/components/plugins/interface/QvPluginInterfaceModels.hpp + src/components/plugins/interface/Qv2rayPluginObjects.hpp + src/components/plugins/interface/Qv2rayPluginProcessor.hpp src/components/plugins/QvPluginHost.hpp src/components/proxy/QvProxyConfigurator.hpp src/components/route/RouteSchemeIO.hpp diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index d0d8e98e..aeb955e7 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5208 \ No newline at end of file +5223 \ No newline at end of file diff --git a/src/components/plugins/QvPluginHost.cpp b/src/components/plugins/QvPluginHost.cpp index 3b780b1c..02c3d063 100644 --- a/src/components/plugins/QvPluginHost.cpp +++ b/src/components/plugins/QvPluginHost.cpp @@ -6,6 +6,7 @@ #include "core/settings/SettingsBackend.hpp" #include + namespace Qv2ray::components::plugins { QvPluginHost::QvPluginHost(QObject *parent) : QObject(parent) @@ -83,8 +84,7 @@ namespace Qv2ray::components::plugins { case SPECIAL_TYPE_NONE: types << tr("No Special Type"); break; case SPECIAL_TYPE_KERNEL: types << tr("Connection Kernel"); break; - case SPECIAL_TYPE_GENERATION: types << tr("Final Configuration Parser"); break; - case SPECIAL_TYPE_SERIALIZATION: types << tr("Connection String Serializer/Deserializer"); break; + case SPECIAL_TYPE_SERIALIZOR: types << tr("Connection String Serializer/Deserializer"); break; default: types << tr("Unknown/unsupported plugin type."); break; } } @@ -98,10 +98,11 @@ namespace Qv2ray::components::plugins { switch (hook) { - case HOOK_TYPE_NONE: hooks << tr("No hook"); break; - case HOOK_TYPE_STATE_EVENTS: hooks << tr("Connection State Change"); break; - case HOOK_TYPE_CONFIG_EVENTS: hooks << tr("Connection Change"); break; - case HOOK_TYPE_STATS_EVENTS: hooks << tr("Statistics Event"); break; + case PROCESSTYPE_NONE: hooks << tr("No hook"); break; + case PROCESSTYPE_CONNECTIVITY: hooks << tr("Connection State Change"); break; + case PROCESSTYPE_ITEM: hooks << tr("Connection Change"); break; + case PROCESSTYPE_STATS: hooks << tr("Statistics Event"); break; + case PROCESSTYPE_SYSTEMPROXY: hooks << tr("System Proxy"); break; default: hooks << tr("Unknown/unsupported hook type."); break; } } @@ -169,52 +170,6 @@ namespace Qv2ray::components::plugins } plugins.clear(); } - - void QvPluginHost::SendHook(QV2RAY_PLUGIN_HOOK_TYPE type, QV2RAY_PLUGIN_HOOK_SUBTYPE subtype, QVariant &data) - { - for (auto name : plugins.keys()) - { - const auto info = plugins[name]; - if (!info.isLoaded) - { - DEBUG(MODULE_PLUGINHOST, "The plugin has not been loaded.") - continue; - } - - auto types = info.pluginInterface->SpecialPluginType(); - if (types.contains(SPECIAL_TYPE_KERNEL)) - { - info.pluginInterface->PluginHooks(); - // A kernel will only listens on pre-connection, post-connection, pre-disconnection, post-disconnection - if (type == HOOK_TYPE_STATE_EVENTS && (subtype == HOOK_STYPE_PRE_CONNECTING || subtype == HOOK_STYPE_PRE_DISCONNECTING)) - { - info.pluginInterface->ProcessHook(type, subtype, &data); - } - } - if (types.contains(SPECIAL_TYPE_GENERATION)) - { - // TODO - } - if (types.contains(SPECIAL_TYPE_SERIALIZATION)) - { - // TODO - } - if (types.contains(SPECIAL_TYPE_NONE)) - { - // This plugin has no special type. - for (auto hook : info.pluginInterface->PluginHooks()) - { - if (hook == HOOK_TYPE_NONE) - continue; - if (hook == type) - { - info.pluginInterface->ProcessHook(type, subtype, &data); - } - } - } - } - } - bool QvPluginHost::InitializePlugin(const QString &internalName) { auto &plugin = plugins[internalName]; @@ -252,4 +207,45 @@ namespace Qv2ray::components::plugins ClearPlugins(); } + // ================== BEGIN SEND EVENTS ================== + void QvPluginHost::Send_ConnectionStatsEvent(const QvConnectionStatsEventObject &object) + { + for (auto &plugin : plugins) + { + if (plugin.pluginInterface->PluginHooks().contains(QV2RAY_PLUGIN_PROCESSTYPE::PROCESSTYPE_STATS)) + { + plugin.pluginInterface->PluginProcessor()->ProcessEvent_ConnectionStats(object); + } + } + } + void QvPluginHost::Send_ConnectivityEvent(const QvConnectivityEventObject &object) + { + for (auto &plugin : plugins) + { + if (plugin.pluginInterface->PluginHooks().contains(QV2RAY_PLUGIN_PROCESSTYPE::PROCESSTYPE_CONNECTIVITY)) + { + plugin.pluginInterface->PluginProcessor()->ProcessEvent_Connectivity(object); + } + } + } + void QvPluginHost::Send_ItemEvent(const QvItemEventObject &object) + { + for (auto &plugin : plugins) + { + if (plugin.pluginInterface->PluginHooks().contains(QV2RAY_PLUGIN_PROCESSTYPE::PROCESSTYPE_ITEM)) + { + plugin.pluginInterface->PluginProcessor()->ProcessEvent_Item(object); + } + } + } + void QvPluginHost::Send_SystemProxyEvent(const QvSystemProxyEventObject &object) + { + for (auto &plugin : plugins) + { + if (plugin.pluginInterface->PluginHooks().contains(QV2RAY_PLUGIN_PROCESSTYPE::PROCESSTYPE_SYSTEMPROXY)) + { + plugin.pluginInterface->PluginProcessor()->ProcessEvent_SystemProxy(object); + } + } + } } // namespace Qv2ray::components::plugins diff --git a/src/components/plugins/QvPluginHost.hpp b/src/components/plugins/QvPluginHost.hpp index ffef96b0..2648f24b 100644 --- a/src/components/plugins/QvPluginHost.hpp +++ b/src/components/plugins/QvPluginHost.hpp @@ -7,6 +7,7 @@ class QPluginLoader; +using namespace Qv2rayPlugin; namespace Qv2ray::components::plugins { struct QvPluginInfo @@ -37,8 +38,12 @@ namespace Qv2ray::components::plugins { return plugins.value(internalName); } - void SendHook(QV2RAY_PLUGIN_HOOK_TYPE type, QV2RAY_PLUGIN_HOOK_SUBTYPE subtype, QVariant &data); - + // + void Send_ConnectionStatsEvent(const QvConnectionStatsEventObject &object); + void Send_ConnectivityEvent(const QvConnectivityEventObject &object); + void Send_ItemEvent(const QvItemEventObject &object); + void Send_SystemProxyEvent(const QvSystemProxyEventObject &object); + // private slots: void QvPluginLog(const QString &log); void QvPluginMessageBox(const QString &message); diff --git a/src/components/plugins/interface b/src/components/plugins/interface index 8e594c28..e60b7a06 160000 --- a/src/components/plugins/interface +++ b/src/components/plugins/interface @@ -1 +1 @@ -Subproject commit 8e594c281f7c1a24a4a50d9e181200f3552a3263 +Subproject commit e60b7a06c9b9ba79498500a07b20d2bef44da34e diff --git a/src/core/handler/ConfigHandler.cpp b/src/core/handler/ConfigHandler.cpp index d11b2f66..4137c288 100644 --- a/src/core/handler/ConfigHandler.cpp +++ b/src/core/handler/ConfigHandler.cpp @@ -215,8 +215,7 @@ namespace Qv2ray::core::handlers connections[id].upLinkData = 0; connections[id].downLinkData = 0; emit OnStatsAvailable(id, 0, 0, 0, 0); - auto v = QVariant::fromValue(QList{ 0, 0, 0, 0 }); - PluginHost->SendHook(HOOK_TYPE_STATS_EVENTS, HOOK_STYPE_STATS_CHANGED, v); + PluginHost->Send_ConnectionStatsEvent({ GetDisplayName(currentConnectionId), 0, 0, 0, 0 }); return {}; } @@ -224,8 +223,7 @@ namespace Qv2ray::core::handlers { CheckConnectionExistance(id); OnConnectionRenamed(id, connections[id].displayName, newName); - auto v = QVariant::fromValue(QList{ connections[id].displayName, newName }); - PluginHost->SendHook(HOOK_TYPE_CONFIG_EVENTS, HOOK_STYPE_RENAMED, v); + PluginHost->Send_ItemEvent({ newName, connections[id].displayName, ConnectionEvent_Renamed }); connections[id].displayName = newName; CHSaveConfigData_p(); return {}; @@ -237,8 +235,7 @@ namespace Qv2ray::core::handlers QFile connectionFile((groups[groupId].isSubscription ? QV2RAY_SUBSCRIPTION_DIR : QV2RAY_CONNECTIONS_DIR) + groupId.toString() + "/" + id.toString() + QV2RAY_CONFIG_FILE_EXTENSION); // - auto v = QVariant::fromValue(QList{ connections[id].displayName }); - PluginHost->SendHook(HOOK_TYPE_CONFIG_EVENTS, HOOK_STYPE_REMOVED, v); + PluginHost->Send_ItemEvent({ connections[id].displayName, "", ConnectionEvent_Deleted }); connections.remove(id); groups[groupId].connections.removeAll(id); // @@ -286,8 +283,7 @@ namespace Qv2ray::core::handlers groups[newGroupId].connections.append(id); connections[id].groupId = newGroupId; // - auto v = QVariant::fromValue(QList{ connections[id].displayName }); - PluginHost->SendHook(HOOK_TYPE_CONFIG_EVENTS, HOOK_STYPE_UPDATED, v); + PluginHost->Send_ItemEvent({ connections[id].displayName, "", ConnectionEvent_Updated }); // emit OnConnectionGroupChanged(id, oldgid, newGroupId); // @@ -318,8 +314,7 @@ namespace Qv2ray::core::handlers QDir(QV2RAY_CONNECTIONS_DIR + id.toString()).removeRecursively(); } // - auto v = QVariant::fromValue(QList{ groups[id].displayName }); - PluginHost->SendHook(HOOK_TYPE_CONFIG_EVENTS, HOOK_STYPE_REMOVED, v); + PluginHost->Send_ItemEvent({ groups[id].displayName, "", ConnectionEvent_Deleted }); // groups.remove(id); CHSaveConfigData_p(); @@ -396,9 +391,6 @@ namespace Qv2ray::core::handlers CheckConnectionExistanceEx(result.connectionId, nothing); connections[result.connectionId].latency = result.avg; emit OnLatencyTestFinished(result.connectionId, result.avg); - // - auto v = QVariant::fromValue(QList{ result.avg, result.best, result.worst }); - PluginHost->SendHook(HOOK_TYPE_STATS_EVENTS, HOOK_STYPE_LATENCY_UPDATED, v); } bool QvConfigHandler::UpdateConnection(const ConnectionId &id, const CONFIGROOT &root, bool skipRestart) @@ -415,8 +407,7 @@ namespace Qv2ray::core::handlers connectionRootCache[id] = root; // emit OnConnectionModified(id); - auto v = QVariant::fromValue(QList{ connections[id].displayName }); - PluginHost->SendHook(HOOK_TYPE_CONFIG_EVENTS, HOOK_STYPE_MODIFIED, v); + PluginHost->Send_ItemEvent({ connections[id].displayName, "", ConnectionEvent_Updated }); if (!skipRestart && id == currentConnectionId) { emit RestartConnection(); @@ -430,8 +421,7 @@ namespace Qv2ray::core::handlers groups[id].displayName = displayName; groups[id].isSubscription = isSubscription; groups[id].importDate = system_clock::to_time_t(system_clock::now()); - auto v = QVariant::fromValue(QList{ groups[id].displayName }); - PluginHost->SendHook(HOOK_TYPE_CONFIG_EVENTS, HOOK_STYPE_CREATED, v); + PluginHost->Send_ItemEvent({ displayName, "", ConnectionEvent_Created }); emit OnGroupCreated(id, displayName); CHSaveConfigData_p(); return id; @@ -445,8 +435,7 @@ namespace Qv2ray::core::handlers return tr("Group does not exist"); } OnGroupRenamed(id, groups[id].displayName, newName); - auto v = QVariant::fromValue(QList{ groups[id].displayName, newName }); - PluginHost->SendHook(HOOK_TYPE_CONFIG_EVENTS, HOOK_STYPE_RENAMED, v); + PluginHost->Send_ItemEvent({ newName, groups[id].displayName, ConnectionEvent_Renamed }); groups[id].displayName = newName; return {}; } @@ -614,8 +603,7 @@ namespace Qv2ray::core::handlers connections[newId].importDate = system_clock::to_time_t(system_clock::now()); connections[newId].displayName = displayName; emit OnConnectionCreated(newId, displayName); - auto v = QVariant::fromValue(QList{ displayName }); - PluginHost->SendHook(HOOK_TYPE_CONFIG_EVENTS, HOOK_STYPE_CREATED, v); + PluginHost->Send_ItemEvent({ displayName, "", ConnectionEvent_Created }); UpdateConnection(newId, root); CHSaveConfigData_p(); return newId; diff --git a/src/core/handler/V2rayInstanceHandler.cpp b/src/core/handler/V2rayInstanceHandler.cpp index d5990503..5364dfd7 100644 --- a/src/core/handler/V2rayInstanceHandler.cpp +++ b/src/core/handler/V2rayInstanceHandler.cpp @@ -9,19 +9,18 @@ optional QvConfigHandler::CHStartConnection_p(const ConnectionId &id, c auto fullConfig = GenerateRuntimeConfig(root); // - auto v = QVariant::fromValue(QList{ connections[id].displayName }); - PluginHost->SendHook(HOOK_TYPE_STATE_EVENTS, HOOK_STYPE_PRE_CONNECTING, v); + PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), QvConnecticity_Connecting }); auto result = vCoreInstance->StartConnection(id, fullConfig); if (!result.has_value()) { currentConnectionId = id; emit OnConnected(currentConnectionId); - PluginHost->SendHook(HOOK_TYPE_STATE_EVENTS, HOOK_STYPE_POST_CONNECTED, v); + PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), QvConnecticity_Connected }); } else { - PluginHost->SendHook(HOOK_TYPE_STATE_EVENTS, HOOK_STYPE_POST_DISCONNECTED, v); + PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), QvConnecticity_Disconnected }); } return result; } @@ -30,14 +29,13 @@ void QvConfigHandler::CHStopConnection_p() { if (vCoreInstance->KernelStarted) { - auto v = QVariant::fromValue(QList{ connections[currentConnectionId].displayName }); - PluginHost->SendHook(HOOK_TYPE_STATE_EVENTS, HOOK_STYPE_PRE_DISCONNECTING, v); + PluginHost->Send_ConnectivityEvent({ GetDisplayName(currentConnectionId), QvConnecticity_Disconnecting }); vCoreInstance->StopConnection(); // Copy ConnectionId id = currentConnectionId; currentConnectionId = NullConnectionId; emit OnDisconnected(id); - PluginHost->SendHook(HOOK_TYPE_STATE_EVENTS, HOOK_STYPE_POST_DISCONNECTED, v); + PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), QvConnecticity_Disconnected }); } else { @@ -50,8 +48,8 @@ void QvConfigHandler::OnStatsDataArrived(const ConnectionId &id, const quint64 u connections[id].upLinkData += uploadSpeed; connections[id].downLinkData += downloadSpeed; emit OnStatsAvailable(id, uploadSpeed, downloadSpeed, connections[id].upLinkData, connections[id].downLinkData); - auto v = QVariant::fromValue(QList() << uploadSpeed << downloadSpeed << connections[id].upLinkData << connections[id].downLinkData); - PluginHost->SendHook(HOOK_TYPE_STATS_EVENTS, HOOK_STYPE_STATS_CHANGED, v); + PluginHost->Send_ConnectionStatsEvent( + { GetDisplayName(currentConnectionId), uploadSpeed, downloadSpeed, connections[id].upLinkData, connections[id].downLinkData }); } void QvConfigHandler::OnVCoreCrashed(const ConnectionId &id) diff --git a/src/core/kernel/APIBackend.cpp b/src/core/kernel/APIBackend.cpp index d8dc0bbb..96b87f6c 100644 --- a/src/core/kernel/APIBackend.cpp +++ b/src/core/kernel/APIBackend.cpp @@ -105,7 +105,6 @@ namespace Qv2ray::core::kernel if (running) { - apiFailedCounter = 0; emit OnDataReady(value_up, value_down); } @@ -169,6 +168,10 @@ namespace Qv2ray::core::kernel LOG(MODULE_VCORE, "API call returns: " + QSTRN(status.error_code()) + " (" + QString::fromStdString(status.error_message()) + ")") apiFailedCounter++; } + else + { + apiFailedCounter = 0; + } qint64 data = response.stat().value(); #else From d0cedcd6844884a8464324870f05c5a8959cde58 Mon Sep 17 00:00:00 2001 From: ymshenyu <373318510@qq.com> Date: Mon, 6 Apr 2020 22:28:59 +0800 Subject: [PATCH 39/92] abi checker: add armhf support --- src/core/kernel/QvKernelABIChecker.cpp | 4 ++++ src/core/kernel/QvKernelABIChecker.hpp | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/src/core/kernel/QvKernelABIChecker.cpp b/src/core/kernel/QvKernelABIChecker.cpp index 473427cd..6c7fed04 100644 --- a/src/core/kernel/QvKernelABIChecker.cpp +++ b/src/core/kernel/QvKernelABIChecker.cpp @@ -11,6 +11,7 @@ namespace Qv2ray::core::kernel::abi case ABI_WIN32: [[fallthrough]]; case ABI_MACH_O: [[fallthrough]]; case ABI_ELF_AARCH64: [[fallthrough]]; + case ABI_ELF_ARM: [[fallthrough]]; ; case ABI_ELF_X86: return targetType == hostType ? ABI_PERFECT : ABI_NOPE; case ABI_ELF_X86_64: return targetType == hostType ? ABI_PERFECT : targetType == ABI_ELF_X86 ? ABI_MAYBE : ABI_NOPE; case ABI_ELF_OTHER: return targetType == hostType ? ABI_PERFECT : ABI_MAYBE; @@ -43,6 +44,8 @@ namespace Qv2ray::core::kernel::abi return { QvKernelABIType::ABI_ELF_X86, std::nullopt }; else if (elfInstruction == 0xB700u) return { QvKernelABIType::ABI_ELF_AARCH64, std::nullopt }; + else if (elfInstruction == 0x2800u) + return { QvKernelABIType::ABI_ELF_ARM, std::nullopt }; else return { QvKernelABIType::ABI_ELF_OTHER, std::nullopt }; } @@ -63,6 +66,7 @@ namespace Qv2ray::core::kernel::abi case ABI_ELF_X86: return QObject::tr("ELF x86 executable"); case ABI_ELF_X86_64: return QObject::tr("ELF amd64 executable"); case ABI_ELF_AARCH64: return QObject::tr("ELF arm64 executable"); + case ABI_ELF_ARM: return QObject::tr("ELF arm executable"); case ABI_ELF_OTHER: return QObject::tr("other ELF executable"); default: return QObject::tr("unknown abi"); } diff --git a/src/core/kernel/QvKernelABIChecker.hpp b/src/core/kernel/QvKernelABIChecker.hpp index b95f1c2d..05457621 100644 --- a/src/core/kernel/QvKernelABIChecker.hpp +++ b/src/core/kernel/QvKernelABIChecker.hpp @@ -17,6 +17,7 @@ namespace Qv2ray::core::kernel ABI_ELF_X86, ABI_ELF_X86_64, ABI_ELF_AARCH64, + ABI_ELF_ARM, ABI_ELF_OTHER, }; @@ -38,6 +39,10 @@ namespace Qv2ray::core::kernel QvKernelABIType::ABI_WIN32; #elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_ARM_64) QvKernelABIType::ABI_ELF_AARCH64; +#elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_ARM_V7) + QvKernelABIType::ABI_ELF_ARM; +#else + #error "unknown architecture" #endif [[nodiscard]] std::pair, std::optional> deduceKernelABI(const QString &pathCoreExecutable); From 684a644557bac40d86e2561688b0f9e3aa310b99 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Tue, 7 Apr 2020 09:17:48 +0800 Subject: [PATCH 40/92] fix: this fixed #500 --- src/components/route/presets/RouteScheme_V2rayN.hpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/route/presets/RouteScheme_V2rayN.hpp b/src/components/route/presets/RouteScheme_V2rayN.hpp index bb662e1f..447c4074 100644 --- a/src/components/route/presets/RouteScheme_V2rayN.hpp +++ b/src/components/route/presets/RouteScheme_V2rayN.hpp @@ -142,9 +142,8 @@ namespace Qv2ray::components::route::presets::v2rayN const inline QList DomainsBlock{ "geosite:category-ads-all" }; const inline QList DomainsProxy{ "geosite:google", "geosite:github", "geosite:netflix", "geosite:steam", - "geosite:telegram", "geosite:tumblr", "geosite:speedtest", "geosite:bbc", - "domain:gvt1.com", "domain:textnow.com", "domain:twitch.tv", "domain:wikileaks.org", - "domain:naver.com" }; + "geosite:telegram", "geosite:tumblr", "domain:naver.com", "geosite:bbc", + "domain:gvt1.com", "domain:textnow.com", "domain:twitch.tv", "domain:wikileaks.org" }; const inline QList IPsProxy{ "91.108.4.0/22", "91.108.8.0/22", "91.108.12.0/22", "91.108.20.0/22", "91.108.36.0/23", From c756a52e3b1f0b902098cfa8ea45ea48b43185e5 Mon Sep 17 00:00:00 2001 From: ymshenyu <373318510@qq.com> Date: Tue, 7 Apr 2020 09:40:21 +0800 Subject: [PATCH 41/92] macOS: enable HiDPI support --- CMakeLists.txt | 2 ++ assets/MacOSXBundleInfo.plist.in | 38 ++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 assets/MacOSXBundleInfo.plist.in diff --git a/CMakeLists.txt b/CMakeLists.txt index 2aade55a..98ff4578 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -362,6 +362,7 @@ if(APPLE) ) set(MACOSX_ICON "${CMAKE_SOURCE_DIR}/assets/icons/qv2ray.icns") + set(MACOSX_PLIST "${CMAKE_SOURCE_DIR}/assets/MacOSXBundleInfo.plist.in") set_source_files_properties(${QM_FILES} PROPERTIES MACOSX_PACKAGE_LOCATION Resources/lang @@ -374,6 +375,7 @@ if(APPLE) set_target_properties(${PROJECT_NAME} PROPERTIES MACOSX_BUNDLE TRUE + MACOSX_BUNDLE_INFO_PLIST ${MACOSX_PLIST} MACOSX_BUNDLE_BUNDLE_NAME "Qv2ray" MACOSX_BUNDLE_BUNDLE_VERSION ${QV2RAY_VERSION_STRING} MACOSX_BUNDLE_COPYRIGHT "Copyright (c) 2019-2020 Qv2ray Development Group" diff --git a/assets/MacOSXBundleInfo.plist.in b/assets/MacOSXBundleInfo.plist.in new file mode 100644 index 00000000..e06b17ec --- /dev/null +++ b/assets/MacOSXBundleInfo.plist.in @@ -0,0 +1,38 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${MACOSX_BUNDLE_EXECUTABLE_NAME} + CFBundleGetInfoString + ${MACOSX_BUNDLE_INFO_STRING} + CFBundleIconFile + ${MACOSX_BUNDLE_ICON_FILE} + CFBundleIdentifier + ${MACOSX_BUNDLE_GUI_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleLongVersionString + ${MACOSX_BUNDLE_LONG_VERSION_STRING} + CFBundleName + ${MACOSX_BUNDLE_BUNDLE_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + ${MACOSX_BUNDLE_SHORT_VERSION_STRING} + CFBundleSignature + ???? + CFBundleVersion + ${MACOSX_BUNDLE_BUNDLE_VERSION} + CSResourcesFileMapped + + NSHumanReadableCopyright + ${MACOSX_BUNDLE_COPYRIGHT} + NSPrincipalClass + NSApplication + NSHighResolutionCapable + True + + From 2c2392ba4e4b5af9bb4a7c98a715779e9ae498a9 Mon Sep 17 00:00:00 2001 From: ymshenyu <373318510@qq.com> Date: Tue, 7 Apr 2020 20:37:24 +0800 Subject: [PATCH 42/92] test against with CMAKE_OSX_DEPLOYMENT_TARGET --- azure-pipelines.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 3033544f..13b99f2d 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -18,14 +18,11 @@ steps: - script: | brew install protobuf grpc ninja qt5 displayName: Prepare dependencies -- script: | - xcversion select 10.3 --symlink - displayName: Select Xcode 10.3 as active - script: | PATH=/usr/local/opt/qt5/bin:$PATH mkdir build cd build - cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release + cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_DEPLOYMENT_TARGET=10.13 sudo cmake --build . --target package --parallel $(sysctl -n hw.logicalcpu) displayName: Build Qv2ray env: From 28a7989cd29c42a9541d3432a1cf3f7eeb43c778 Mon Sep 17 00:00:00 2001 From: ymshenyu <373318510@qq.com> Date: Tue, 7 Apr 2020 20:41:04 +0800 Subject: [PATCH 43/92] bump osx deployment target version --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 13b99f2d..66ce01c5 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -22,7 +22,7 @@ steps: PATH=/usr/local/opt/qt5/bin:$PATH mkdir build cd build - cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_DEPLOYMENT_TARGET=10.13 + cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_DEPLOYMENT_TARGET=10.14 sudo cmake --build . --target package --parallel $(sysctl -n hw.logicalcpu) displayName: Build Qv2ray env: From 75db4ed76640b9726949a46f49a20cc6ed8c99d2 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Tue, 7 Apr 2020 20:55:14 +0800 Subject: [PATCH 44/92] update: adapt interface draft-2.2 --- makespec/BUILDVERSION | 2 +- src/components/plugins/QvPluginHost.cpp | 60 ++++++++++++------------- src/components/plugins/QvPluginHost.hpp | 3 +- src/components/plugins/interface | 2 +- src/core/handler/ConfigHandler.cpp | 16 +++---- src/ui/w_PluginManager.cpp | 24 +++++----- 6 files changed, 54 insertions(+), 53 deletions(-) diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index aeb955e7..7ea136c9 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5223 \ No newline at end of file +5224 \ No newline at end of file diff --git a/src/components/plugins/QvPluginHost.cpp b/src/components/plugins/QvPluginHost.cpp index 02c3d063..ef421ef9 100644 --- a/src/components/plugins/QvPluginHost.cpp +++ b/src/components/plugins/QvPluginHost.cpp @@ -48,28 +48,28 @@ namespace Qv2ray::components::plugins info.pluginLoader->unload(); continue; } - if (plugins.contains(info.pluginInterface->InternalName())) + info.metadata = info.pluginInterface->GetMetadata(); + if (plugins.contains(info.metadata.InternalName)) { - LOG(MODULE_PLUGINHOST, - "Found another plugin with the same internal name: " + info.pluginInterface->InternalName() + ". Skipped") + LOG(MODULE_PLUGINHOST, "Found another plugin with the same internal name: " + info.metadata.InternalName + ". Skipped") continue; } if (info.pluginInterface->QvPluginInterfaceVersion != QV2RAY_PLUGIN_INTERFACE_VERSION) { // The plugin was built for a not-compactable version of Qv2ray. Don't load the plugin by default. - LOG(MODULE_PLUGINHOST, "The plugin " + info.pluginInterface->InternalName() + + LOG(MODULE_PLUGINHOST, "The plugin " + info.metadata.InternalName + " is not loaded since it was built against a different version of interface") info.errorMessage = tr("This plugin was built against an incompactable version of Qv2ray Plugin Interface.") + NEWLINE + QObject::tr("Please contact the plugin provider or report the issue to Qv2ray Workgroup."); } - connect(info.pluginInterface->GetQObject(), SIGNAL(PluginLog(const QString &)), this, SLOT(QvPluginLog(const QString &))); - connect(info.pluginInterface->GetQObject(), SIGNAL(PluginErrorMessageBox(const QString &)), this, + connect(dynamic_cast(info.pluginInterface), SIGNAL(PluginLog(const QString &)), this, + SLOT(QvPluginLog(const QString &))); + connect(dynamic_cast(info.pluginInterface), SIGNAL(PluginErrorMessageBox(const QString &)), this, SLOT(QvPluginMessageBox(const QString &))); - LOG(MODULE_PLUGINHOST, - "Loaded plugin: \"" + info.pluginInterface->Name() + "\" made by: \"" + info.pluginInterface->Author() + "\"") - plugins.insert(info.pluginInterface->InternalName(), info); + LOG(MODULE_PLUGINHOST, "Loaded plugin: \"" + info.metadata.Name + "\" made by: \"" + info.metadata.Author + "\"") + plugins.insert(info.metadata.InternalName, info); } } return plugins.count(); @@ -78,7 +78,7 @@ namespace Qv2ray::components::plugins const QString QvPluginHost::GetPluginTypeString(const QString &internalName) const { QStringList types; - for (auto type : plugins.value(internalName).pluginInterface->SpecialPluginType()) + for (auto type : plugins.value(internalName).metadata.SpecialPluginType) { switch (type) { @@ -94,15 +94,15 @@ namespace Qv2ray::components::plugins const QString QvPluginHost::GetPluginHookTypeString(const QString &internalName) const { QStringList hooks; - for (auto hook : plugins.value(internalName).pluginInterface->PluginHooks()) + for (auto hook : plugins.value(internalName).metadata.Capabilities) { switch (hook) { - case PROCESSTYPE_NONE: hooks << tr("No hook"); break; - case PROCESSTYPE_CONNECTIVITY: hooks << tr("Connection State Change"); break; - case PROCESSTYPE_ITEM: hooks << tr("Connection Change"); break; - case PROCESSTYPE_STATS: hooks << tr("Statistics Event"); break; - case PROCESSTYPE_SYSTEMPROXY: hooks << tr("System Proxy"); break; + case CAPABILITY_NONE: hooks << tr("No hook"); break; + case CAPABILITY_CONNECTIVITY: hooks << tr("Connection State Change"); break; + case CAPABILITY_CONNECTION_ENTRY: hooks << tr("Connection Change"); break; + case CAPABILITY_STATS: hooks << tr("Statistics Event"); break; + case CAPABILITY_SYSTEM_PROXY: hooks << tr("System Proxy"); break; default: hooks << tr("Unknown/unsupported hook type."); break; } } @@ -114,7 +114,7 @@ namespace Qv2ray::components::plugins auto _sender = sender(); if (auto _interface = qobject_cast(_sender); _interface) { - LOG(MODULE_PLUGINCLIENT + "-" + _interface->InternalName(), log) + LOG(MODULE_PLUGINCLIENT + "-" + _interface->GetMetadata().InternalName, log) } else { @@ -127,7 +127,7 @@ namespace Qv2ray::components::plugins auto _sender = sender(); if (auto _interface = qobject_cast(_sender); _interface) { - QvMessageBoxWarn(nullptr, _interface->InternalName(), msg); + QvMessageBoxWarn(nullptr, _interface->GetMetadata().Name, msg); } else { @@ -164,7 +164,7 @@ namespace Qv2ray::components::plugins { for (auto &&plugin : plugins) { - DEBUG(MODULE_PLUGINHOST, "Unloading: \"" + plugin.pluginInterface->Name() + "\"") + DEBUG(MODULE_PLUGINHOST, "Unloading: \"" + plugin.metadata.Name + "\"") plugin.pluginLoader->unload(); plugin.pluginLoader->deleteLater(); } @@ -191,7 +191,7 @@ namespace Qv2ray::components::plugins } auto conf = JsonFromString(StringFromFile(QV2RAY_PLUGIN_SETTINGS_DIR + internalName + ".conf")); - plugins[internalName].pluginInterface->InitializePlugin(QV2RAY_PLUGIN_SETTINGS_DIR + internalName + "/", conf); + plugins[internalName].pluginInterface->Initialize(QV2RAY_PLUGIN_SETTINGS_DIR + internalName + "/", conf); plugins[internalName].isLoaded = true; return true; } @@ -201,7 +201,7 @@ namespace Qv2ray::components::plugins for (auto name : plugins.keys()) { LOG(MODULE_PLUGINHOST, "Saving plugin settings for: \"" + name + "\"") - auto &conf = plugins[name].pluginInterface->GetPluginSettngs(); + auto &conf = plugins[name].pluginInterface->GetSettngs(); StringToFile(JsonToString(conf), QV2RAY_PLUGIN_SETTINGS_DIR + name + ".conf"); } ClearPlugins(); @@ -212,9 +212,9 @@ namespace Qv2ray::components::plugins { for (auto &plugin : plugins) { - if (plugin.pluginInterface->PluginHooks().contains(QV2RAY_PLUGIN_PROCESSTYPE::PROCESSTYPE_STATS)) + if (plugin.metadata.Capabilities.contains(CAPABILITY_STATS)) { - plugin.pluginInterface->PluginProcessor()->ProcessEvent_ConnectionStats(object); + plugin.pluginInterface->GetEventHandler()->ProcessEvent_ConnectionStats(object); } } } @@ -222,19 +222,19 @@ namespace Qv2ray::components::plugins { for (auto &plugin : plugins) { - if (plugin.pluginInterface->PluginHooks().contains(QV2RAY_PLUGIN_PROCESSTYPE::PROCESSTYPE_CONNECTIVITY)) + if (plugin.metadata.Capabilities.contains(CAPABILITY_CONNECTIVITY)) { - plugin.pluginInterface->PluginProcessor()->ProcessEvent_Connectivity(object); + plugin.pluginInterface->GetEventHandler()->ProcessEvent_Connectivity(object); } } } - void QvPluginHost::Send_ItemEvent(const QvItemEventObject &object) + void QvPluginHost::Send_ConnectionEvent(const QvConnectionEntryEventObject &object) { for (auto &plugin : plugins) { - if (plugin.pluginInterface->PluginHooks().contains(QV2RAY_PLUGIN_PROCESSTYPE::PROCESSTYPE_ITEM)) + if (plugin.metadata.Capabilities.contains(CAPABILITY_CONNECTION_ENTRY)) { - plugin.pluginInterface->PluginProcessor()->ProcessEvent_Item(object); + plugin.pluginInterface->GetEventHandler()->ProcessEvent_ConnectionEntry(object); } } } @@ -242,9 +242,9 @@ namespace Qv2ray::components::plugins { for (auto &plugin : plugins) { - if (plugin.pluginInterface->PluginHooks().contains(QV2RAY_PLUGIN_PROCESSTYPE::PROCESSTYPE_SYSTEMPROXY)) + if (plugin.metadata.Capabilities.contains(CAPABILITY_SYSTEM_PROXY)) { - plugin.pluginInterface->PluginProcessor()->ProcessEvent_SystemProxy(object); + plugin.pluginInterface->GetEventHandler()->ProcessEvent_SystemProxy(object); } } } diff --git a/src/components/plugins/QvPluginHost.hpp b/src/components/plugins/QvPluginHost.hpp index 2648f24b..4aa5ec6d 100644 --- a/src/components/plugins/QvPluginHost.hpp +++ b/src/components/plugins/QvPluginHost.hpp @@ -15,6 +15,7 @@ namespace Qv2ray::components::plugins bool isLoaded = false; QString libraryPath; QString errorMessage; + QvPluginMetadata metadata; QPluginLoader *pluginLoader; Qv2rayInterface *pluginInterface; }; @@ -41,7 +42,7 @@ namespace Qv2ray::components::plugins // void Send_ConnectionStatsEvent(const QvConnectionStatsEventObject &object); void Send_ConnectivityEvent(const QvConnectivityEventObject &object); - void Send_ItemEvent(const QvItemEventObject &object); + void Send_ConnectionEvent(const QvConnectionEntryEventObject &object); void Send_SystemProxyEvent(const QvSystemProxyEventObject &object); // private slots: diff --git a/src/components/plugins/interface b/src/components/plugins/interface index e60b7a06..05cec33c 160000 --- a/src/components/plugins/interface +++ b/src/components/plugins/interface @@ -1 +1 @@ -Subproject commit e60b7a06c9b9ba79498500a07b20d2bef44da34e +Subproject commit 05cec33cc42bf291de0115f799b5d10920715182 diff --git a/src/core/handler/ConfigHandler.cpp b/src/core/handler/ConfigHandler.cpp index 4137c288..42bd36da 100644 --- a/src/core/handler/ConfigHandler.cpp +++ b/src/core/handler/ConfigHandler.cpp @@ -223,7 +223,7 @@ namespace Qv2ray::core::handlers { CheckConnectionExistance(id); OnConnectionRenamed(id, connections[id].displayName, newName); - PluginHost->Send_ItemEvent({ newName, connections[id].displayName, ConnectionEvent_Renamed }); + PluginHost->Send_ConnectionEvent({ newName, connections[id].displayName, ConnectionEvent_Renamed }); connections[id].displayName = newName; CHSaveConfigData_p(); return {}; @@ -235,7 +235,7 @@ namespace Qv2ray::core::handlers QFile connectionFile((groups[groupId].isSubscription ? QV2RAY_SUBSCRIPTION_DIR : QV2RAY_CONNECTIONS_DIR) + groupId.toString() + "/" + id.toString() + QV2RAY_CONFIG_FILE_EXTENSION); // - PluginHost->Send_ItemEvent({ connections[id].displayName, "", ConnectionEvent_Deleted }); + PluginHost->Send_ConnectionEvent({ connections[id].displayName, "", ConnectionEvent_Deleted }); connections.remove(id); groups[groupId].connections.removeAll(id); // @@ -283,7 +283,7 @@ namespace Qv2ray::core::handlers groups[newGroupId].connections.append(id); connections[id].groupId = newGroupId; // - PluginHost->Send_ItemEvent({ connections[id].displayName, "", ConnectionEvent_Updated }); + PluginHost->Send_ConnectionEvent({ connections[id].displayName, "", ConnectionEvent_Updated }); // emit OnConnectionGroupChanged(id, oldgid, newGroupId); // @@ -314,7 +314,7 @@ namespace Qv2ray::core::handlers QDir(QV2RAY_CONNECTIONS_DIR + id.toString()).removeRecursively(); } // - PluginHost->Send_ItemEvent({ groups[id].displayName, "", ConnectionEvent_Deleted }); + PluginHost->Send_ConnectionEvent({ groups[id].displayName, "", ConnectionEvent_Deleted }); // groups.remove(id); CHSaveConfigData_p(); @@ -407,7 +407,7 @@ namespace Qv2ray::core::handlers connectionRootCache[id] = root; // emit OnConnectionModified(id); - PluginHost->Send_ItemEvent({ connections[id].displayName, "", ConnectionEvent_Updated }); + PluginHost->Send_ConnectionEvent({ connections[id].displayName, "", ConnectionEvent_Updated }); if (!skipRestart && id == currentConnectionId) { emit RestartConnection(); @@ -421,7 +421,7 @@ namespace Qv2ray::core::handlers groups[id].displayName = displayName; groups[id].isSubscription = isSubscription; groups[id].importDate = system_clock::to_time_t(system_clock::now()); - PluginHost->Send_ItemEvent({ displayName, "", ConnectionEvent_Created }); + PluginHost->Send_ConnectionEvent({ displayName, "", ConnectionEvent_Created }); emit OnGroupCreated(id, displayName); CHSaveConfigData_p(); return id; @@ -435,7 +435,7 @@ namespace Qv2ray::core::handlers return tr("Group does not exist"); } OnGroupRenamed(id, groups[id].displayName, newName); - PluginHost->Send_ItemEvent({ newName, groups[id].displayName, ConnectionEvent_Renamed }); + PluginHost->Send_ConnectionEvent({ newName, groups[id].displayName, ConnectionEvent_Renamed }); groups[id].displayName = newName; return {}; } @@ -603,7 +603,7 @@ namespace Qv2ray::core::handlers connections[newId].importDate = system_clock::to_time_t(system_clock::now()); connections[newId].displayName = displayName; emit OnConnectionCreated(newId, displayName); - PluginHost->Send_ItemEvent({ displayName, "", ConnectionEvent_Created }); + PluginHost->Send_ConnectionEvent({ displayName, "", ConnectionEvent_Created }); UpdateConnection(newId, root); CHSaveConfigData_p(); return newId; diff --git a/src/ui/w_PluginManager.cpp b/src/ui/w_PluginManager.cpp index a4f40689..7f7ddcc7 100644 --- a/src/ui/w_PluginManager.cpp +++ b/src/ui/w_PluginManager.cpp @@ -12,9 +12,9 @@ PluginManageWindow::PluginManageWindow(QWidget *parent) : QDialog(parent) { const auto &info = PluginHost->GetPluginInfo(plugin); auto item = new QListWidgetItem(pluginListWidget); - item->setCheckState(PluginHost->GetPluginEnableState(info.pluginInterface->InternalName()) ? Qt::Checked : Qt::Unchecked); - item->setData(Qt::UserRole, info.pluginInterface->InternalName()); - item->setText(info.pluginInterface->Name() + " (" + (info.isLoaded ? tr("Loaded") : tr("Not loaded")) + ")"); + item->setCheckState(PluginHost->GetPluginEnableState(info.metadata.InternalName) ? Qt::Checked : Qt::Unchecked); + item->setData(Qt::UserRole, info.metadata.InternalName); + item->setText(info.metadata.Name + " (" + (info.isLoaded ? tr("Loaded") : tr("Not loaded")) + ")"); pluginListWidget->addItem(item); } isLoading = false; @@ -30,15 +30,15 @@ void PluginManageWindow::on_pluginListWidget_currentItemChanged(QListWidgetItem auto &info = PluginHost->GetPluginInfo(current->data(Qt::UserRole).toString()); if (info.pluginInterface) { - pluginIconLabel->setPixmap(info.pluginInterface->Icon().pixmap(pluginIconLabel->size() * devicePixelRatio())); + pluginIconLabel->setPixmap(info.metadata.Icon.pixmap(pluginIconLabel->size() * devicePixelRatio())); // - pluginNameLabel->setText(info.pluginInterface->Name()); - pluginAuthorLabel->setText(info.pluginInterface->Author()); - pluginDescriptionLabel->setText(info.pluginInterface->Description()); + pluginNameLabel->setText(info.metadata.Name); + pluginAuthorLabel->setText(info.metadata.Author); + pluginDescriptionLabel->setText(info.metadata.Description); pluginLibPathLabel->setText(info.libraryPath); pluginStateLabel->setText(info.isLoaded ? tr("Loaded") : tr("Not loaded")); - pluginTypeLabel->setText(PluginHost->GetPluginTypeString(info.pluginInterface->InternalName())); - pluginHookTypeLabel->setText(PluginHost->GetPluginHookTypeString(info.pluginInterface->InternalName())); + pluginTypeLabel->setText(PluginHost->GetPluginTypeString(info.metadata.InternalName)); + pluginHookTypeLabel->setText(PluginHost->GetPluginHookTypeString(info.metadata.InternalName)); pluginErrMessageTxt->setPlainText(info.errorMessage.isEmpty() ? "OK" : info.errorMessage); } } @@ -56,7 +56,7 @@ void PluginManageWindow::on_pluginListWidget_itemChanged(QListWidgetItem *item) auto pluginInternalName = item->data(Qt::UserRole).toString(); PluginHost->SetPluginEnableState(pluginInternalName, isEnabled); auto &info = PluginHost->GetPluginInfo(pluginInternalName); - item->setText(info.pluginInterface->Name() + " (" + (info.isLoaded ? tr("Loaded") : tr("Not loaded")) + ")"); + item->setText(info.metadata.Name + " (" + (info.isLoaded ? tr("Loaded") : tr("Not loaded")) + ")"); // if (!isEnabled) { @@ -96,11 +96,11 @@ void PluginManageWindow::on_pluginEditSettingsJsonBtn_clicked() tr("This plugin has been unloaded or has been disabled, please enable or reload the plugin to continue.")); return; } - JsonEditor w(info.pluginInterface->GetPluginSettngs()); + JsonEditor w(info.pluginInterface->GetSettngs()); auto newConf = w.OpenEditor(); if (w.result() == QDialog::Accepted) { - info.pluginInterface->UpdatePluginSettings(newConf); + info.pluginInterface->UpdateSettings(newConf); } } } From 69a7200b25b5794ea8125f97a24955063bdb331d Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Wed, 8 Apr 2020 11:36:28 +0800 Subject: [PATCH 45/92] update: adapt interface draft-2.3.1 --- makespec/BUILDVERSION | 2 +- src/components/plugins/interface | 2 +- src/core/CoreUtils.cpp | 19 +++++++++++++++++++ src/core/CoreUtils.hpp | 4 +++- src/core/handler/V2rayInstanceHandler.cpp | 17 +++++++++-------- 5 files changed, 33 insertions(+), 11 deletions(-) diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 7ea136c9..789f4578 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5224 \ No newline at end of file +5225 \ No newline at end of file diff --git a/src/components/plugins/interface b/src/components/plugins/interface index 05cec33c..79632b01 160000 --- a/src/components/plugins/interface +++ b/src/components/plugins/interface @@ -1 +1 @@ -Subproject commit 05cec33cc42bf291de0115f799b5d10920715182 +Subproject commit 79632b01f48bbfff9404af740f10e543d77bb0a3 diff --git a/src/core/CoreUtils.cpp b/src/core/CoreUtils.cpp index d7e6c815..1101bd45 100644 --- a/src/core/CoreUtils.cpp +++ b/src/core/CoreUtils.cpp @@ -159,4 +159,23 @@ namespace Qv2ray::core return ConnectionManager->GetConnectionMetaObject(id).groupId; } + const QMap GetInboundPorts(const CONFIGROOT &root) + { + if (!root.contains("inbounds")) + { + return {}; + } + QMap inboundPorts; + for (const auto &inboundVal : root["inbounds"].toArray()) + { + const auto &inbound = inboundVal.toObject(); + inboundPorts.insert(inbound["protocol"].toString(), inbound["port"].toInt()); + } + return inboundPorts; + } + + const QMap GetInboundPorts(const ConnectionId &id) + { + return GetInboundPorts(ConnectionManager->GetConnectionRoot(id)); + } } // namespace Qv2ray::core diff --git a/src/core/CoreUtils.hpp b/src/core/CoreUtils.hpp index b5c1af08..50182ca8 100644 --- a/src/core/CoreUtils.hpp +++ b/src/core/CoreUtils.hpp @@ -43,9 +43,11 @@ namespace Qv2ray::core const QString GetDisplayName(const ConnectionId &id, int limit = -1); const QString GetDisplayName(const GroupId &id, int limit = -1); // - const GroupId GetConnectionGroupId(const ConnectionId &id); // + const QMap GetInboundPorts(const CONFIGROOT &root); + const QMap GetInboundPorts(const ConnectionId &id); + // } // namespace Qv2ray::core using namespace Qv2ray::core; diff --git a/src/core/handler/V2rayInstanceHandler.cpp b/src/core/handler/V2rayInstanceHandler.cpp index 5364dfd7..733fec80 100644 --- a/src/core/handler/V2rayInstanceHandler.cpp +++ b/src/core/handler/V2rayInstanceHandler.cpp @@ -7,20 +7,20 @@ optional QvConfigHandler::CHStartConnection_p(const ConnectionId &id, c connections[id].lastConnected = system_clock::to_time_t(system_clock::now()); // auto fullConfig = GenerateRuntimeConfig(root); - + auto inboundPorts = GetInboundPorts(fullConfig); // - PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), QvConnecticity_Connecting }); + PluginHost->Send_ConnectivityEvent(QvConnectivityEventObject{ GetDisplayName(id), inboundPorts, QvConnecticity_Connecting }); auto result = vCoreInstance->StartConnection(id, fullConfig); if (!result.has_value()) { currentConnectionId = id; emit OnConnected(currentConnectionId); - PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), QvConnecticity_Connected }); + PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), inboundPorts, QvConnecticity_Connected }); } else { - PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), QvConnecticity_Disconnected }); + PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), {}, QvConnecticity_Disconnected }); } return result; } @@ -29,13 +29,13 @@ void QvConfigHandler::CHStopConnection_p() { if (vCoreInstance->KernelStarted) { - PluginHost->Send_ConnectivityEvent({ GetDisplayName(currentConnectionId), QvConnecticity_Disconnecting }); + PluginHost->Send_ConnectivityEvent({ GetDisplayName(currentConnectionId), {}, QvConnecticity_Disconnecting }); vCoreInstance->StopConnection(); // Copy ConnectionId id = currentConnectionId; currentConnectionId = NullConnectionId; emit OnDisconnected(id); - PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), QvConnecticity_Disconnected }); + PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), {}, QvConnecticity_Disconnected }); } else { @@ -48,8 +48,8 @@ void QvConfigHandler::OnStatsDataArrived(const ConnectionId &id, const quint64 u connections[id].upLinkData += uploadSpeed; connections[id].downLinkData += downloadSpeed; emit OnStatsAvailable(id, uploadSpeed, downloadSpeed, connections[id].upLinkData, connections[id].downLinkData); - PluginHost->Send_ConnectionStatsEvent( - { GetDisplayName(currentConnectionId), uploadSpeed, downloadSpeed, connections[id].upLinkData, connections[id].downLinkData }); + PluginHost->Send_ConnectionStatsEvent({ GetDisplayName(currentConnectionId), // + uploadSpeed, downloadSpeed, connections[id].upLinkData, connections[id].downLinkData }); } void QvConfigHandler::OnVCoreCrashed(const ConnectionId &id) @@ -57,5 +57,6 @@ void QvConfigHandler::OnVCoreCrashed(const ConnectionId &id) LOG(MODULE_CORE_HANDLER, "V2ray core crashed!") currentConnectionId = NullConnectionId; emit OnDisconnected(id); + PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), {}, QvConnecticity_Disconnected }); emit OnCrashed(); } From 0bd4e4bd22606f2740d3447c17be72153bfd1110 Mon Sep 17 00:00:00 2001 From: ymshenyu <373318510@qq.com> Date: Wed, 8 Apr 2020 15:03:24 +0800 Subject: [PATCH 46/92] rebuild against with CMAKE_OSX_DEPLOYMENT_TARGET --- .github/workflows/build-qv2ray-cmake.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-qv2ray-cmake.yml b/.github/workflows/build-qv2ray-cmake.yml index 023d16d4..f2bd6fe4 100644 --- a/.github/workflows/build-qv2ray-cmake.yml +++ b/.github/workflows/build-qv2ray-cmake.yml @@ -81,10 +81,7 @@ jobs: - name: macOS - ${{ matrix.qt_version }} - Build preparation - Install Packages if: matrix.platform == 'macos-latest' run: | - brew install protobuf grpc ninja wget - wget https://github.com/phracker/MacOSX-SDKs/releases/download/10.15/MacOSX10.14.sdk.tar.xz - tar -xf MacOSX10.14.sdk.tar.xz - sudo mv -v ./MacOSX10.14.sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk + brew install protobuf grpc ninja # -------------------------------------------------------- - name: Win-${{ matrix.arch }} - ${{ matrix.qt_version }} - Build preparation - Download Dependencies shell: bash @@ -105,7 +102,7 @@ jobs: run: | mkdir build cd build - cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release + cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_DEPLOYMENT_TARGET=10.14 sudo cmake --build . --target package --parallel $(sysctl -n hw.logicalcpu) cp qv2ray-*.dmg ../ - name: macOS - Get package name From f439cc7f491232d4ed6a0cfa26da25aaa365032b Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Wed, 8 Apr 2020 15:42:35 +0800 Subject: [PATCH 47/92] add: added SystemProxyEvent --- src/components/plugins/QvPluginHost.cpp | 7 ++-- src/components/plugins/interface | 2 +- src/components/proxy/QvProxyConfigurator.cpp | 35 +++++++------------- 3 files changed, 15 insertions(+), 29 deletions(-) diff --git a/src/components/plugins/QvPluginHost.cpp b/src/components/plugins/QvPluginHost.cpp index ef421ef9..4f899e94 100644 --- a/src/components/plugins/QvPluginHost.cpp +++ b/src/components/plugins/QvPluginHost.cpp @@ -63,11 +63,8 @@ namespace Qv2ray::components::plugins info.errorMessage = tr("This plugin was built against an incompactable version of Qv2ray Plugin Interface.") + NEWLINE + QObject::tr("Please contact the plugin provider or report the issue to Qv2ray Workgroup."); } - - connect(dynamic_cast(info.pluginInterface), SIGNAL(PluginLog(const QString &)), this, - SLOT(QvPluginLog(const QString &))); - connect(dynamic_cast(info.pluginInterface), SIGNAL(PluginErrorMessageBox(const QString &)), this, - SLOT(QvPluginMessageBox(const QString &))); + connect(plugin, SIGNAL(PluginLog(const QString &)), this, SLOT(QvPluginLog(const QString &))); + connect(plugin, SIGNAL(PluginErrorMessageBox(const QString &)), this, SLOT(QvPluginMessageBox(const QString &))); LOG(MODULE_PLUGINHOST, "Loaded plugin: \"" + info.metadata.Name + "\" made by: \"" + info.metadata.Author + "\"") plugins.insert(info.metadata.InternalName, info); } diff --git a/src/components/plugins/interface b/src/components/plugins/interface index 79632b01..95f4540a 160000 --- a/src/components/plugins/interface +++ b/src/components/plugins/interface @@ -1 +1 @@ -Subproject commit 79632b01f48bbfff9404af740f10e543d77bb0a3 +Subproject commit 95f4540ac3b8e32b6bc7251212d4aee2182f9740 diff --git a/src/components/proxy/QvProxyConfigurator.cpp b/src/components/proxy/QvProxyConfigurator.cpp index c1bb628b..64fb6d4f 100644 --- a/src/components/proxy/QvProxyConfigurator.cpp +++ b/src/components/proxy/QvProxyConfigurator.cpp @@ -1,6 +1,7 @@ #include "QvProxyConfigurator.hpp" #include "common/QvHelpers.hpp" +#include "components/plugins/QvPluginHost.hpp" #ifdef Q_OS_WIN #include #include @@ -234,21 +235,6 @@ namespace Qv2ray::components::proxy { LOG(MODULE_PROXY, "KDE detected") } - // - // if (usePAC) - // { - // actions << QString("gsettings set org.gnome.system.proxy autoconfig-url '%1'").arg(address); - // if (isKDE) - // { - // actions << QString("kwriteconfig5 --file " + QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + - // "/kioslaverc --group \"Proxy Settings\" --key ProxyType 2"); - - // actions << QString("kwriteconfig5 --file " + QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + - // "/kioslaverc --group \"Proxy Settings\" --key \"Proxy Config Script\" " + address); - // } - // } - // else - // { if (isKDE) { actions << QString("kwriteconfig5 --file " + QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + @@ -309,13 +295,6 @@ namespace Qv2ray::components::proxy { LOG(MODULE_PROXY, "Setting proxy for interface: " + service) - // if (usePAC) - // { - // QProcess::execute("/usr/sbin/networksetup -setautoproxystate " + service + " on"); - // QProcess::execute("/usr/sbin/networksetup -setautoproxyurl " + service + " " + address); - // } - // else - // { if (hasHTTP) { QProcess::execute("/usr/sbin/networksetup -setwebproxystate " + service + " on"); @@ -329,10 +308,17 @@ namespace Qv2ray::components::proxy QProcess::execute("/usr/sbin/networksetup -setsocksfirewallproxystate " + service + " on"); QProcess::execute("/usr/sbin/networksetup -setsocksfirewallproxy " + service + " " + address + " " + QSTRN(socksPort)); } - // } } #endif + // + // Trigger plugin events + QMap portSettings; + if (hasHTTP) + portSettings.insert(QvSystemProxyType::SystemProxy_HTTP, httpPort); + if (hasSOCKS) + portSettings.insert(QvSystemProxyType::SystemProxy_SOCKS, socksPort); + PluginHost->Send_SystemProxyEvent(QvSystemProxyEventObject{ portSettings, QvSystemProxyStateType::SystemProxyState_SetProxy }); } void ClearSystemProxy() @@ -386,5 +372,8 @@ namespace Qv2ray::components::proxy } #endif + // + // Trigger plugin events + PluginHost->Send_SystemProxyEvent(QvSystemProxyEventObject{ {}, QvSystemProxyStateType::SystemProxyState_ClearProxy }); } } // namespace Qv2ray::components::proxy From ceeff95f63cc6830b8d04250296185d51c094faa Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Wed, 8 Apr 2020 15:56:09 +0800 Subject: [PATCH 48/92] fix: some UI and logical fixes --- src/components/plugins/QvPluginHost.cpp | 4 ++-- src/ui/w_PluginManager.cpp | 7 +++++++ src/ui/w_PluginManager.hpp | 2 ++ src/ui/w_PluginManager.ui | 12 +++++++++--- 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/components/plugins/QvPluginHost.cpp b/src/components/plugins/QvPluginHost.cpp index 4f899e94..482475a2 100644 --- a/src/components/plugins/QvPluginHost.cpp +++ b/src/components/plugins/QvPluginHost.cpp @@ -177,8 +177,8 @@ namespace Qv2ray::components::plugins } if (!GlobalConfig.pluginConfig.pluginStates.contains(internalName)) { - // If not contained, default to enable. - GlobalConfig.pluginConfig.pluginStates[internalName] = true; + // If not contained, default to disable. + GlobalConfig.pluginConfig.pluginStates[internalName] = false; } // If the plugin is disabled if (!GlobalConfig.pluginConfig.pluginStates[internalName]) diff --git a/src/ui/w_PluginManager.cpp b/src/ui/w_PluginManager.cpp index 7f7ddcc7..ed2553ec 100644 --- a/src/ui/w_PluginManager.cpp +++ b/src/ui/w_PluginManager.cpp @@ -104,3 +104,10 @@ void PluginManageWindow::on_pluginEditSettingsJsonBtn_clicked() } } } + +void PluginManageWindow::on_pluginListWidget_itemSelectionChanged() +{ + auto needEnable = !pluginListWidget->selectedItems().isEmpty(); + pluginSettingsBtn->setEnabled(needEnable); + pluginEditSettingsJsonBtn->setEnabled(needEnable); +} diff --git a/src/ui/w_PluginManager.hpp b/src/ui/w_PluginManager.hpp index 8cef05d1..3d5535b6 100644 --- a/src/ui/w_PluginManager.hpp +++ b/src/ui/w_PluginManager.hpp @@ -22,6 +22,8 @@ class PluginManageWindow void on_pluginEditSettingsJsonBtn_clicked(); + void on_pluginListWidget_itemSelectionChanged(); + private: bool isLoading = true; }; diff --git a/src/ui/w_PluginManager.ui b/src/ui/w_PluginManager.ui index 16ba253c..b37c765a 100644 --- a/src/ui/w_PluginManager.ui +++ b/src/ui/w_PluginManager.ui @@ -51,12 +51,18 @@ + + IBeamCursor + true + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + @@ -154,7 +160,7 @@ - Hook Type + Capability @@ -177,7 +183,7 @@ - Special Plugin Type + Special Type @@ -224,7 +230,7 @@ - ICON! + Qt::AlignCenter From 229362c4e2f02a707f6e5f8de0368efee436bac9 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Wed, 8 Apr 2020 16:03:30 +0800 Subject: [PATCH 49/92] update: updated Preference Window default page ID, updated translations --- src/ui/w_PreferencesWindow.ui | 2 +- translations/en_US.ts | 545 ++++++++++++++++++---------------- translations/ja_JP.ts | 500 ++++++++++++++++++++----------- translations/ru_RU.ts | 498 ++++++++++++++++++++----------- translations/zh_CN.ts | 500 ++++++++++++++++++++----------- 5 files changed, 1269 insertions(+), 776 deletions(-) diff --git a/src/ui/w_PreferencesWindow.ui b/src/ui/w_PreferencesWindow.ui index 9754dcf6..5c27bc55 100644 --- a/src/ui/w_PreferencesWindow.ui +++ b/src/ui/w_PreferencesWindow.ui @@ -23,7 +23,7 @@ - 1 + 0 diff --git a/translations/en_US.ts b/translations/en_US.ts index 08e9b046..00358bfe 100644 --- a/translations/en_US.ts +++ b/translations/en_US.ts @@ -335,12 +335,12 @@ - + QRCode scanning failed - + Cannot find any QRCode from the image. @@ -797,14 +797,14 @@ - + Hide - - + + Show @@ -844,121 +844,90 @@ - - - - + + + + Connected: - - - Configuring PAC - - - - - Could not start PAC server as it is configured to use SOCKS, but it is not enabled - - - - - Could not start PAC server as it is configured to use HTTP, but it is not enabled - - - - + Duplicating Connection(s) - + Are you sure to duplicate these connection(s)? - + (Copy) - + Set auto connection - + Set %1 as auto connect. - - PAC Processing Failed - - - - - HTTP or SOCKS inbound is not properly configured for PAC - - - - - Qv2ray will continue, but will not set system proxy. - - - - + Cannot set system proxy - + Both HTTP and SOCKS inbounds are not enabled - + System proxy configured. - + Didn't set proxy for complex config. - + System proxy removed. - + Update Subscriptions - + There are subscriptions need to be updated, please go to subscriptions window to update them. - + These subscriptions are out-of-date: - + V2ray vcore terminated. - + V2ray vcore terminated unexpectedly. - + To solve the problem, read the V2ray log in the log text browser. @@ -968,17 +937,17 @@ - + Disconnected from: - + Removing Connection(s) - + Are you sure to remove selected connection(s)? @@ -994,7 +963,7 @@ - + Not Connected @@ -1083,6 +1052,11 @@ Switch to Qv2ray log + + + Plugins + + OutboundEditor @@ -1207,15 +1181,54 @@ + + PluginManageWindow + + + + + Loaded + + + + + + + Not loaded + + + + + Disabling a plugin + + + + + This plugin will keep loaded until the next time Qv2ray starts. + + + + + + Plugin not loaded + + + + + + This plugin has been unloaded or has been disabled, please enable or reload the plugin to continue. + + + PreferencesWindow - - - - - + + + + + Preferences @@ -1294,11 +1307,6 @@ HTTP - - - SOCKS - - Set System Proxy @@ -1349,96 +1357,11 @@ HTTP Settings - - - PAC Settings - - - - - The system proxy will be configured to use the PAC instead of HTTP and SOCKS. - - - - - Local IP for PAC - - 127.0.0.1 - - - Use Proxy - - - - - Import GFWList - - - - - Mirror: Gitlab - - - - - Github - - - - - Mirror: Pagure - - - - - Mirror: Repo.or.cz - - - - - Mirror: Bitbucket - - - - - Mirror: TuxFamily - - - - - GFWList File - - - - - Download with System Proxy - - - - - Go - - - - - Edit PAC - - - - - Open PAC Folder - - - - - PAC Access Path - - Connection Settings @@ -1601,13 +1524,13 @@ - + Bold - + Italic @@ -1707,23 +1630,23 @@ - + Page - + Item(s) - - + + Enable tProxy Support - + to this path: @@ -1733,120 +1656,99 @@ - + Update is disabled by your vendor. - + Duplicated port numbers detected, please check the port number settings. - + Invalid inbound listening address. - + Open V2ray assets folder - + Open V2ray core file - + This will append capabilities to the V2ray executable. - + Qv2ray will copy your V2ray core to this path: - + If anything goes wrong after enabling this, please check issue #57 or the link below: - + Qv2ray cannot copy one or both V2ray files from: - - + + Failed to setcap onto V2ray executable. You may need to run `setcap` manually. - + tProxy is not supported on macOS and Windows - + Apply network toolbar settings - + All other modified settings will be applied as well after this object. - + Do you want to continue? - - Select GFWList in base64 - - - - - - Download GFWList - - - - - Operation is cancelled. - - - - - Successfully downloaded GFWList. - - - - + Start with boot - + Failed to set auto start option. - - + + V2ray Core Settings - + V2ray path configuration check passed. - + Current version of V2ray is: @@ -2054,114 +1956,114 @@ p, li { white-space: pre-wrap; } - + Cannot Start Qv2ray - + Cannot find a place to store config files. - + Qv2ray has searched these paths below: - + It usually means you don't have the write permission to all of those locations. - - - + + + Qv2ray will now exit. - + Failed to initialise Qv2ray - + Please report if you think it's a bug. - + You cannot run Qv2ray as root, please use --I-just-wanna-run-with-root if you REALLY want to do so. - + --> USE IT AT YOUR OWN RISK! - + Debug version - + Qv2ray Cannot Continue - + You are running a lower version of Qv2ray compared to the current config file. - + Please check if there's an issue explaining about it. - + Or submit a new issue if you think this is an error. - + Dependency Missing - + This could be caused by a missing of `openssl` package in your system. - + If you are using an AppImage from Github Action, please report a bug. - + Cannot find openssl libs - + Failed to determine the location of config file: - + Qv2ray has found a config file, but it failed to be loaded due to some errors. - + A workaround is to remove the this file and restart Qv2ray: - + Technical Details @@ -2173,7 +2075,7 @@ p, li { white-space: pre-wrap; } - + Qv2ray - A cross-platform Qt frontend for V2ray. @@ -2184,25 +2086,21 @@ p, li { white-space: pre-wrap; } - Deprecated - PAC is now deprecated and is not encouraged to be used anymore. - It will be removed or be provided as a plugin in the future. - PAC will still work currently, but please switch to the V2ray built-in routing as soon as possible. @@ -2382,65 +2280,75 @@ p, li { white-space: pre-wrap; } - + core executable file %1 does not exist - + cannot open core executable file %1 in read-only mode - + core executable file %1 is an empty file - + core executable file %1 is too short to be executed - + cannot deduce the type of core executable file %1 - + Windows PE executable - + macOS Mach-O executable - + ELF x86 executable - + ELF amd64 executable - + ELF arm64 executable - + + ELF arm executable + + + + other ELF executable - + unknown abi + + + Please contact the plugin provider or report the issue to Qv2ray Workgroup. + + Qv2ray::common::QvCommandArgParser @@ -2466,6 +2374,11 @@ p, li { white-space: pre-wrap; } + Disable plugin feature + + + + Enable Qv2ray network toolbar plugin @@ -2484,44 +2397,89 @@ p, li { white-space: pre-wrap; } - Qv2ray::components::pac::PACServer + Qv2ray::components::plugins::QvPluginHost - - PAC Handler + + This plugin was built against an incompactable version of Qv2ray Plugin Interface. - - Failed to listen PAC request on this port, please verify the permissions + + No Special Type + + + + + Connection Kernel + + + + + Connection String Serializer/Deserializer + + + + + Unknown/unsupported plugin type. + + + + + No hook + + + + + Connection State Change + + + + + Connection Change + + + + + Statistics Event + + + + + System Proxy + + + + + Unknown/unsupported hook type. Qv2ray::core::handlers::QvConfigHandler - - + + Default Group - + File does not exist. - - + + Group does not exist - + Update Subscription - + %1 entrie(s) have been found from the subscription source, do you want to continue? @@ -3497,6 +3455,79 @@ Maybe you have downloaded the wrong core? + + w_PluginManager + + + Plugin Manager + + + + + Plugins + + + + + Plugin Info + + + + + Name + + + + + Author + + + + + Description + + + + + Library Path + + + + + State + + + + + Capability + + + + + Special Type + + + + + Error Message + + + + + Open settings page for current plugin + + + + + Open plugin settings + + + + + Manually Edit Settings + + + w_SubscribeEditor diff --git a/translations/ja_JP.ts b/translations/ja_JP.ts index db153445..ff7fb549 100644 --- a/translations/ja_JP.ts +++ b/translations/ja_JP.ts @@ -445,12 +445,12 @@ インポートする画像を選択してください - + QRCode scanning failed QRコードのスキャンに失敗しました - + Cannot find any QRCode from the image. 画像からQRコードが見つかりません。 @@ -1017,7 +1017,7 @@ - + Not Connected 接続されていません @@ -1084,7 +1084,7 @@ #Restart - + Hide 非表示 @@ -1147,8 +1147,8 @@ Subscription: - - + + Show 表示 @@ -1226,58 +1226,53 @@ ダウンロードリンク: - - - - + + + + Connected: 接続済み: - - Configuring PAC - PAC設定 + PAC設定 - Could not start PAC server as it is configured to use SOCKS, but it is not enabled - SOCKSを使用するため、PACサーバーを起動できませんでした + SOCKSを使用するため、PACサーバーを起動できませんでした - Could not start PAC server as it is configured to use HTTP, but it is not enabled - HTTPを使用するため、PACサーバーを起動できませんでした + HTTPを使用するため、PACサーバーを起動できませんでした - + Duplicating Connection(s) 項目をコピー - + Are you sure to duplicate these connection(s)? 選択した項目をコピーしますか? - + (Copy) (コピー) - + Set auto connection 自動接続設定 - + Set %1 as auto connect. %1 を自動接続として設定します。 - PAC Processing Failed - PAC処理に失敗しました + PAC処理に失敗しました Please reset the settings in Preference Window @@ -1288,37 +1283,35 @@ System proxy cleared. - HTTP or SOCKS inbound is not properly configured for PAC - HTTPまたはSOCKSインバウンドがPAC用に正しく設定されていません + HTTPまたはSOCKSインバウンドがPAC用に正しく設定されていません - Qv2ray will continue, but will not set system proxy. - Qv2rayは続行されますが、システムプロキシは設定されません。 + Qv2rayは続行されますが、システムプロキシは設定されません。 - + Cannot set system proxy Cannot set system proxy - + Both HTTP and SOCKS inbounds are not enabled HTTPインバウンドとSOCKSインバウンドの両方とも無効です - + System proxy configured. システムプロキシが設定されています。 - + Didn't set proxy for complex config. 複雑構成のプロキシ設定を行いませんでした。 - + System proxy removed. システムプロキシが削除されました。 @@ -1331,17 +1324,17 @@ Cannot set proxy for complex config. - + Update Subscriptions サブスクリプションを更新 - + There are subscriptions need to be updated, please go to subscriptions window to update them. 更新が必要なサブスクリプションがあります。サブスクリプションウィンドウに移動して更新してください。 - + These subscriptions are out-of-date: これらのサブスクリプションは最新ではありません: @@ -1378,17 +1371,17 @@ Rename a Connection - + V2ray vcore terminated. V2ray コアが終了しました。 - + V2ray vcore terminated unexpectedly. V2rayコアが終了しました。 - + To solve the problem, read the V2ray log in the log text browser. 問題を解決するには、ログテキストブラウザーでV2rayログを確認してください。 @@ -1424,7 +1417,7 @@ Already connected to: - + Disconnected from: 接続切断されました: @@ -1441,12 +1434,12 @@ The name you suggested is not valid, please try another. - + Removing Connection(s) 項目を削除 - + Are you sure to remove selected connection(s)? 選択した項目を削除しますか? @@ -1494,6 +1487,11 @@ Share Connection Share Connection + + + Plugins + + OutboundEditor @@ -1622,15 +1620,54 @@ ユーザー名 + + PluginManageWindow + + + + + Loaded + + + + + + + Not loaded + + + + + Disabling a plugin + + + + + This plugin will keep loaded until the next time Qv2ray starts. + + + + + + Plugin not loaded + + + + + + This plugin has been unloaded or has been disabled, please enable or reload the plugin to continue. + + + PreferencesWindow - - - - - + + + + + Preferences 設定 @@ -1723,9 +1760,8 @@ HTTP - SOCKS - SOCKS + SOCKS @@ -1778,19 +1814,16 @@ HTTP設定 - PAC Settings - PAC設定 + PAC設定 - The system proxy will be configured to use the PAC instead of HTTP and SOCKS. - システムプロキシは、HTTPもしくはSOCKSではなく、PACを使用するように設定されました。 + システムプロキシは、HTTPもしくはSOCKSではなく、PACを使用するように設定されました。 - Local IP for PAC - PACのローカルIP + PACのローカルIP @@ -1798,74 +1831,60 @@ 127.0.0.1 - Use Proxy - プロキシを使う + プロキシを使う - Import GFWList - GFWListをインポートする + GFWListをインポートする - Mirror: Gitlab - Mirror: Gitlab + Mirror: Gitlab - Github - Github + Github - Mirror: Pagure - Mirror: Pagure + Mirror: Pagure - Mirror: Repo.or.cz - Mirror: Repo.or.cz + Mirror: Repo.or.cz - Mirror: Bitbucket - Mirror: Bitbucket + Mirror: Bitbucket - Mirror: TuxFamily - Mirror: TuxFamily + Mirror: TuxFamily - GFWList File - GFWListファイル + GFWListファイル - Download with System Proxy - システムプロキシでダウンロード + システムプロキシでダウンロード - Go - 実行 + 実行 - Edit PAC - PACを編集 + PACを編集 - Open PAC Folder - PACフォルダーを開く + PACフォルダーを開く - PAC Access Path - PACアクセスパス + PACアクセスパス @@ -2098,13 +2117,13 @@ - + Bold Bold - + Italic 斜体 @@ -2216,23 +2235,23 @@ ダークモードテーマを使用する - + Page ページ - + Item(s) アイテム - - + + Enable tProxy Support tProxyサポートを有効にする - + to this path: このパスへ: @@ -2242,73 +2261,73 @@ Qv2rayネットワークツールバーは無効になっており、まだテスト中です。 --withToolbarPluginを追加して有効にします。 - + Update is disabled by your vendor. ベンダーによって更新が無効になっています。 - + Duplicated port numbers detected, please check the port number settings. 重複したポート番号が検出されました。ポート番号の設定を確認してください。 - + Invalid inbound listening address. 無効なインバウンドリスニングアドレス。 - + Open V2ray assets folder V2rayアセットフォルダーを開く - + Open V2ray core file V2rayコアファイルを開く - + This will append capabilities to the V2ray executable. これにより、V2ray実行可能ファイルに機能が追加されます。 - + Qv2ray will copy your V2ray core to this path: Qv2rayは、V2rayコアを次のパスにコピーします: - + If anything goes wrong after enabling this, please check issue #57 or the link below: これを有効にした後に何か問題が発生した場合は、問題#57または以下のリンクを確認してください: - + Qv2ray cannot copy one or both V2ray files from: Qv2rayは、V2rayファイルの一つまたは二つを以下からコピーできません: - - + + Failed to setcap onto V2ray executable. You may need to run `setcap` manually. V2ray実行可能ファイルへのsetcapに失敗しました。`setcap`を手動で実行する必要がある可能性があります。 - + tProxy is not supported on macOS and Windows tProxyはmacOSおよびWindowsではサポートされていません - + Apply network toolbar settings ネットワークツールバー設定を適用する - + All other modified settings will be applied as well after this object. All other modified settings will be applied as well after this object. - + Do you want to continue? 続けますか? @@ -2321,49 +2340,44 @@ Please restart Qv2ray to fully apply this feature. - Select GFWList in base64 - Base64でGFWListを選択します + Base64でGFWListを選択します - - Download GFWList - GFWListをダウンロードする + GFWListをダウンロードする - Operation is cancelled. - 操作はキャンセルされます。 + 操作はキャンセルされます。 - Successfully downloaded GFWList. - GFWListを正常にダウンロードしました。 + GFWListを正常にダウンロードしました。 - + Start with boot システム起動時に起動します - + Failed to set auto start option. 自動起動オプションの設定に失敗しました。 - - + + V2ray Core Settings V2ray Core設定 - + V2ray path configuration check passed. V2rayパス構成チェックに通過しました。 - + Current version of V2ray is: V2rayの現在のバージョンは次のとおりです: @@ -2517,34 +2531,34 @@ p, li { white-space: pre-wrap; } Qv2rayはここから設定ファイルをロードできません: - + Cannot Start Qv2ray Qv2rayを起動できません - + Cannot find a place to store config files. 設定ファイルの保存先が見つかりません。 - + Qv2ray has searched these paths below: Qv2rayは以下のパスを検索しました: - + It usually means you don't have the write permission to all of those locations. 通常、これらの場所すべてに対する書き込み権限がないことを意味します。 - - - + + + Qv2ray will now exit. Qv2rayは終了します。 - + Failed to initialise Qv2ray Qv2rayの初期化に失敗しました @@ -2553,17 +2567,17 @@ p, li { white-space: pre-wrap; } 構成ファイルの場所を特定できませんでした。 - + Please report if you think it's a bug. バグだと思われる場合は報告してください。 - + You cannot run Qv2ray as root, please use --I-just-wanna-run-with-root if you REALLY want to do so. Qv2rayをルートとして実行することはできません。本当に実行したい場合は、--I-just-wanna-run-with-rootを使用してください。 - + --> USE IT AT YOUR OWN RISK! --> 自分の責任で使用してください! @@ -2576,67 +2590,67 @@ p, li { white-space: pre-wrap; } Qv2ray will continue running, but you cannot change the UI language. - + Qv2ray Cannot Continue Qv2rayを続行できません - + You are running a lower version of Qv2ray compared to the current config file. 現在の構成ファイルと比較して、より低いバージョンのQv2rayを実行しています。 - + Please check if there's an issue explaining about it. 関連説明があるかどうかを確認してください。 - + Or submit a new issue if you think this is an error. または、これがエラーだと思う場合は、新しい問題として送信してください。 - + Dependency Missing 依存関係がありません - + This could be caused by a missing of `openssl` package in your system. これは、システムに `openssl`パッケージが存在しないことが原因である可能性があります。 - + If you are using an AppImage from Github Action, please report a bug. Github ActionsのAppImageを使用している場合は、バグを報告してください。 - + Cannot find openssl libs OpenSSLライブラリが見つかりません - + Failed to determine the location of config file: 構成ファイルの場所を特定できませんでした: - + Qv2ray has found a config file, but it failed to be loaded due to some errors. Qv2rayは設定ファイルを見つけましたが、いくつかのエラーのためにロードに失敗しました。 - + A workaround is to remove the this file and restart Qv2ray: 回避策は、このファイルを削除してQv2rayを再起動することです: - + Debug version デバッグバージョン - + Technical Details 技術詳細 @@ -2753,7 +2767,7 @@ p, li { white-space: pre-wrap; } なし - + Qv2ray - A cross-platform Qt frontend for V2ray. Qv2ray - V2ray用のクロスプラットフォームQtフロントエンド。 @@ -2855,25 +2869,21 @@ p, li { white-space: pre-wrap; } - Deprecated 非推奨 - PAC is now deprecated and is not encouraged to be used anymore. PACは非推奨になり、使用を推奨されなくなりました。 - It will be removed or be provided as a plugin in the future. 今後削除されるか、プラグインとして提供されます。 - PAC will still work currently, but please switch to the V2ray built-in routing as soon as possible. PACは現在も機能しますが、できるだけ早くV2Rayの組み込みルーティングに切り替えてください。 @@ -2913,65 +2923,75 @@ p, li { white-space: pre-wrap; } インバウンド - + core executable file %1 does not exist コア実行可能ファイル%1は存在しません - + cannot open core executable file %1 in read-only mode コア実行可能ファイル%1を読み取り専用モードで開けません - + core executable file %1 is an empty file コア実行可能ファイル%1は空のファイルです - + core executable file %1 is too short to be executed コア実行可能ファイル%1は実行するには短すぎます - + cannot deduce the type of core executable file %1 コア実行可能ファイル%1のタイプを推測できません - + Windows PE executable Windows PE実行可能ファイル - + macOS Mach-O executable macOS Mach-O実行可能ファイル - + ELF x86 executable ELF x86実行可能ファイル - + ELF amd64 executable ELF amd64実行可能ファイル - + ELF arm64 executable ELF arm64実行可能ファイル - + + ELF arm executable + + + + other ELF executable 他のELF実行可能ファイル - + unknown abi 不明なABI + + + Please contact the plugin provider or report the issue to Qv2ray Workgroup. + + Qv2ray::common::QvCommandArgParser @@ -2995,6 +3015,11 @@ p, li { white-space: pre-wrap; } Disable manually set QT_SCALE_FACTOR QT_SCALE_FACTORの手動設定を無効にする + + + Disable plugin feature + + Enable HiDPI support for Qt QtのHiDPIサポートを有効にする @@ -3004,7 +3029,7 @@ p, li { white-space: pre-wrap; } QtのHiDPIサポートを強制的に有効にする - + Enable Qv2ray network toolbar plugin Qv2rayネットワークツールバープラグインを有効にする @@ -3025,42 +3050,98 @@ p, li { white-space: pre-wrap; } Qv2ray::components::pac::PACServer - PAC Handler - PACハンドラー + PACハンドラー - Failed to listen PAC request on this port, please verify the permissions - このポートでPACリクエストをリッスンできませんでした、権限を確認してください + このポートでPACリクエストをリッスンできませんでした、権限を確認してください + + + + Qv2ray::components::plugins::QvPluginHost + + + This plugin was built against an incompactable version of Qv2ray Plugin Interface. + + + + + No Special Type + + + + + Connection Kernel + + + + + Connection String Serializer/Deserializer + + + + + Unknown/unsupported plugin type. + + + + + No hook + + + + + Connection State Change + + + + + Connection Change + + + + + Statistics Event + + + + + System Proxy + システムプロキシ + + + + Unknown/unsupported hook type. + Qv2ray::core::handlers::QvConfigHandler - - + + Default Group デフォルトグループ - + File does not exist. ファイルが存在しません。 - - + + Group does not exist グループが存在しません - + Update Subscription サブスクリプションを更新 - + %1 entrie(s) have been found from the subscription source, do you want to continue? アップストリームから返されるノードは %1 つだけです。続行してもよろしいですか? @@ -4118,6 +4199,79 @@ Maybe you have downloaded the wrong core? /s + + w_PluginManager + + + Plugin Manager + + + + + Plugins + + + + + Plugin Info + + + + + Name + + + + + Author + + + + + Description + + + + + Library Path + + + + + State + + + + + Capability + + + + + Special Type + + + + + Error Message + + + + + Open settings page for current plugin + + + + + Open plugin settings + + + + + Manually Edit Settings + + + w_SubscribeEditor diff --git a/translations/ru_RU.ts b/translations/ru_RU.ts index 68492008..14c8f70e 100644 --- a/translations/ru_RU.ts +++ b/translations/ru_RU.ts @@ -429,12 +429,12 @@ Выберите изображение для импорта - + QRCode scanning failed Ошибка сканирования QRCode - + Cannot find any QRCode from the image. Не удается найти QRCode в изображении. @@ -1001,7 +1001,7 @@ - + Not Connected @@ -1068,7 +1068,7 @@ #Перезапуск - + Hide Скрыть @@ -1131,8 +1131,8 @@ Подписка: - - + + Show Показать @@ -1210,58 +1210,53 @@ Ссылка для скачивания: - - - - + + + + Connected: Подключен: - - Configuring PAC - Настройка PAC + Настройка PAC - Could not start PAC server as it is configured to use SOCKS, but it is not enabled - Не удалось запустить PAC сервер, так как он настроен на использование SOCKS, но он не включен + Не удалось запустить PAC сервер, так как он настроен на использование SOCKS, но он не включен - Could not start PAC server as it is configured to use HTTP, but it is not enabled - Не удалось запустить PAC сервер, так как он настроен на использование HTTP, но он не включен + Не удалось запустить PAC сервер, так как он настроен на использование HTTP, но он не включен - + Duplicating Connection(s) - + Are you sure to duplicate these connection(s)? - + (Copy) - + Set auto connection - + Set %1 as auto connect. - PAC Processing Failed - Ошибка обработки PAC + Ошибка обработки PAC Please reset the settings in Preference Window @@ -1272,37 +1267,35 @@ Системный прокси очищен. - HTTP or SOCKS inbound is not properly configured for PAC - Входящие HTTP или SOCKS не настроены должным образом для PAC + Входящие HTTP или SOCKS не настроены должным образом для PAC - Qv2ray will continue, but will not set system proxy. - Qv2ray будет работать, но не сможет изменить системный прокси. + Qv2ray будет работать, но не сможет изменить системный прокси. - + Cannot set system proxy Не удается задать системный прокси - + Both HTTP and SOCKS inbounds are not enabled И HTTP, и SOCKS отключены - + System proxy configured. - + Didn't set proxy for complex config. - + System proxy removed. @@ -1315,17 +1308,17 @@ Не удается установить прокси для комплексной конфигурации. - + Update Subscriptions Обновить подписку - + There are subscriptions need to be updated, please go to subscriptions window to update them. Есть подписки, которые необходимо обновить, перейдите в окно подписок, чтобы обновить их. - + These subscriptions are out-of-date: Эти подписки устарели: @@ -1362,17 +1355,17 @@ Переименовать соединение - + V2ray vcore terminated. V2ray ядро прекращено. - + V2ray vcore terminated unexpectedly. Ядро V2ray неожиданно прервано. - + To solve the problem, read the V2ray log in the log text browser. Чтобы решить эту проблему, прочитайте журнал V2ray в текстовом браузере журнала. @@ -1408,7 +1401,7 @@ Уже подключен к: - + Disconnected from: Отключен от: @@ -1425,12 +1418,12 @@ Предложенное вами имя неверно, пожалуйста, попробуйте другое. - + Removing Connection(s) Удаление подключения(й) - + Are you sure to remove selected connection(s)? Вы уверены, что хотите удалить выбранные подключение(и)? @@ -1478,6 +1471,11 @@ Share Connection Поделиться подключением + + + Plugins + + OutboundEditor @@ -1606,15 +1604,54 @@ Имя пользователя + + PluginManageWindow + + + + + Loaded + + + + + + + Not loaded + + + + + Disabling a plugin + + + + + This plugin will keep loaded until the next time Qv2ray starts. + + + + + + Plugin not loaded + + + + + + This plugin has been unloaded or has been disabled, please enable or reload the plugin to continue. + + + PreferencesWindow - - - - - + + + + + Preferences Настройки @@ -1707,9 +1744,8 @@ HTTP - SOCKS - SOCKS + SOCKS @@ -1762,19 +1798,16 @@ Настройки HTTP - PAC Settings - Настройки PAC + Настройки PAC - The system proxy will be configured to use the PAC instead of HTTP and SOCKS. - Системный прокси-сервер будет настроен на использование PAC вместо HTTP и SOCKS. + Системный прокси-сервер будет настроен на использование PAC вместо HTTP и SOCKS. - Local IP for PAC - Локальный IP для PAC + Локальный IP для PAC @@ -1782,74 +1815,60 @@ 127.0.0.1 - Use Proxy - Использовать прокси + Использовать прокси - Import GFWList - Импорт GFWList + Импорт GFWList - Mirror: Gitlab - Mirror: Gitlab + Mirror: Gitlab - Github - GitHub + GitHub - Mirror: Pagure - Зеркало: Страсть + Зеркало: Страсть - Mirror: Repo.or.cz - Зеркало: Repo.or.cz + Зеркало: Repo.or.cz - Mirror: Bitbucket - Зеркало: Bitbucket + Зеркало: Bitbucket - Mirror: TuxFamily - Зеркало: TuxFamily + Зеркало: TuxFamily - GFWList File - GFWList файл + GFWList файл - Download with System Proxy - Скачать с системным прокси + Скачать с системным прокси - Go - Далее + Далее - Edit PAC - Изменить PAC + Изменить PAC - Open PAC Folder - Открыть папку PAC + Открыть папку PAC - PAC Access Path - PAC Access Path + PAC Access Path @@ -2082,13 +2101,13 @@ - + Bold Жирный - + Italic Курсив @@ -2200,23 +2219,23 @@ Использовать темную тему - + Page Стр. - + Item(s) Объекты - - + + Enable tProxy Support Включить tProxy поддержку - + to this path: на этот путь: @@ -2226,73 +2245,73 @@ Панель инструментов сети Qv2ray отключена и все еще тестируется. Добавьте --withToolbarPlugin для включения. - + Update is disabled by your vendor. - + Duplicated port numbers detected, please check the port number settings. Обнаружены дублированные номера портов, проверьте настройки номера порта. - + Invalid inbound listening address. Неверный входящий адрес прослушивания. - + Open V2ray assets folder Откройте папку активов v2ray - + Open V2ray core file Открыть основной файл V2ray - + This will append capabilities to the V2ray executable. Это добавит возможности к исполняемому файлу V2ray. - + Qv2ray will copy your V2ray core to this path: Qv2ray скопирует ваше ядро V2ray по этому пути: - + If anything goes wrong after enabling this, please check issue #57 or the link below: Если после включения этого параметра что-то пойдет не так, проверьте проблему № 57 или ссылку ниже: - + Qv2ray cannot copy one or both V2ray files from: Qv2ray не может скопировать один или оба файла V2ray из: - - + + Failed to setcap onto V2ray executable. You may need to run `setcap` manually. Не удалось установить setcap на исполняемый файл V2ray. Возможно, вам придется запустить `setcap` вручную. - + tProxy is not supported on macOS and Windows tProxy не поддерживается на macOS и Windows - + Apply network toolbar settings Применить настройки панели инструментов сети - + All other modified settings will be applied as well after this object. Все остальные измененные настройки будут применены и после этого объекта. - + Do you want to continue? Вы хотите продолжить? @@ -2305,49 +2324,44 @@ Пожалуйста, перезапустите Qv2ray, чтобы полностью применить эту функцию. - Select GFWList in base64 - Выберите GFWList в base64 + Выберите GFWList в base64 - - Download GFWList - Скачать GFWList + Скачать GFWList - Operation is cancelled. - Операция отменена. + Операция отменена. - Successfully downloaded GFWList. - Успешно загружено %1$s. + Успешно загружено %1$s. - + Start with boot Автозапуск - + Failed to set auto start option. Не удалось установить опцию автозапуска. - - + + V2ray Core Settings Настройки ядра V2ray - + V2ray path configuration check passed. Проверка конфигурации пути V2ray пройдена. - + Current version of V2ray is: Текущая версия V2ray: @@ -2489,34 +2503,34 @@ p, li { white-space: pre-wrap; } Qv2ray не может загрузить файл конфигурации отсюда: - + Cannot Start Qv2ray Невозможно запустить Qv2ray - + Cannot find a place to store config files. Не удается найти место для хранения конфигурационных файлов. - + Qv2ray has searched these paths below: Qv2ray искал эти пути ниже: - + It usually means you don't have the write permission to all of those locations. - - - + + + Qv2ray will now exit. Qv2ray теперь выйдет. - + Failed to initialise Qv2ray Не удалось инициализировать Qv2ray @@ -2525,17 +2539,17 @@ p, li { white-space: pre-wrap; } Не удалось определить местоположение конфигурационного файла. - + Please report if you think it's a bug. Пожалуйста, сообщите, если вы считаете об этом, если это ошибка. - + You cannot run Qv2ray as root, please use --I-just-wanna-run-with-root if you REALLY want to do so. Вы не можете запустить Qv2ray как root, пожалуйста, используйте --I-just-wanna-run-with-root, если вы хотите это сделать. - + --> USE IT AT YOUR OWN RISK! --> ИСПОЛЬЗУЙТЕ ЕГО НА СВОЙ СТРАХ И РИСК! @@ -2548,67 +2562,67 @@ p, li { white-space: pre-wrap; } Qv2ray продолжит работу, но вы не сможете изменить язык интерфейса. - + Qv2ray Cannot Continue Qv2ray не может продолжить - + You are running a lower version of Qv2ray compared to the current config file. Вы используете более низкую версию Qv2ray по сравнению с текущим файлом конфигурации. - + Please check if there's an issue explaining about it. Пожалуйста, проверьте, есть ли проблема с объяснением этого. - + Or submit a new issue if you think this is an error. Или отправьте новую проблему, если вы считаете, что это ошибка. - + Dependency Missing Зависимость отсутствует - + This could be caused by a missing of `openssl` package in your system. Это может быть вызвано отсутствием пакета `openssl` в вашей системе. - + If you are using an AppImage from Github Action, please report a bug. Если вы используете AppImage из Github Action, пожалуйста, сообщите об ошибке. - + Cannot find openssl libs Не удается найти openssl libs - + Failed to determine the location of config file: - + Qv2ray has found a config file, but it failed to be loaded due to some errors. - + A workaround is to remove the this file and restart Qv2ray: - + Debug version - + Technical Details Технические детали @@ -2725,7 +2739,7 @@ p, li { white-space: pre-wrap; } N/A - + Qv2ray - A cross-platform Qt frontend for V2ray. Qv2ray - кросс-платформенный Qt фронтенд для V2ray. @@ -2827,25 +2841,21 @@ p, li { white-space: pre-wrap; } - Deprecated - PAC is now deprecated and is not encouraged to be used anymore. - It will be removed or be provided as a plugin in the future. - PAC will still work currently, but please switch to the V2ray built-in routing as soon as possible. @@ -2885,65 +2895,75 @@ p, li { white-space: pre-wrap; } Входящее - + core executable file %1 does not exist - + cannot open core executable file %1 in read-only mode - + core executable file %1 is an empty file - + core executable file %1 is too short to be executed - + cannot deduce the type of core executable file %1 - + Windows PE executable - + macOS Mach-O executable - + ELF x86 executable - + ELF amd64 executable - + ELF arm64 executable - + + ELF arm executable + + + + other ELF executable - + unknown abi + + + Please contact the plugin provider or report the issue to Qv2ray Workgroup. + + Qv2ray::common::QvCommandArgParser @@ -2969,6 +2989,11 @@ p, li { white-space: pre-wrap; } + Disable plugin feature + + + + Enable Qv2ray network toolbar plugin Включить плагин панели инструментов сети Qv2ray @@ -2989,42 +3014,98 @@ p, li { white-space: pre-wrap; } Qv2ray::components::pac::PACServer - PAC Handler - PAC Handler + PAC Handler - Failed to listen PAC request on this port, please verify the permissions - Не удалось прослушать PAC-запрос для этого порта, проверьте разрешения + Не удалось прослушать PAC-запрос для этого порта, проверьте разрешения + + + + Qv2ray::components::plugins::QvPluginHost + + + This plugin was built against an incompactable version of Qv2ray Plugin Interface. + + + + + No Special Type + + + + + Connection Kernel + + + + + Connection String Serializer/Deserializer + + + + + Unknown/unsupported plugin type. + + + + + No hook + + + + + Connection State Change + + + + + Connection Change + + + + + Statistics Event + + + + + System Proxy + Системный прокси + + + + Unknown/unsupported hook type. + Qv2ray::core::handlers::QvConfigHandler - - + + Default Group - + File does not exist. - - + + Group does not exist - + Update Subscription Обновить подписку - + %1 entrie(s) have been found from the subscription source, do you want to continue? @@ -4043,6 +4124,79 @@ Maybe you have downloaded the wrong core? /s + + w_PluginManager + + + Plugin Manager + + + + + Plugins + + + + + Plugin Info + + + + + Name + + + + + Author + + + + + Description + + + + + Library Path + + + + + State + + + + + Capability + + + + + Special Type + + + + + Error Message + + + + + Open settings page for current plugin + + + + + Open plugin settings + + + + + Manually Edit Settings + + + w_SubscribeEditor diff --git a/translations/zh_CN.ts b/translations/zh_CN.ts index a35f721b..5a0e0427 100644 --- a/translations/zh_CN.ts +++ b/translations/zh_CN.ts @@ -350,12 +350,12 @@ 选择要导入的图像 - + QRCode scanning failed 二维码扫描失败 - + Cannot find any QRCode from the image. 无法从图像中找到任何二维码。 @@ -834,14 +834,14 @@ 0.00 B - + Hide 隐藏 - - + + Show 显示 @@ -893,121 +893,114 @@ 下载链接: - - - - + + + + Connected: 已连接: - - Configuring PAC - 配置 PAC + 配置 PAC - Could not start PAC server as it is configured to use SOCKS, but it is not enabled - 无法启动 PAC 服务器,因为它被配置为使用 SOCKS,但它没有启用 + 无法启动 PAC 服务器,因为它被配置为使用 SOCKS,但它没有启用 - Could not start PAC server as it is configured to use HTTP, but it is not enabled - 无法启动 PAC 服务器,因为它被配置为使用 HTTP ,但它尚未启用 + 无法启动 PAC 服务器,因为它被配置为使用 HTTP ,但它尚未启用 - + Duplicating Connection(s) 复制连接 - + Are you sure to duplicate these connection(s)? 您确定要复制这(些)连接吗? - + (Copy) (副本) - + Set auto connection 设置自动连接 - + Set %1 as auto connect. 已将 %1 设为自动连接。 - PAC Processing Failed - PAC 处理失败 + PAC 处理失败 - HTTP or SOCKS inbound is not properly configured for PAC - HTTP 或 SOCKS 入站配置不正确 + HTTP 或 SOCKS 入站配置不正确 - Qv2ray will continue, but will not set system proxy. - Qv2ray 将继续,但不会设置系统代理。 + Qv2ray 将继续,但不会设置系统代理。 - + Cannot set system proxy 无法设置系统代理 - + Both HTTP and SOCKS inbounds are not enabled HTTP 和 SOCKS 都没有启用 - + System proxy configured. 系统代理已设置。 - + Didn't set proxy for complex config. 未对复杂配置设置代理。 - + System proxy removed. 系统代理已清除。 - + Update Subscriptions 更新订阅 - + There are subscriptions need to be updated, please go to subscriptions window to update them. 有订阅需要更新,请转到订阅窗口进行更新。 - + These subscriptions are out-of-date: 这些订阅已过期: - + V2ray vcore terminated. V2ray 核心已终止。 - + V2ray vcore terminated unexpectedly. V2ray 核心意外终止。 - + To solve the problem, read the V2ray log in the log text browser. 要解决问题,请阅读日志文本浏览器中的 V2ray 日志。 @@ -1017,17 +1010,17 @@ 系统代理 - + Disconnected from: 已断开连接: - + Removing Connection(s) 删除连接 - + Are you sure to remove selected connection(s)? 您确定要删除这(些)连接吗? @@ -1043,7 +1036,7 @@ - + Not Connected 未连接 @@ -1132,6 +1125,11 @@ Switch to Qv2ray log 切换到 Qv2ray 日志 + + + Plugins + + OutboundEditor @@ -1260,15 +1258,54 @@ 用户名 + + PluginManageWindow + + + + + Loaded + + + + + + + Not loaded + + + + + Disabling a plugin + + + + + This plugin will keep loaded until the next time Qv2ray starts. + + + + + + Plugin not loaded + + + + + + This plugin has been unloaded or has been disabled, please enable or reload the plugin to continue. + + + PreferencesWindow - - - - - + + + + + Preferences 首选项 @@ -1348,9 +1385,8 @@ HTTP - SOCKS - SOCKS + SOCKS @@ -1403,19 +1439,16 @@ HTTP 设置 - PAC Settings - PAC 设置 + PAC 设置 - The system proxy will be configured to use the PAC instead of HTTP and SOCKS. - 系统代理将被配置为使用 PAC 而不是 HTTP 和 SOCKS。 + 系统代理将被配置为使用 PAC 而不是 HTTP 和 SOCKS。 - Local IP for PAC - PAC 本地 IP 地址 + PAC 本地 IP 地址 @@ -1423,74 +1456,60 @@ 127.0.0.1 - Use Proxy - 使用代理服务器 + 使用代理服务器 - Import GFWList - 导入 GFWList + 导入 GFWList - Mirror: Gitlab - 镜像源:Gitlab + 镜像源:Gitlab - Github - GitHub + GitHub - Mirror: Pagure - 镜像源:Pagure + 镜像源:Pagure - Mirror: Repo.or.cz - 镜像源:Repo.or.cz + 镜像源:Repo.or.cz - Mirror: Bitbucket - 镜像源:Bitbucket + 镜像源:Bitbucket - Mirror: TuxFamily - 镜像源:TuxFamily + 镜像源:TuxFamily - GFWList File - GFWList 文件 + GFWList 文件 - Download with System Proxy - 使用系统代理下载 + 使用系统代理下载 - Go - 开始 + 开始 - Edit PAC - 编辑 PAC + 编辑 PAC - Open PAC Folder - 打开 PAC 文件夹 + 打开 PAC 文件夹 - PAC Access Path - PAC 访问路径 + PAC 访问路径 @@ -1658,13 +1677,13 @@ - + Bold 粗体 - + Italic 斜体 @@ -1776,23 +1795,23 @@ 使用暗色模式主题 - + Page - + Item(s) 项目 - - + + Enable tProxy Support 启用 tProxy 支持 - + to this path: 到此路径: @@ -1802,120 +1821,115 @@ Qv2ray 的网络工具栏已被禁用,请使用 --withToolbarPlugin 来启用。 - + Update is disabled by your vendor. 自动更新已被供应者禁用。 - + Duplicated port numbers detected, please check the port number settings. 检测到重复的端口号,请检查端口号设置。 - + Invalid inbound listening address. 入站监听地址不可用。 - + Open V2ray assets folder 打开 V2ray 资源文件夹 - + Open V2ray core file 打开 V2ray 核心文件 - + This will append capabilities to the V2ray executable. 这会将功能附加到 V2ray 可执行文件。 - + Qv2ray will copy your V2ray core to this path: Qv2ray 会将您的 V2ray 核心复制到以下路径: - + If anything goes wrong after enabling this, please check issue #57 or the link below: 如果有出现任何问题,请参阅 Issue #57 或以下链接: - + Qv2ray cannot copy one or both V2ray files from: Qv2ray 无法从以下位置复制一个或两个 V2ray 文件: - - + + Failed to setcap onto V2ray executable. You may need to run `setcap` manually. 无法将 Capcap 设置到 V2ray 可执行文件上。 您可能需要手动运行“ setcap”。 - + tProxy is not supported on macOS and Windows 在 macOS 和 Windows 上不支持 tProxy - + Apply network toolbar settings 应用网络工具栏设置 - + All other modified settings will be applied as well after this object. 已经编辑的其它设置也会被同时应用。 - + Do you want to continue? 你想继续吗? - Select GFWList in base64 - 选择 base64 格式的 GFWList + 选择 base64 格式的 GFWList - - Download GFWList - 下载 GFWList + 下载 GFWList - Operation is cancelled. - 操作已取消。 + 操作已取消。 - Successfully downloaded GFWList. - 下载 GFWList 成功。 + 下载 GFWList 成功。 - + Start with boot 开机启动 - + Failed to set auto start option. 无法设置自动启动选项。 - - + + V2ray Core Settings V2ray 核心设置 - + V2ray path configuration check passed. V2ray 路径配置检查通过。 - + Current version of V2ray is: V2ray 当前版本是: @@ -2143,114 +2157,114 @@ p, li { white-space: pre-wrap; } Qv2ray 无法从这里加载配置文件: - + Cannot Start Qv2ray 无法启动 Qv2ray - + Cannot find a place to store config files. 找不到保存配置文件的地方。 - + Qv2ray has searched these paths below: Qv2ray 搜索了以下路径: - + It usually means you don't have the write permission to all of those locations. 这通常意味着您没有所有这些位置的文件写入权限。 - - - + + + Qv2ray will now exit. Qv2ray 现在将退出。 - + Failed to initialise Qv2ray 初始化 Qv2ray 失败 - + Please report if you think it's a bug. 如果您认为它是一个bug,请'报告。 - + You cannot run Qv2ray as root, please use --I-just-wanna-run-with-root if you REALLY want to do so. 您不能以root用户身份运行Qv2ray,如果您确实想这样做,请使用 --I-just-wanna-run-with-root。 - + --> USE IT AT YOUR OWN RISK! --> 请自负风险! - + Debug version 调试版 - + Qv2ray Cannot Continue Qv2ray 无法继续 - + You are running a lower version of Qv2ray compared to the current config file. 与当前配置文件相比,您正在运行一个较低版本的 Qv2ray。 - + Please check if there's an issue explaining about it. 请检查是否存在'有关此问题的说明。 - + Or submit a new issue if you think this is an error. 或者如果你认为这是一个错误,请提交一个新问题。 - + Dependency Missing 缺少依赖关系 - + This could be caused by a missing of `openssl` package in your system. 这可能是系统中缺少`openssl`软件包造成的。 - + If you are using an AppImage from Github Action, please report a bug. 如果您使用的是来自 Github Action 的 AppImage,请报告错误。 - + Cannot find openssl libs 找不到 OpenSSL 库 - + Failed to determine the location of config file: 无法确定配置文件的位置: - + Qv2ray has found a config file, but it failed to be loaded due to some errors. Qv2ray已找到配置文件,但由于某些错误而无法加载。 - + A workaround is to remove the this file and restart Qv2ray: 一种解决方法是删除此文件并重新启动Qv2ray: - + Technical Details 技术细节 @@ -2262,7 +2276,7 @@ p, li { white-space: pre-wrap; } 不适用 - + Qv2ray - A cross-platform Qt frontend for V2ray. Qv2ray - V2ray的跨平台Qt前端。 @@ -2289,25 +2303,21 @@ p, li { white-space: pre-wrap; } - Deprecated 已经过时 - PAC is now deprecated and is not encouraged to be used anymore. PAC 已经被标记为过时的功能,并且我们不建议您继续使用。 - It will be removed or be provided as a plugin in the future. 它可能会在将来某个版本被移除,或者作为单独的插件提供。 - PAC will still work currently, but please switch to the V2ray built-in routing as soon as possible. PAC 目前将会继续工作,但是我们强烈建议您切换使用 V2ray 自带的路由功能。 @@ -2487,65 +2497,75 @@ p, li { white-space: pre-wrap; } 入站 - + core executable file %1 does not exist 核心可执行文件 %1 不存在 - + cannot open core executable file %1 in read-only mode 无法以只读模式打开核心可执行文件 %1 - + core executable file %1 is an empty file 核心可执行文件 %1 是空文件 - + core executable file %1 is too short to be executed 核心可执行文件 %1 文件过小 - + cannot deduce the type of core executable file %1 无法推测核心可执行文件 %1 的类型 - + Windows PE executable Windows PE 可执行文件 - + macOS Mach-O executable macOS Mach-O 可执行文件 - + ELF x86 executable ELF x86 可执行文件 - + ELF amd64 executable ELF amd64 可执行文件 - + ELF arm64 executable ELF arm64 可执行文件 - + + ELF arm executable + + + + other ELF executable 其他 ELF 可执行文件 - + unknown abi 未知 ABI + + + Please contact the plugin provider or report the issue to Qv2ray Workgroup. + + Qv2ray::common::QvCommandArgParser @@ -2569,6 +2589,11 @@ p, li { white-space: pre-wrap; } Disable manually set QT_SCALE_FACTOR 禁用 QT_SCALE_FACTOR + + + Disable plugin feature + + Enable HiDPI support for Qt 启用 Qt HiDPI 支持 @@ -2578,7 +2603,7 @@ p, li { white-space: pre-wrap; } 强制启用 Qt HiDPI 支持 - + Enable Qv2ray network toolbar plugin 启用 Qv2ray 网络工具栏插件 @@ -2599,42 +2624,98 @@ p, li { white-space: pre-wrap; } Qv2ray::components::pac::PACServer - PAC Handler - PAC 处理器 + PAC 处理器 - Failed to listen PAC request on this port, please verify the permissions - 无法在此端口监听 PAC 请求,请检查是否具有足够的权限 + 无法在此端口监听 PAC 请求,请检查是否具有足够的权限 + + + + Qv2ray::components::plugins::QvPluginHost + + + This plugin was built against an incompactable version of Qv2ray Plugin Interface. + + + + + No Special Type + + + + + Connection Kernel + + + + + Connection String Serializer/Deserializer + + + + + Unknown/unsupported plugin type. + + + + + No hook + + + + + Connection State Change + + + + + Connection Change + + + + + Statistics Event + + + + + System Proxy + 系统代理 + + + + Unknown/unsupported hook type. + Qv2ray::core::handlers::QvConfigHandler - - + + Default Group 默认分组 - + File does not exist. 文件不存在。 - - + + Group does not exist 分组不存在 - + Update Subscription 更新订阅 - + %1 entrie(s) have been found from the subscription source, do you want to continue? 订阅源仅返回了 %1 个节点,确定要继续吗? @@ -3656,6 +3737,79 @@ Maybe you have downloaded the wrong core? /s + + w_PluginManager + + + Plugin Manager + + + + + Plugins + + + + + Plugin Info + + + + + Name + + + + + Author + + + + + Description + + + + + Library Path + + + + + State + + + + + Capability + + + + + Special Type + + + + + Error Message + + + + + Open settings page for current plugin + + + + + Open plugin settings + + + + + Manually Edit Settings + + + w_SubscribeEditor From 60009e0b47bb2d5057361d7e2a46e6cac7e6d3f9 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Thu, 9 Apr 2020 09:58:50 +0800 Subject: [PATCH 50/92] fix: some fixes --- CMakeLists.txt | 8 ++++---- makespec/BUILDVERSION | 2 +- src/base/Qv2rayBase.hpp | 6 ++++++ src/components/plugins/QvPluginHost.cpp | 2 -- src/core/kernel/KernelInteractions.cpp | 11 +++++------ src/core/kernel/QvKernelABIChecker.cpp | 14 +++++++------- src/core/kernel/QvKernelABIChecker.hpp | 6 +++--- 7 files changed, 26 insertions(+), 23 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 98ff4578..e4c22b87 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -146,7 +146,11 @@ if(QV2RAY_DISABLE_AUTO_UPDATE) add_definitions(-DDISABLE_AUTO_UPDATE) endif() +set(QVPLUGIN_INTERFACE_INCLUDE_DIR "src/components/plugins/interface") +include(src/components/plugins/interface/QvPluginInterface.cmake) + set(QV2RAY_SOURCES + ${QVPLUGIN_INTERFACE_HEADERS} 3rdparty/libsemver/version.cpp src/base/Qv2rayLog.cpp src/common/CommandArgs.cpp @@ -248,10 +252,6 @@ set(QV2RAY_SOURCES src/components/latency/win/ICMPPinger.hpp src/components/latency/QvTCPing.hpp src/components/plugins/toolbar/QvToolbar.hpp - src/components/plugins/interface/QvPluginInterface.hpp - src/components/plugins/interface/QvPluginInterfaceModels.hpp - src/components/plugins/interface/Qv2rayPluginObjects.hpp - src/components/plugins/interface/Qv2rayPluginProcessor.hpp src/components/plugins/QvPluginHost.hpp src/components/proxy/QvProxyConfigurator.hpp src/components/route/RouteSchemeIO.hpp diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 789f4578..abde00fb 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5225 \ No newline at end of file +5231 \ No newline at end of file diff --git a/src/base/Qv2rayBase.hpp b/src/base/Qv2rayBase.hpp index 3d5349d1..6b03ee7c 100644 --- a/src/base/Qv2rayBase.hpp +++ b/src/base/Qv2rayBase.hpp @@ -91,6 +91,12 @@ using namespace Qv2ray::base::objects::transfer; #define BLACK(obj) obj->setPalette(QWidget::palette()); +#ifdef Q_OS_MACOS + #define ACCESS_OPTIONAL_VALUE(obj) (*obj) +#else + #define ACCESS_OPTIONAL_VALUE(obj) (obj.value()) +#endif + #define QV2RAY_UI_COLORSCHEME_ROOT \ ((GlobalConfig.uiConfig.useDarkTheme) ? QStringLiteral(":/assets/icons/ui_dark/") : QStringLiteral(":/assets/icons/ui_light/")) #define QICON_R(file) QIcon(QV2RAY_UI_COLORSCHEME_ROOT + file) diff --git a/src/components/plugins/QvPluginHost.cpp b/src/components/plugins/QvPluginHost.cpp index 482475a2..b517e04e 100644 --- a/src/components/plugins/QvPluginHost.cpp +++ b/src/components/plugins/QvPluginHost.cpp @@ -79,7 +79,6 @@ namespace Qv2ray::components::plugins { switch (type) { - case SPECIAL_TYPE_NONE: types << tr("No Special Type"); break; case SPECIAL_TYPE_KERNEL: types << tr("Connection Kernel"); break; case SPECIAL_TYPE_SERIALIZOR: types << tr("Connection String Serializer/Deserializer"); break; default: types << tr("Unknown/unsupported plugin type."); break; @@ -95,7 +94,6 @@ namespace Qv2ray::components::plugins { switch (hook) { - case CAPABILITY_NONE: hooks << tr("No hook"); break; case CAPABILITY_CONNECTIVITY: hooks << tr("Connection State Change"); break; case CAPABILITY_CONNECTION_ENTRY: hooks << tr("Connection Change"); break; case CAPABILITY_STATS: hooks << tr("Statistics Event"); break; diff --git a/src/core/kernel/KernelInteractions.cpp b/src/core/kernel/KernelInteractions.cpp index 6db9fa39..c4b5f3a8 100644 --- a/src/core/kernel/KernelInteractions.cpp +++ b/src/core/kernel/KernelInteractions.cpp @@ -31,30 +31,29 @@ namespace Qv2ray::core::kernel } coreFile.close(); - // Get Core ABI. auto [abi, err] = kernel::abi::deduceKernelABI(vCorePath); if (err) { - LOG(MODULE_VCORE, "Core ABI deduction failed: " + err.value()) - *message = err.value(); + LOG(MODULE_VCORE, "Core ABI deduction failed: " + ACCESS_OPTIONAL_VALUE(err)) + *message = ACCESS_OPTIONAL_VALUE(err); return false; } - LOG(MODULE_VCORE, "Core ABI: " + kernel::abi::abiToString(abi.value())) + LOG(MODULE_VCORE, "Core ABI: " + kernel::abi::abiToString(ACCESS_OPTIONAL_VALUE(abi))) // Get Compiled ABI auto compiledABI = kernel::abi::COMPILED_ABI_TYPE; LOG(MODULE_VCORE, "Host ABI: " + kernel::abi::abiToString(compiledABI)) // Check ABI Compatibility. - switch (kernel::abi::checkCompatibility(compiledABI, abi.value())) + switch (kernel::abi::checkCompatibility(compiledABI, ACCESS_OPTIONAL_VALUE(abi))) { case kernel::abi::ABI_NOPE: LOG(MODULE_VCORE, "Host is incompatible with core") *message = tr("V2Ray core is incompatible with your platform.\r\n" // "Expected core ABI is %1, but got actual %2.\r\n" // "Maybe you have downloaded the wrong core?") - .arg(kernel::abi::abiToString(compiledABI), kernel::abi::abiToString(abi.value())); + .arg(kernel::abi::abiToString(compiledABI), kernel::abi::abiToString(ACCESS_OPTIONAL_VALUE(abi))); return false; case kernel::abi::ABI_MAYBE: LOG(MODULE_VCORE, "WARNING: Host maybe incompatible with core"); [[fallthrough]]; case kernel::abi::ABI_PERFECT: LOG(MODULE_VCORE, "Host is compatible with core"); diff --git a/src/core/kernel/QvKernelABIChecker.cpp b/src/core/kernel/QvKernelABIChecker.cpp index 6c7fed04..d2c57b5f 100644 --- a/src/core/kernel/QvKernelABIChecker.cpp +++ b/src/core/kernel/QvKernelABIChecker.cpp @@ -4,14 +4,14 @@ namespace Qv2ray::core::kernel::abi { - [[nodiscard]] QvKernelABICompatibility checkCompatibility(QvKernelABIType hostType, QvKernelABIType targetType) + QvKernelABICompatibility checkCompatibility(QvKernelABIType hostType, QvKernelABIType targetType) { switch (hostType) { - case ABI_WIN32: [[fallthrough]]; - case ABI_MACH_O: [[fallthrough]]; - case ABI_ELF_AARCH64: [[fallthrough]]; - case ABI_ELF_ARM: [[fallthrough]]; ; + case ABI_WIN32: + case ABI_MACH_O: + case ABI_ELF_AARCH64: + case ABI_ELF_ARM: case ABI_ELF_X86: return targetType == hostType ? ABI_PERFECT : ABI_NOPE; case ABI_ELF_X86_64: return targetType == hostType ? ABI_PERFECT : targetType == ABI_ELF_X86 ? ABI_MAYBE : ABI_NOPE; case ABI_ELF_OTHER: return targetType == hostType ? ABI_PERFECT : ABI_MAYBE; @@ -19,7 +19,7 @@ namespace Qv2ray::core::kernel::abi } } - [[nodiscard]] std::pair, std::optional> deduceKernelABI(const QString &pathCoreExecutable) + std::pair, std::optional> deduceKernelABI(const QString &pathCoreExecutable) { QFile file(pathCoreExecutable); if (!file.exists()) @@ -57,7 +57,7 @@ namespace Qv2ray::core::kernel::abi return { std::nullopt, QObject::tr("cannot deduce the type of core executable file %1").arg(pathCoreExecutable) }; } - [[nodiscard]] QString abiToString(QvKernelABIType abi) + QString abiToString(QvKernelABIType abi) { switch (abi) { diff --git a/src/core/kernel/QvKernelABIChecker.hpp b/src/core/kernel/QvKernelABIChecker.hpp index 05457621..d0346388 100644 --- a/src/core/kernel/QvKernelABIChecker.hpp +++ b/src/core/kernel/QvKernelABIChecker.hpp @@ -45,8 +45,8 @@ namespace Qv2ray::core::kernel #error "unknown architecture" #endif - [[nodiscard]] std::pair, std::optional> deduceKernelABI(const QString &pathCoreExecutable); - [[nodiscard]] QvKernelABICompatibility checkCompatibility(QvKernelABIType hostType, QvKernelABIType targetType); - [[nodiscard]] QString abiToString(QvKernelABIType abi); + std::pair, std::optional> deduceKernelABI(const QString &pathCoreExecutable); + QvKernelABICompatibility checkCompatibility(QvKernelABIType hostType, QvKernelABIType targetType); + QString abiToString(QvKernelABIType abi); } // namespace abi } // namespace Qv2ray::core::kernel From 6fdb80b2f603bd07d22476f1ca9d06479781ce10 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Thu, 9 Apr 2020 23:23:38 +0800 Subject: [PATCH 51/92] add: refactored PluginHost, added Outbound Editor support --- makespec/BUILDVERSION | 2 +- src/components/plugins/QvPluginHost.cpp | 92 +++-- src/components/plugins/QvPluginHost.hpp | 56 ++- src/components/plugins/interface | 2 +- src/ui/editors/w_OutboundEditor.cpp | 76 ++-- src/ui/editors/w_OutboundEditor.hpp | 27 +- src/ui/w_PluginManager.cpp | 96 ++--- src/ui/w_PluginManager.hpp | 3 +- src/ui/w_PluginManager.ui | 495 ++++++++++++------------ 9 files changed, 455 insertions(+), 394 deletions(-) diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index abde00fb..5f7fc967 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5231 \ No newline at end of file +5233 \ No newline at end of file diff --git a/src/components/plugins/QvPluginHost.cpp b/src/components/plugins/QvPluginHost.cpp index b517e04e..486ad2fe 100644 --- a/src/components/plugins/QvPluginHost.cpp +++ b/src/components/plugins/QvPluginHost.cpp @@ -43,7 +43,6 @@ namespace Qv2ray::components::plugins info.pluginInterface = qobject_cast(plugin); if (info.pluginInterface == nullptr) { - // info.errorMessage = tr("Failed to cast from QObject to Qv2rayPluginInterface"); LOG(MODULE_PLUGINHOST, "Failed to cast from QObject to Qv2rayPluginInterface") info.pluginLoader->unload(); continue; @@ -58,10 +57,12 @@ namespace Qv2ray::components::plugins if (info.pluginInterface->QvPluginInterfaceVersion != QV2RAY_PLUGIN_INTERFACE_VERSION) { // The plugin was built for a not-compactable version of Qv2ray. Don't load the plugin by default. - LOG(MODULE_PLUGINHOST, "The plugin " + info.metadata.InternalName + - " is not loaded since it was built against a different version of interface") - info.errorMessage = tr("This plugin was built against an incompactable version of Qv2ray Plugin Interface.") + NEWLINE + - QObject::tr("Please contact the plugin provider or report the issue to Qv2ray Workgroup."); + LOG(MODULE_PLUGINHOST, info.metadata.InternalName + " is built with an older Interface, ignoring") + QvMessageBoxWarn(nullptr, tr("Cannot load plugin"), + info.metadata.Name + " " + tr("cannot be loaded.") + NEWLINE NEWLINE + + tr("This plugin was built against an older/newer version of the Plugin Interface.") + NEWLINE + + tr("Please contact the plugin provider or report the issue to Qv2ray Workgroup.")); + continue; } connect(plugin, SIGNAL(PluginLog(const QString &)), this, SLOT(QvPluginLog(const QString &))); connect(plugin, SIGNAL(PluginErrorMessageBox(const QString &)), this, SLOT(QvPluginMessageBox(const QString &))); @@ -72,38 +73,6 @@ namespace Qv2ray::components::plugins return plugins.count(); } - const QString QvPluginHost::GetPluginTypeString(const QString &internalName) const - { - QStringList types; - for (auto type : plugins.value(internalName).metadata.SpecialPluginType) - { - switch (type) - { - case SPECIAL_TYPE_KERNEL: types << tr("Connection Kernel"); break; - case SPECIAL_TYPE_SERIALIZOR: types << tr("Connection String Serializer/Deserializer"); break; - default: types << tr("Unknown/unsupported plugin type."); break; - } - } - return types.join("; "); - } - - const QString QvPluginHost::GetPluginHookTypeString(const QString &internalName) const - { - QStringList hooks; - for (auto hook : plugins.value(internalName).metadata.Capabilities) - { - switch (hook) - { - case CAPABILITY_CONNECTIVITY: hooks << tr("Connection State Change"); break; - case CAPABILITY_CONNECTION_ENTRY: hooks << tr("Connection Change"); break; - case CAPABILITY_STATS: hooks << tr("Statistics Event"); break; - case CAPABILITY_SYSTEM_PROXY: hooks << tr("System Proxy"); break; - default: hooks << tr("Unknown/unsupported hook type."); break; - } - } - return hooks.join("; "); - } - void QvPluginHost::QvPluginLog(const QString &log) { auto _sender = sender(); @@ -195,9 +164,12 @@ namespace Qv2ray::components::plugins { for (auto name : plugins.keys()) { - LOG(MODULE_PLUGINHOST, "Saving plugin settings for: \"" + name + "\"") - auto &conf = plugins[name].pluginInterface->GetSettngs(); - StringToFile(JsonToString(conf), QV2RAY_PLUGIN_SETTINGS_DIR + name + ".conf"); + if (plugins[name].isLoaded) + { + LOG(MODULE_PLUGINHOST, "Saving plugin settings for: \"" + name + "\"") + auto &conf = plugins[name].pluginInterface->GetSettngs(); + StringToFile(JsonToString(conf), QV2RAY_PLUGIN_SETTINGS_DIR + name + ".conf"); + } } ClearPlugins(); } @@ -243,4 +215,44 @@ namespace Qv2ray::components::plugins } } } + + const QString GetPluginTypeString(const SPECIAL_TYPE_FLAGS &types) + { + QStringList typesList; + if (types.isEmpty()) + { + typesList << QObject::tr("Normal Plugin"); + } + for (auto type : types) + { + switch (type) + { + case SPECIAL_TYPE_KERNEL: typesList << QObject::tr("Kernel"); break; + case SPECIAL_TYPE_SERIALIZOR: typesList << QObject::tr("Share Link Parser"); break; + default: typesList << QObject::tr("Unknown type."); break; + } + } + return typesList.join(NEWLINE); + } + + const QString GetPluginCapabilityString(const CAPABILITY_FLAGS &caps) + { + QStringList capsString; + if (caps.isEmpty()) + { + capsString << QObject::tr("No Capability"); + } + for (auto cap : caps) + { + switch (cap) + { + case CAPABILITY_CONNECTIVITY: capsString << QObject::tr("Connection State Change"); break; + case CAPABILITY_CONNECTION_ENTRY: capsString << QObject::tr("Connection Change"); break; + case CAPABILITY_STATS: capsString << QObject::tr("Statistics Event"); break; + case CAPABILITY_SYSTEM_PROXY: capsString << QObject::tr("System Proxy Event"); break; + default: capsString << QObject::tr("Unknown"); break; + } + } + return capsString.join(NEWLINE); + } } // namespace Qv2ray::components::plugins diff --git a/src/components/plugins/QvPluginHost.hpp b/src/components/plugins/QvPluginHost.hpp index 4aa5ec6d..cf5a784a 100644 --- a/src/components/plugins/QvPluginHost.hpp +++ b/src/components/plugins/QvPluginHost.hpp @@ -14,7 +14,6 @@ namespace Qv2ray::components::plugins { bool isLoaded = false; QString libraryPath; - QString errorMessage; QvPluginMetadata metadata; QPluginLoader *pluginLoader; Qv2rayInterface *pluginInterface; @@ -27,17 +26,58 @@ namespace Qv2ray::components::plugins explicit QvPluginHost(QObject *parent = nullptr); ~QvPluginHost(); void InitializePluginHost(); + // bool GetPluginEnableState(const QString &internalName) const; void SetPluginEnableState(const QString &internalName, bool isEnabled); - const QString GetPluginTypeString(const QString &internalName) const; - const QString GetPluginHookTypeString(const QString &internalName) const; - const QStringList AvailablePlugins() const + // + bool inline GetPluginLoadState(const QString &internalName) const + { + return plugins.value(internalName).isLoaded; + } + const inline QString GetPluginLibraryPath(const QString &internalName) const + { + return plugins.value(internalName).libraryPath; + } + const inline QStringList AvailablePlugins() const { return plugins.keys(); } - const inline QvPluginInfo GetPluginInfo(const QString &internalName) const + inline std::unique_ptr GetPluginSettingsWidget(const QString &internalName) const { - return plugins.value(internalName); + return plugins.value(internalName).pluginInterface->GetSettingsWidget(); + } + const inline std::unique_ptr GetPluginEditorWidget(const QString &internalName, UI_TYPE type) const + { + return plugins.value(internalName).pluginInterface->GetEditorWidget(type); + } + const inline QJsonObject GetPluginSettings(const QString &internalName) const + { + return plugins.value(internalName).pluginInterface->GetSettngs(); + } + bool inline SetPluginSettings(const QString &internalName, const QJsonObject &settings) const + { + return plugins.value(internalName).pluginInterface->UpdateSettings(settings); + } + const inline QvPluginMetadata GetPluginMetadata(const QString &internalName) const + { + return plugins.value(internalName).metadata; + } + const QList> GetOutboundEditorWidgets() + { + QList> data; + for (const auto &plugin : plugins) + { + if (!plugin.isLoaded) + continue; + if (auto editor = plugin.pluginInterface->GetEditorWidget(UI_TYPE::UI_TYPE_OUTBOUND_EDITOR); editor) + { + for (const auto &cap : editor->OutboundCapabilities()) + { + data.append({ cap, plugin.metadata.InternalName }); + } + } + } + return data; } // void Send_ConnectionStatsEvent(const QvConnectionStatsEventObject &object); @@ -54,9 +94,11 @@ namespace Qv2ray::components::plugins bool InitializePlugin(const QString &internalName); void ClearPlugins(); // Internal name, plugin info - QMap plugins; + QHash plugins; }; + const QString GetPluginTypeString(const SPECIAL_TYPE_FLAGS &types); + const QString GetPluginCapabilityString(const CAPABILITY_FLAGS &caps); inline ::Qv2ray::components::plugins::QvPluginHost *PluginHost = nullptr; } // namespace Qv2ray::components::plugins diff --git a/src/components/plugins/interface b/src/components/plugins/interface index 95f4540a..7df6621b 160000 --- a/src/components/plugins/interface +++ b/src/components/plugins/interface @@ -1 +1 @@ -Subproject commit 95f4540ac3b8e32b6bc7251212d4aee2182f9740 +Subproject commit 7df6621bcdfcddadd94bdfd253306e1680035941 diff --git a/src/ui/editors/w_OutboundEditor.cpp b/src/ui/editors/w_OutboundEditor.cpp index 4fbf4be6..b42a3b28 100644 --- a/src/ui/editors/w_OutboundEditor.cpp +++ b/src/ui/editors/w_OutboundEditor.cpp @@ -1,5 +1,6 @@ #include "w_OutboundEditor.hpp" +#include "components/plugins/QvPluginHost.hpp" #include "core/connection/Generation.hpp" #include "ui/editors/w_JsonEditor.hpp" #include "ui/editors/w_RoutesEditor.hpp" @@ -9,26 +10,23 @@ #include #include -OutboundEditor::OutboundEditor(QWidget *parent) : QDialog(parent), Tag(""), Mux(), vmess(), shadowsocks() +OutboundEditor::OutboundEditor(QWidget *parent) : QDialog(parent), tag(""), Mux(), vmess(), shadowsocks() { QvMessageBusConnect(OutboundEditor); setupUi(this); // - ssWidget = new StreamSettingsWidget(this); - transportFrame->addWidget(ssWidget); + streamSettingsWidget = new StreamSettingsWidget(this); + streamSettingsWidget->SetStreamObject({}); + transportFrame->addWidget(streamSettingsWidget); // - shadowsocks = ShadowSocksServerObject(); - socks = SocksServerObject(); - vmess = VMessServerObject(); socks.users.push_back(SocksServerObject::UserObject()); vmess.users.push_back(VMessServerObject::UserObject()); // - auto stream = StreamSettingsObject(); - ssWidget->SetStreamObject(stream); + auto data = PluginHost->GetOutboundEditorWidgets(); // - OutboundType = "vmess"; - Tag = OUTBOUND_TAG_PROXY; - useFProxy = false; + outboundType = "vmess"; + tag = OUTBOUND_TAG_PROXY; + useForwardProxy = false; ReloadGUI(); Result = GenerateConnectionJson(); } @@ -37,21 +35,27 @@ QvMessageBusSlotImpl(OutboundEditor) { switch (msg) { - MBShowDefaultImpl MBHideDefaultImpl MBRetranslateDefaultImpl + case UPDATE_COLORSCHEME: + { + break; + }; + MBShowDefaultImpl; + MBHideDefaultImpl; + MBRetranslateDefaultImpl; } } -OutboundEditor::OutboundEditor(OUTBOUND outboundEntry, QWidget *parent) : OutboundEditor(parent) +OutboundEditor::OutboundEditor(const OUTBOUND &outboundEntry, QWidget *parent) : OutboundEditor(parent) { Original = outboundEntry; - Tag = outboundEntry["tag"].toString(); - tagTxt->setText(Tag); - OutboundType = outboundEntry["protocol"].toString(); + tag = outboundEntry["tag"].toString(); + tagTxt->setText(tag); + outboundType = outboundEntry["protocol"].toString(); Mux = outboundEntry["mux"].toObject(); - useFProxy = outboundEntry[QV2RAY_USE_FPROXY_KEY].toBool(false); - ssWidget->SetStreamObject(StructFromJsonString(JsonToString(outboundEntry["streamSettings"].toObject()))); + useForwardProxy = outboundEntry[QV2RAY_USE_FPROXY_KEY].toBool(false); + streamSettingsWidget->SetStreamObject(StructFromJsonString(JsonToString(outboundEntry["streamSettings"].toObject()))); - if (OutboundType == "vmess") + if (outboundType == "vmess") { vmess = StructFromJsonString(JsonToString(outboundEntry["settings"].toObject()["vnext"].toArray().first().toObject())); @@ -60,7 +64,7 @@ OutboundEditor::OutboundEditor(OUTBOUND outboundEntry, QWidget *parent) : Outbou socks.address = vmess.address; socks.port = vmess.port; } - else if (OutboundType == "shadowsocks") + else if (outboundType == "shadowsocks") { shadowsocks = StructFromJsonString( JsonToString(outboundEntry["settings"].toObject()["servers"].toArray().first().toObject())); @@ -69,7 +73,7 @@ OutboundEditor::OutboundEditor(OUTBOUND outboundEntry, QWidget *parent) : Outbou socks.address = shadowsocks.address; socks.port = shadowsocks.port; } - else if (OutboundType == "socks") + else if (outboundType == "socks") { socks = StructFromJsonString(JsonToString(outboundEntry["settings"].toObject()["servers"].toArray().first().toObject())); @@ -97,24 +101,24 @@ QString OutboundEditor::GetFriendlyName() { auto host = ipLineEdit->text().replace(":", "-").replace("/", "_").replace("\\", "_"); auto port = portLineEdit->text().replace(":", "-").replace("/", "_").replace("\\", "_"); - auto type = OutboundType; - QString name = Tag.isEmpty() ? host + "-[" + port + "]-" + type : Tag; + auto type = outboundType; + QString name = tag.isEmpty() ? host + "-[" + port + "]-" + type : tag; return name; } OUTBOUND OutboundEditor::GenerateConnectionJson() { OUTBOUNDSETTING settings; - auto streaming = GetRootObject(ssWidget->GetStreamSettings()); + auto streaming = GetRootObject(streamSettingsWidget->GetStreamSettings()); - if (OutboundType == "vmess") + if (outboundType == "vmess") { // VMess is only a ServerObject, and we need an array { "vnext": [] } QJsonArray vnext; vnext.append(GetRootObject(vmess)); settings.insert("vnext", vnext); } - else if (OutboundType == "shadowsocks") + else if (outboundType == "shadowsocks") { streaming = QJsonObject(); LOG(MODULE_CONNECTION, "Shadowsocks outbound does not need StreamSettings.") @@ -122,7 +126,7 @@ OUTBOUND OutboundEditor::GenerateConnectionJson() servers.append(GetRootObject(shadowsocks)); settings["servers"] = servers; } - else if (OutboundType == "socks") + else if (outboundType == "socks") { if (!socks.users.isEmpty() && socks.users.first().user.isEmpty() && socks.users.first().pass.isEmpty()) { @@ -136,14 +140,14 @@ OUTBOUND OutboundEditor::GenerateConnectionJson() settings["servers"] = servers; } - auto root = GenerateOutboundEntry(OutboundType, settings, streaming, Mux, "0.0.0.0", Tag); - root[QV2RAY_USE_FPROXY_KEY] = useFProxy; + auto root = GenerateOutboundEntry(outboundType, settings, streaming, Mux, "0.0.0.0", tag); + root[QV2RAY_USE_FPROXY_KEY] = useForwardProxy; return root; } void OutboundEditor::ReloadGUI() { - if (OutboundType == "vmess") + if (outboundType == "vmess") { outBoundTypeCombo->setCurrentIndex(0); ipLineEdit->setText(vmess.address); @@ -152,7 +156,7 @@ void OutboundEditor::ReloadGUI() alterLineEdit->setValue(vmess.users.front().alterId); securityCombo->setCurrentText(vmess.users.front().security); } - else if (OutboundType == "shadowsocks") + else if (outboundType == "shadowsocks") { outBoundTypeCombo->setCurrentIndex(1); // ShadowSocks Configs @@ -164,7 +168,7 @@ void OutboundEditor::ReloadGUI() ss_passwordTxt->setText(shadowsocks.password); ss_encryptionMethod->setCurrentText(shadowsocks.method); } - else if (OutboundType == "socks") + else if (outboundType == "socks") { outBoundTypeCombo->setCurrentIndex(2); ipLineEdit->setText(socks.address); @@ -177,7 +181,7 @@ void OutboundEditor::ReloadGUI() socks_UserNameTxt->setText(socks.users.front().user); } - useFPCB->setChecked(useFProxy); + useFPCB->setChecked(useForwardProxy); muxEnabledCB->setChecked(Mux["enabled"].toBool()); muxConcurrencyTxt->setValue(Mux["concurrency"].toInt()); } @@ -222,7 +226,7 @@ void OutboundEditor::on_securityCombo_currentIndexChanged(const QString &arg1) void OutboundEditor::on_tagTxt_textEdited(const QString &arg1) { - Tag = arg1; + tag = arg1; } void OutboundEditor::on_muxEnabledCB_stateChanged(int arg1) @@ -245,13 +249,13 @@ void OutboundEditor::on_alterLineEdit_valueChanged(int arg1) void OutboundEditor::on_useFPCB_stateChanged(int arg1) { - useFProxy = arg1 == Qt::Checked; + useForwardProxy = arg1 == Qt::Checked; } void OutboundEditor::on_outBoundTypeCombo_currentIndexChanged(int index) { outboundTypeStackView->setCurrentIndex(index); - OutboundType = outBoundTypeCombo->currentText().toLower(); + outboundType = outBoundTypeCombo->currentText().toLower(); } void OutboundEditor::on_ss_emailTxt_textEdited(const QString &arg1) diff --git a/src/ui/editors/w_OutboundEditor.hpp b/src/ui/editors/w_OutboundEditor.hpp index 34a81964..53c56bca 100644 --- a/src/ui/editors/w_OutboundEditor.hpp +++ b/src/ui/editors/w_OutboundEditor.hpp @@ -14,7 +14,7 @@ class OutboundEditor Q_OBJECT public: explicit OutboundEditor(QWidget *parent = nullptr); - explicit OutboundEditor(OUTBOUND outboundEntry, QWidget *parent = nullptr); + explicit OutboundEditor(const OUTBOUND &outboundEntry, QWidget *parent = nullptr); ~OutboundEditor(); OUTBOUND OpenEditor(); QString GetFriendlyName(); @@ -25,56 +25,39 @@ class OutboundEditor void s_reload_config(bool need_restart); private slots: void on_buttonBox_accepted(); - void on_ipLineEdit_textEdited(const QString &arg1); - void on_portLineEdit_textEdited(const QString &arg1); - void on_idLineEdit_textEdited(const QString &arg1); - void on_tagTxt_textEdited(const QString &arg1); - void on_muxEnabledCB_stateChanged(int arg1); - void on_muxConcurrencyTxt_valueChanged(int arg1); - void on_alterLineEdit_valueChanged(int arg1); - void on_useFPCB_stateChanged(int arg1); - void on_outBoundTypeCombo_currentIndexChanged(int index); - void on_ss_emailTxt_textEdited(const QString &arg1); - void on_ss_passwordTxt_textEdited(const QString &arg1); - void on_ss_encryptionMethod_currentIndexChanged(const QString &arg1); - void on_ss_levelSpin_valueChanged(int arg1); - void on_ss_otaCheckBox_stateChanged(int arg1); - void on_socks_UserNameTxt_textEdited(const QString &arg1); - void on_socks_PasswordTxt_textEdited(const QString &arg1); - void on_securityCombo_currentIndexChanged(const QString &arg1); private: - QString Tag; + QString tag; void ReloadGUI(); - bool useFProxy; + bool useForwardProxy; OUTBOUND GenerateConnectionJson(); OUTBOUND Original; OUTBOUND Result; QJsonObject Mux; // // Connection Configs - QString OutboundType; + QString outboundType; // VMessServerObject vmess; ShadowSocksServerObject shadowsocks; SocksServerObject socks; // - StreamSettingsWidget *ssWidget; + StreamSettingsWidget *streamSettingsWidget; }; diff --git a/src/ui/w_PluginManager.cpp b/src/ui/w_PluginManager.cpp index ed2553ec..1feb196c 100644 --- a/src/ui/w_PluginManager.cpp +++ b/src/ui/w_PluginManager.cpp @@ -10,11 +10,11 @@ PluginManageWindow::PluginManageWindow(QWidget *parent) : QDialog(parent) setupUi(this); for (auto &plugin : PluginHost->AvailablePlugins()) { - const auto &info = PluginHost->GetPluginInfo(plugin); + const auto &info = PluginHost->GetPluginMetadata(plugin); auto item = new QListWidgetItem(pluginListWidget); - item->setCheckState(PluginHost->GetPluginEnableState(info.metadata.InternalName) ? Qt::Checked : Qt::Unchecked); - item->setData(Qt::UserRole, info.metadata.InternalName); - item->setText(info.metadata.Name + " (" + (info.isLoaded ? tr("Loaded") : tr("Not loaded")) + ")"); + item->setCheckState(PluginHost->GetPluginEnableState(info.InternalName) ? Qt::Checked : Qt::Unchecked); + item->setData(Qt::UserRole, info.InternalName); + item->setText(info.Name + " (" + (PluginHost->GetPluginLoadState(info.InternalName) ? tr("Loaded") : tr("Not loaded")) + ")"); pluginListWidget->addItem(item); } isLoading = false; @@ -27,25 +27,50 @@ PluginManageWindow::~PluginManageWindow() void PluginManageWindow::on_pluginListWidget_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous) { Q_UNUSED(previous) - auto &info = PluginHost->GetPluginInfo(current->data(Qt::UserRole).toString()); - if (info.pluginInterface) + auto &info = PluginHost->GetPluginMetadata(current->data(Qt::UserRole).toString()); + + pluginIconLabel->setPixmap(info.Icon.pixmap(pluginIconLabel->size() * devicePixelRatio())); + // + pluginNameLabel->setText(info.Name); + pluginAuthorLabel->setText(info.Author); + pluginDescriptionLabel->setText(info.Description); + pluginLibPathLabel->setText(PluginHost->GetPluginLibraryPath(info.InternalName)); + pluginStateLabel->setText(PluginHost->GetPluginLoadState(info.InternalName) ? tr("Loaded") : tr("Not loaded")); + pluginTypeLabel->setText(GetPluginTypeString(info.SpecialPluginType)); + pluginHookTypeLabel->setText(GetPluginCapabilityString(info.Capabilities)); + // + if (!current) { - pluginIconLabel->setPixmap(info.metadata.Icon.pixmap(pluginIconLabel->size() * devicePixelRatio())); - // - pluginNameLabel->setText(info.metadata.Name); - pluginAuthorLabel->setText(info.metadata.Author); - pluginDescriptionLabel->setText(info.metadata.Description); - pluginLibPathLabel->setText(info.libraryPath); - pluginStateLabel->setText(info.isLoaded ? tr("Loaded") : tr("Not loaded")); - pluginTypeLabel->setText(PluginHost->GetPluginTypeString(info.metadata.InternalName)); - pluginHookTypeLabel->setText(PluginHost->GetPluginHookTypeString(info.metadata.InternalName)); - pluginErrMessageTxt->setPlainText(info.errorMessage.isEmpty() ? "OK" : info.errorMessage); + return; + } + if (settingsWidget || settingsWidget.get()) + { + pluginSettingsLayout->removeWidget(settingsWidget.get()); + settingsWidget.reset(); + } + if (!PluginHost->GetPluginLoadState(info.InternalName)) + { + pluginUnloadLabel->setVisible(true); + pluginUnloadLabel->setText(tr("Plugin Not Loaded")); + return; + } + settingsWidget = PluginHost->GetPluginSettingsWidget(info.InternalName); + if (settingsWidget) + { + pluginUnloadLabel->setVisible(false); + settingsWidget.get()->setParent(this); + pluginSettingsLayout->addWidget(settingsWidget.get()); + } + else + { + pluginUnloadLabel->setVisible(true); + pluginUnloadLabel->setText(tr("Plugin does not have settings widget.")); } } void PluginManageWindow::on_pluginListWidget_itemClicked(QListWidgetItem *item) { - on_pluginListWidget_currentItemChanged(item, nullptr); + // on_pluginListWidget_currentItemChanged(item, nullptr); } void PluginManageWindow::on_pluginListWidget_itemChanged(QListWidgetItem *item) @@ -55,8 +80,8 @@ void PluginManageWindow::on_pluginListWidget_itemChanged(QListWidgetItem *item) bool isEnabled = item->checkState() == Qt::Checked; auto pluginInternalName = item->data(Qt::UserRole).toString(); PluginHost->SetPluginEnableState(pluginInternalName, isEnabled); - auto &info = PluginHost->GetPluginInfo(pluginInternalName); - item->setText(info.metadata.Name + " (" + (info.isLoaded ? tr("Loaded") : tr("Not loaded")) + ")"); + auto &info = PluginHost->GetPluginMetadata(pluginInternalName); + item->setText(info.Name + " (" + (PluginHost->GetPluginLoadState(info.InternalName) ? tr("Loaded") : tr("Not loaded")) + ")"); // if (!isEnabled) { @@ -64,43 +89,21 @@ void PluginManageWindow::on_pluginListWidget_itemChanged(QListWidgetItem *item) } } -void PluginManageWindow::on_pluginSettingsBtn_clicked() -{ - if (const auto ¤t = pluginListWidget->currentItem(); current != nullptr) - { - auto &info = PluginHost->GetPluginInfo(current->data(Qt::UserRole).toString()); - if (!info.isLoaded) - { - QvMessageBoxWarn(this, tr("Plugin not loaded"), - tr("This plugin has been unloaded or has been disabled, please enable or reload the plugin to continue.")); - return; - } - if (auto widget = info.pluginInterface->GetSettingsWidget(); widget != nullptr) - { - QDialog d; - widget->setParent(&d); - d.exec(); - widget->setParent(nullptr); - } - } -} - void PluginManageWindow::on_pluginEditSettingsJsonBtn_clicked() { if (const auto ¤t = pluginListWidget->currentItem(); current != nullptr) { - const auto &info = PluginHost->GetPluginInfo(current->data(Qt::UserRole).toString()); - if (!info.isLoaded) + const auto &info = PluginHost->GetPluginMetadata(current->data(Qt::UserRole).toString()); + if (!PluginHost->GetPluginLoadState(info.InternalName)) { - QvMessageBoxWarn(this, tr("Plugin not loaded"), - tr("This plugin has been unloaded or has been disabled, please enable or reload the plugin to continue.")); + QvMessageBoxWarn(this, tr("Plugin not loaded"), tr("This plugin is not loaded, please enable or reload the plugin to continue.")); return; } - JsonEditor w(info.pluginInterface->GetSettngs()); + JsonEditor w(PluginHost->GetPluginSettings(info.InternalName)); auto newConf = w.OpenEditor(); if (w.result() == QDialog::Accepted) { - info.pluginInterface->UpdateSettings(newConf); + PluginHost->SetPluginSettings(info.InternalName, newConf); } } } @@ -108,6 +111,5 @@ void PluginManageWindow::on_pluginEditSettingsJsonBtn_clicked() void PluginManageWindow::on_pluginListWidget_itemSelectionChanged() { auto needEnable = !pluginListWidget->selectedItems().isEmpty(); - pluginSettingsBtn->setEnabled(needEnable); pluginEditSettingsJsonBtn->setEnabled(needEnable); } diff --git a/src/ui/w_PluginManager.hpp b/src/ui/w_PluginManager.hpp index 3d5535b6..856a7814 100644 --- a/src/ui/w_PluginManager.hpp +++ b/src/ui/w_PluginManager.hpp @@ -18,12 +18,11 @@ class PluginManageWindow void on_pluginListWidget_itemClicked(QListWidgetItem *item); void on_pluginListWidget_itemChanged(QListWidgetItem *item); - void on_pluginSettingsBtn_clicked(); - void on_pluginEditSettingsJsonBtn_clicked(); void on_pluginListWidget_itemSelectionChanged(); private: + std::unique_ptr settingsWidget; bool isLoading = true; }; diff --git a/src/ui/w_PluginManager.ui b/src/ui/w_PluginManager.ui index b37c765a..64ba2988 100644 --- a/src/ui/w_PluginManager.ui +++ b/src/ui/w_PluginManager.ui @@ -35,245 +35,264 @@ - - - Plugin Info + + + 0 - - - - - - - Name - - - - - - - IBeamCursor - - - - - - true - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - Author - - - - - - - IBeamCursor - - - - - - true - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - Description - - - - - - - IBeamCursor - - - - - - true - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - Library Path - - - - - - - IBeamCursor - - - - - - true - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - State - - - - - - - IBeamCursor - - - - - - true - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - Capability - - - - - - - IBeamCursor - - - - - - true - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - Special Type - - - - - - - IBeamCursor - - - - - - true - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - Error Message - - - - - - - true - - - - - - - - - - - - 64 - 64 - - - - - - - Qt::AlignCenter - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - Open settings page for current plugin - - - Open plugin settings - - - - - - - Manually Edit Settings - - - - - - + + + Plugin Metadata + + + + + + + + Name + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + IBeamCursor + + + + + + true + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + Author + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + IBeamCursor + + + + + + true + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + Description + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + IBeamCursor + + + + + + true + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + Library Path + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + IBeamCursor + + + + + + true + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + State + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + IBeamCursor + + + + + + true + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + Capability + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + IBeamCursor + + + + + + true + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + Special Type + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + IBeamCursor + + + + + + true + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + + + + + + 64 + 64 + + + + + + + Qt::AlignCenter + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + Plugin Settings + + + + + + + + Plugin Not Loaded + + + Qt::AlignCenter + + + + + + + + + Manually Edit Settings + + + + + From f136a9efb5f89610dfcfb7cce7c76ab71d2713eb Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Thu, 9 Apr 2020 23:45:16 +0800 Subject: [PATCH 52/92] Update w_PluginManager.hpp --- src/ui/w_PluginManager.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ui/w_PluginManager.hpp b/src/ui/w_PluginManager.hpp index 856a7814..065f0bdb 100644 --- a/src/ui/w_PluginManager.hpp +++ b/src/ui/w_PluginManager.hpp @@ -3,6 +3,7 @@ #include "ui_w_PluginManager.h" #include +#include class PluginManageWindow : public QDialog From 3020df5726589d3308344275146703b867435703 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Fri, 10 Apr 2020 10:12:17 +0800 Subject: [PATCH 53/92] add: added Plugin OutboundEditor support. --- src/components/plugins/QvPluginHost.cpp | 8 +- src/components/plugins/QvPluginHost.hpp | 4 +- src/ui/editors/w_OutboundEditor.cpp | 182 ++++++++++++++---------- src/ui/editors/w_OutboundEditor.hpp | 13 +- src/ui/w_ImportConfig.cpp | 2 +- 5 files changed, 122 insertions(+), 87 deletions(-) diff --git a/src/components/plugins/QvPluginHost.cpp b/src/components/plugins/QvPluginHost.cpp index 486ad2fe..7d6479d2 100644 --- a/src/components/plugins/QvPluginHost.cpp +++ b/src/components/plugins/QvPluginHost.cpp @@ -179,7 +179,7 @@ namespace Qv2ray::components::plugins { for (auto &plugin : plugins) { - if (plugin.metadata.Capabilities.contains(CAPABILITY_STATS)) + if (plugin.isLoaded && plugin.metadata.Capabilities.contains(CAPABILITY_STATS)) { plugin.pluginInterface->GetEventHandler()->ProcessEvent_ConnectionStats(object); } @@ -189,7 +189,7 @@ namespace Qv2ray::components::plugins { for (auto &plugin : plugins) { - if (plugin.metadata.Capabilities.contains(CAPABILITY_CONNECTIVITY)) + if (plugin.isLoaded && plugin.metadata.Capabilities.contains(CAPABILITY_CONNECTIVITY)) { plugin.pluginInterface->GetEventHandler()->ProcessEvent_Connectivity(object); } @@ -199,7 +199,7 @@ namespace Qv2ray::components::plugins { for (auto &plugin : plugins) { - if (plugin.metadata.Capabilities.contains(CAPABILITY_CONNECTION_ENTRY)) + if (plugin.isLoaded && plugin.metadata.Capabilities.contains(CAPABILITY_CONNECTION_ENTRY)) { plugin.pluginInterface->GetEventHandler()->ProcessEvent_ConnectionEntry(object); } @@ -209,7 +209,7 @@ namespace Qv2ray::components::plugins { for (auto &plugin : plugins) { - if (plugin.metadata.Capabilities.contains(CAPABILITY_SYSTEM_PROXY)) + if (plugin.isLoaded && plugin.metadata.Capabilities.contains(CAPABILITY_SYSTEM_PROXY)) { plugin.pluginInterface->GetEventHandler()->ProcessEvent_SystemProxy(object); } diff --git a/src/components/plugins/QvPluginHost.hpp b/src/components/plugins/QvPluginHost.hpp index cf5a784a..8eed2a4b 100644 --- a/src/components/plugins/QvPluginHost.hpp +++ b/src/components/plugins/QvPluginHost.hpp @@ -46,7 +46,7 @@ namespace Qv2ray::components::plugins { return plugins.value(internalName).pluginInterface->GetSettingsWidget(); } - const inline std::unique_ptr GetPluginEditorWidget(const QString &internalName, UI_TYPE type) const + inline std::unique_ptr GetPluginEditorWidget(const QString &internalName, UI_TYPE type) const { return plugins.value(internalName).pluginInterface->GetEditorWidget(type); } @@ -62,7 +62,7 @@ namespace Qv2ray::components::plugins { return plugins.value(internalName).metadata; } - const QList> GetOutboundEditorWidgets() + const QList> GetOutboundEditorWidgets() const { QList> data; for (const auto &plugin : plugins) diff --git a/src/ui/editors/w_OutboundEditor.cpp b/src/ui/editors/w_OutboundEditor.cpp index b42a3b28..9ff75238 100644 --- a/src/ui/editors/w_OutboundEditor.cpp +++ b/src/ui/editors/w_OutboundEditor.cpp @@ -1,6 +1,5 @@ #include "w_OutboundEditor.hpp" -#include "components/plugins/QvPluginHost.hpp" #include "core/connection/Generation.hpp" #include "ui/editors/w_JsonEditor.hpp" #include "ui/editors/w_RoutesEditor.hpp" @@ -10,7 +9,7 @@ #include #include -OutboundEditor::OutboundEditor(QWidget *parent) : QDialog(parent), tag(""), Mux(), vmess(), shadowsocks() +OutboundEditor::OutboundEditor(QWidget *parent) : QDialog(parent), tag(OUTBOUND_TAG_PROXY) { QvMessageBusConnect(OutboundEditor); setupUi(this); @@ -19,16 +18,20 @@ OutboundEditor::OutboundEditor(QWidget *parent) : QDialog(parent), tag(""), Mux( streamSettingsWidget->SetStreamObject({}); transportFrame->addWidget(streamSettingsWidget); // - socks.users.push_back(SocksServerObject::UserObject()); - vmess.users.push_back(VMessServerObject::UserObject()); + socks.users.push_back({}); + vmess.users.push_back({}); // - auto data = PluginHost->GetOutboundEditorWidgets(); + auto pluginEditorWidgetsInfo = PluginHost->GetOutboundEditorWidgets(); + for (const auto &plugin : pluginEditorWidgetsInfo) + { + outBoundTypeCombo->addItem(plugin.first.displayName, plugin.first.protocol); + auto widget = PluginHost->GetPluginEditorWidget(plugin.second, UI_TYPE::UI_TYPE_OUTBOUND_EDITOR).release(); + auto index = outboundTypeStackView->addWidget(widget); + pluginWidgets.insert(index, { plugin.first, plugin.second, widget }); + } // outboundType = "vmess"; - tag = OUTBOUND_TAG_PROXY; useForwardProxy = false; - ReloadGUI(); - Result = GenerateConnectionJson(); } QvMessageBusSlotImpl(OutboundEditor) @@ -47,44 +50,8 @@ QvMessageBusSlotImpl(OutboundEditor) OutboundEditor::OutboundEditor(const OUTBOUND &outboundEntry, QWidget *parent) : OutboundEditor(parent) { - Original = outboundEntry; - tag = outboundEntry["tag"].toString(); - tagTxt->setText(tag); - outboundType = outboundEntry["protocol"].toString(); - Mux = outboundEntry["mux"].toObject(); - useForwardProxy = outboundEntry[QV2RAY_USE_FPROXY_KEY].toBool(false); - streamSettingsWidget->SetStreamObject(StructFromJsonString(JsonToString(outboundEntry["streamSettings"].toObject()))); - - if (outboundType == "vmess") - { - vmess = - StructFromJsonString(JsonToString(outboundEntry["settings"].toObject()["vnext"].toArray().first().toObject())); - shadowsocks.port = vmess.port; - shadowsocks.address = vmess.address; - socks.address = vmess.address; - socks.port = vmess.port; - } - else if (outboundType == "shadowsocks") - { - shadowsocks = StructFromJsonString( - JsonToString(outboundEntry["settings"].toObject()["servers"].toArray().first().toObject())); - vmess.address = shadowsocks.address; - vmess.port = shadowsocks.port; - socks.address = shadowsocks.address; - socks.port = shadowsocks.port; - } - else if (outboundType == "socks") - { - socks = - StructFromJsonString(JsonToString(outboundEntry["settings"].toObject()["servers"].toArray().first().toObject())); - vmess.address = socks.address; - vmess.port = socks.port; - shadowsocks.address = socks.address; - shadowsocks.port = socks.port; - } - + originalConfig = outboundEntry; ReloadGUI(); - Result = GenerateConnectionJson(); } OutboundEditor::~OutboundEditor() @@ -94,7 +61,7 @@ OutboundEditor::~OutboundEditor() OUTBOUND OutboundEditor::OpenEditor() { int resultCode = this->exec(); - return resultCode == QDialog::Accepted ? Result : Original; + return resultCode == QDialog::Accepted ? resultConfig : originalConfig; } QString OutboundEditor::GetFriendlyName() @@ -115,6 +82,8 @@ OUTBOUND OutboundEditor::GenerateConnectionJson() { // VMess is only a ServerObject, and we need an array { "vnext": [] } QJsonArray vnext; + vmess.address = address; + vmess.port = port; vnext.append(GetRootObject(vmess)); settings.insert("vnext", vnext); } @@ -123,6 +92,8 @@ OUTBOUND OutboundEditor::GenerateConnectionJson() streaming = QJsonObject(); LOG(MODULE_CONNECTION, "Shadowsocks outbound does not need StreamSettings.") QJsonArray servers; + shadowsocks.address = address; + shadowsocks.port = port; servers.append(GetRootObject(shadowsocks)); settings["servers"] = servers; } @@ -133,25 +104,60 @@ OUTBOUND OutboundEditor::GenerateConnectionJson() LOG(MODULE_UI, "Removed empty user form SOCKS settings") socks.users.clear(); } + socks.address = address; + socks.port = port; streaming = QJsonObject(); LOG(MODULE_CONNECTION, "Socks outbound does not need StreamSettings.") QJsonArray servers; servers.append(GetRootObject(socks)); settings["servers"] = servers; } + else + { + bool processed = false; + for (const auto &plugin : pluginWidgets) + { + if (get<0>(plugin).protocol == outboundType) + { + get<2>(plugin)->SetHostInfo(address, port); + settings = OUTBOUNDSETTING(get<2>(plugin)->GetContent()); + processed = true; + break; + } + } + if (!processed) + { + QvMessageBoxWarn(this, tr("Unknown outbound type."), + tr("The specified outbound type is not supported, this may happen due to a plugin failure.")); + } + } - auto root = GenerateOutboundEntry(outboundType, settings, streaming, Mux, "0.0.0.0", tag); + auto root = GenerateOutboundEntry(outboundType, settings, streaming, muxConfig, "0.0.0.0", tag); root[QV2RAY_USE_FPROXY_KEY] = useForwardProxy; return root; } void OutboundEditor::ReloadGUI() { + tag = originalConfig["tag"].toString(); + tagTxt->setText(tag); + outboundType = originalConfig["protocol"].toString(); + muxConfig = originalConfig["mux"].toObject(); + useForwardProxy = originalConfig[QV2RAY_USE_FPROXY_KEY].toBool(false); + streamSettingsWidget->SetStreamObject(StructFromJsonString(JsonToString(originalConfig["streamSettings"].toObject()))); + // + useFPCB->setChecked(useForwardProxy); + muxEnabledCB->setChecked(muxConfig["enabled"].toBool()); + muxConcurrencyTxt->setValue(muxConfig["concurrency"].toInt()); + // + const auto &settings = originalConfig["settings"].toObject(); + // if (outboundType == "vmess") { outBoundTypeCombo->setCurrentIndex(0); - ipLineEdit->setText(vmess.address); - portLineEdit->setText(QSTRN(vmess.port)); + vmess = StructFromJsonString(JsonToString(settings["vnext"].toArray().first().toObject())); + address = vmess.address; + port = vmess.port; idLineEdit->setText(vmess.users.front().id); alterLineEdit->setValue(vmess.users.front().alterId); securityCombo->setCurrentText(vmess.users.front().security); @@ -159,9 +165,10 @@ void OutboundEditor::ReloadGUI() else if (outboundType == "shadowsocks") { outBoundTypeCombo->setCurrentIndex(1); + shadowsocks = StructFromJsonString(JsonToString(settings["servers"].toArray().first().toObject())); + address = shadowsocks.address; + port = shadowsocks.port; // ShadowSocks Configs - ipLineEdit->setText(shadowsocks.address); - portLineEdit->setText(QSTRN(shadowsocks.port)); ss_emailTxt->setText(shadowsocks.email); ss_levelSpin->setValue(shadowsocks.level); ss_otaCheckBox->setChecked(shadowsocks.ota); @@ -171,47 +178,62 @@ void OutboundEditor::ReloadGUI() else if (outboundType == "socks") { outBoundTypeCombo->setCurrentIndex(2); - ipLineEdit->setText(socks.address); - portLineEdit->setText(QSTRN(socks.port)); - + socks = StructFromJsonString(JsonToString(settings["servers"].toArray().first().toObject())); + address = socks.address; + port = socks.port; if (socks.users.empty()) socks.users.push_back(SocksServerObject::UserObject()); socks_PasswordTxt->setText(socks.users.front().pass); socks_UserNameTxt->setText(socks.users.front().user); } - - useFPCB->setChecked(useForwardProxy); - muxEnabledCB->setChecked(Mux["enabled"].toBool()); - muxConcurrencyTxt->setValue(Mux["concurrency"].toInt()); + else + { + bool processed = false; + for (const auto &index : pluginWidgets.keys()) + { + const auto &plugin = pluginWidgets.value(index); + if (get<0>(plugin).protocol == outboundType) + { + get<2>(plugin)->SetContent(settings); + outBoundTypeCombo->setCurrentIndex(index); + auto [_address, _port] = get<2>(plugin)->GetHostInfo(); + address = _address; + port = _port; + processed = true; + break; + } + } + if (!processed) + { + QvMessageBoxWarn(this, tr("Unknown outbound."), + tr("The specified outbound type is invalid, this may be caused by a plugin failure.")); + } + } + // + ipLineEdit->setText(address); + portLineEdit->setText(QSTRN(port)); } void OutboundEditor::on_buttonBox_accepted() { - Result = GenerateConnectionJson(); + resultConfig = GenerateConnectionJson(); } void OutboundEditor::on_ipLineEdit_textEdited(const QString &arg1) { - vmess.address = arg1; - shadowsocks.address = arg1; - socks.address = arg1; + address = arg1; } void OutboundEditor::on_portLineEdit_textEdited(const QString &arg1) { - if (arg1 != "") - { - vmess.port = arg1.toInt(); - shadowsocks.port = arg1.toInt(); - socks.port = arg1.toInt(); - } + port = arg1.toInt(); } void OutboundEditor::on_idLineEdit_textEdited(const QString &arg1) { if (vmess.users.empty()) - vmess.users.push_back(VMessServerObject::UserObject()); + vmess.users.push_back({}); vmess.users.front().id = arg1; } @@ -219,7 +241,7 @@ void OutboundEditor::on_idLineEdit_textEdited(const QString &arg1) void OutboundEditor::on_securityCombo_currentIndexChanged(const QString &arg1) { if (vmess.users.empty()) - vmess.users.push_back(VMessServerObject::UserObject()); + vmess.users.push_back({}); vmess.users.front().security = arg1; } @@ -231,18 +253,18 @@ void OutboundEditor::on_tagTxt_textEdited(const QString &arg1) void OutboundEditor::on_muxEnabledCB_stateChanged(int arg1) { - Mux["enabled"] = arg1 == Qt::Checked; + muxConfig["enabled"] = arg1 == Qt::Checked; } void OutboundEditor::on_muxConcurrencyTxt_valueChanged(int arg1) { - Mux["concurrency"] = arg1; + muxConfig["concurrency"] = arg1; } void OutboundEditor::on_alterLineEdit_valueChanged(int arg1) { if (vmess.users.empty()) - vmess.users.push_back(VMessServerObject::UserObject()); + vmess.users.push_back({}); vmess.users.front().alterId = arg1; } @@ -254,8 +276,16 @@ void OutboundEditor::on_useFPCB_stateChanged(int arg1) void OutboundEditor::on_outBoundTypeCombo_currentIndexChanged(int index) { + // 0, 1, 2 as built-in vmess, ss, socks outboundTypeStackView->setCurrentIndex(index); - outboundType = outBoundTypeCombo->currentText().toLower(); + if (index < 3) + { + outboundType = outBoundTypeCombo->currentText().toLower(); + } + else + { + outboundType = get<0>(pluginWidgets.value(index)).protocol; + } } void OutboundEditor::on_ss_emailTxt_textEdited(const QString &arg1) @@ -286,13 +316,13 @@ void OutboundEditor::on_ss_otaCheckBox_stateChanged(int arg1) void OutboundEditor::on_socks_UserNameTxt_textEdited(const QString &arg1) { if (socks.users.isEmpty()) - socks.users.push_back(SocksServerObject::UserObject()); + socks.users.push_back({}); socks.users.front().user = arg1; } void OutboundEditor::on_socks_PasswordTxt_textEdited(const QString &arg1) { if (socks.users.isEmpty()) - socks.users.push_back(SocksServerObject::UserObject()); + socks.users.push_back({}); socks.users.front().pass = arg1; } diff --git a/src/ui/editors/w_OutboundEditor.hpp b/src/ui/editors/w_OutboundEditor.hpp index 53c56bca..44d13b41 100644 --- a/src/ui/editors/w_OutboundEditor.hpp +++ b/src/ui/editors/w_OutboundEditor.hpp @@ -1,5 +1,6 @@ #pragma once #include "base/Qv2rayBase.hpp" +#include "components/plugins/QvPluginHost.hpp" #include "ui/messaging/QvMessageBus.hpp" #include "ui/widgets/StreamSettingsWidget.hpp" #include "ui_w_OutboundEditor.h" @@ -13,13 +14,13 @@ class OutboundEditor { Q_OBJECT public: - explicit OutboundEditor(QWidget *parent = nullptr); explicit OutboundEditor(const OUTBOUND &outboundEntry, QWidget *parent = nullptr); ~OutboundEditor(); OUTBOUND OpenEditor(); QString GetFriendlyName(); private: + explicit OutboundEditor(QWidget *parent = nullptr); QvMessageBusSlotDecl; signals: void s_reload_config(bool need_restart); @@ -48,16 +49,20 @@ class OutboundEditor void ReloadGUI(); bool useForwardProxy; OUTBOUND GenerateConnectionJson(); - OUTBOUND Original; - OUTBOUND Result; - QJsonObject Mux; + OUTBOUND originalConfig; + OUTBOUND resultConfig; + QJsonObject muxConfig; // // Connection Configs QString outboundType; + QString address; + int port; // VMessServerObject vmess; ShadowSocksServerObject shadowsocks; SocksServerObject socks; // StreamSettingsWidget *streamSettingsWidget; + // + QMap> pluginWidgets; }; diff --git a/src/ui/w_ImportConfig.cpp b/src/ui/w_ImportConfig.cpp index 2f4f8758..0190c470 100644 --- a/src/ui/w_ImportConfig.cpp +++ b/src/ui/w_ImportConfig.cpp @@ -260,7 +260,7 @@ void ImportConfigWindow::on_errorsList_currentItemChanged(QListWidgetItem *curre void ImportConfigWindow::on_connectionEditBtn_clicked() { - OutboundEditor w(this); + OutboundEditor w(OUTBOUND(), this); auto outboundEntry = w.OpenEditor(); bool isChanged = w.result() == QDialog::Accepted; QString alias = w.GetFriendlyName(); From 059bad8d4b3934ab5d93259a7dd7f290aae440e9 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Fri, 10 Apr 2020 12:44:34 +0800 Subject: [PATCH 54/92] add: support Plugin Serialization --- src/components/plugins/QvPluginHost.cpp | 68 +++++++++++++++++++++++++ src/components/plugins/QvPluginHost.hpp | 34 +++++-------- src/components/plugins/interface | 2 +- src/core/connection/Serialization.cpp | 57 +++++++++++++-------- src/core/connection/Serialization.hpp | 4 +- src/ui/editors/w_OutboundEditor.cpp | 24 +++++---- src/ui/editors/w_OutboundEditor.hpp | 2 +- 7 files changed, 135 insertions(+), 56 deletions(-) diff --git a/src/components/plugins/QvPluginHost.cpp b/src/components/plugins/QvPluginHost.cpp index 7d6479d2..366eda21 100644 --- a/src/components/plugins/QvPluginHost.cpp +++ b/src/components/plugins/QvPluginHost.cpp @@ -216,6 +216,74 @@ namespace Qv2ray::components::plugins } } + const QList QvPluginHost::GetOutboundEditorWidgets() const + { + QList data; + for (const auto &plugin : plugins) + { + if (!plugin.isLoaded) + continue; + auto editor = plugin.pluginInterface->GetEditorWidget(UI_TYPE::UI_TYPE_OUTBOUND_EDITOR); + if (editor) + { + data.append(editor.release()); + } + } + return data; + } + + const QMultiHash> QvPluginHost::TryDeserializeShareLink(const QString &sharelink, // + QString *prefix, // + QString *errMessage, // + QString *newGroupName, // + bool *status) const + { + Q_UNUSED(newGroupName) + QMultiHash> data; + *status = true; + for (const auto &plugin : plugins) + { + if (plugin.isLoaded && plugin.metadata.SpecialPluginType.contains(SPECIAL_TYPE_SERIALIZOR)) + { + auto serializer = plugin.pluginInterface->GetSerializer(); + bool thisPluginCanHandle = false; + for (const auto &prefix : serializer->ShareLinkPrefixes()) + { + thisPluginCanHandle = thisPluginCanHandle || sharelink.startsWith(prefix); + } + if (thisPluginCanHandle) + { + auto [protocol, outboundSettings] = serializer->DeserializeOutbound(sharelink, prefix, errMessage); + *status = *status && errMessage->isEmpty(); + data.insert(*prefix, { protocol, outboundSettings }); + } + } + } + return data; + } + const QString QvPluginHost::TrySerializeShareLink(const QString &protocol, // + const QJsonObject &outboundSettings, // + const QString &alias, // + const QString &groupName, // + bool *status) const + { + *status = false; + for (const auto &plugin : plugins) + { + if (plugin.isLoaded && plugin.metadata.SpecialPluginType.contains(SPECIAL_TYPE_SERIALIZOR)) + { + auto serializer = plugin.pluginInterface->GetSerializer(); + if (serializer->OutboundProtocols().contains(protocol)) + { + auto link = serializer->SerializeOutbound(protocol, alias, groupName, outboundSettings); + *status = true; + return link; + } + } + } + return ""; + } + const QString GetPluginTypeString(const SPECIAL_TYPE_FLAGS &types) { QStringList typesList; diff --git a/src/components/plugins/QvPluginHost.hpp b/src/components/plugins/QvPluginHost.hpp index 8eed2a4b..3e05bf9c 100644 --- a/src/components/plugins/QvPluginHost.hpp +++ b/src/components/plugins/QvPluginHost.hpp @@ -46,10 +46,6 @@ namespace Qv2ray::components::plugins { return plugins.value(internalName).pluginInterface->GetSettingsWidget(); } - inline std::unique_ptr GetPluginEditorWidget(const QString &internalName, UI_TYPE type) const - { - return plugins.value(internalName).pluginInterface->GetEditorWidget(type); - } const inline QJsonObject GetPluginSettings(const QString &internalName) const { return plugins.value(internalName).pluginInterface->GetSettngs(); @@ -62,23 +58,19 @@ namespace Qv2ray::components::plugins { return plugins.value(internalName).metadata; } - const QList> GetOutboundEditorWidgets() const - { - QList> data; - for (const auto &plugin : plugins) - { - if (!plugin.isLoaded) - continue; - if (auto editor = plugin.pluginInterface->GetEditorWidget(UI_TYPE::UI_TYPE_OUTBOUND_EDITOR); editor) - { - for (const auto &cap : editor->OutboundCapabilities()) - { - data.append({ cap, plugin.metadata.InternalName }); - } - } - } - return data; - } + // + const QMultiHash> TryDeserializeShareLink(const QString &sharelink, // + QString *prefix, // + QString *errMessage, // + QString *newGroupName, // + bool *status) const; + // + const QString TrySerializeShareLink(const QString &protocol, // + const QJsonObject &outboundSettings, // + const QString &alias, // + const QString &groupName, // + bool *status) const; + const QList GetOutboundEditorWidgets() const; // void Send_ConnectionStatsEvent(const QvConnectionStatsEventObject &object); void Send_ConnectivityEvent(const QvConnectivityEventObject &object); diff --git a/src/components/plugins/interface b/src/components/plugins/interface index 7df6621b..71dab933 160000 --- a/src/components/plugins/interface +++ b/src/components/plugins/interface @@ -1 +1 @@ -Subproject commit 7df6621bcdfcddadd94bdfd253306e1680035941 +Subproject commit 71dab9339510d7e766bff00c89159f0407cff910 diff --git a/src/core/connection/Serialization.cpp b/src/core/connection/Serialization.cpp index 2b12af8e..56a3445f 100644 --- a/src/core/connection/Serialization.cpp +++ b/src/core/connection/Serialization.cpp @@ -2,6 +2,7 @@ #include "Generation.hpp" #include "common/QvHelpers.hpp" +#include "components/plugins/QvPluginHost.hpp" #include "core/CoreUtils.hpp" #include "core/handler/ConfigHandler.hpp" @@ -11,29 +12,45 @@ namespace Qv2ray::core::connection { QMultiHash ConvertConfigFromString(const QString &link, QString *prefix, QString *errMessage, QString *newGroupName) { - QMultiHash config; + QMultiHash connectionConf; if (link.startsWith("vmess://")) { auto conf = ConvertConfigFromVMessString(link, prefix, errMessage); - config.insert(*prefix, conf); + connectionConf.insert(*prefix, conf); } else if (link.startsWith("ss://")) { auto conf = ConvertConfigFromSSString(link, prefix, errMessage); - config.insert(*prefix, conf); + connectionConf.insert(*prefix, conf); } else if (link.startsWith("ssd://")) { QStringList errMessageList; - config = ConvertConfigFromSSDString(link, newGroupName, &errMessageList); + connectionConf = ConvertConfigFromSSDString(link, newGroupName, &errMessageList); *errMessage = errMessageList.join(NEWLINE); } else { - *errMessage = QObject::tr("Unsupported share link format."); + bool ok = false; + auto configs = PluginHost->TryDeserializeShareLink(link, prefix, errMessage, newGroupName, &ok); + for (const auto &key : configs.keys()) + { + auto vals = configs.values(key); + for (const auto &val : vals) + { + CONFIGROOT root; + auto outbound = GenerateOutboundEntry(val.first, OUTBOUNDSETTING(val.second), {}); + root.insert("outbounds", QJsonArray{ outbound }); + connectionConf.insert(key, root); + } + } + if (!ok) + { + *errMessage = QObject::tr("Unsupported share link format."); + } } - return config; + return connectionConf; } const QString ConvertConfigToString(const ConnectionId &id, bool isSip002) @@ -45,51 +62,51 @@ namespace Qv2ray::core::connection return QV2RAY_SERIALIZATION_COMPLEX_CONFIG_PLACEHOLDER; } auto server = ConnectionManager->GetConnectionRoot(id); - return ConvertConfigToString(alias, server, isSip002); + return ConvertConfigToString(alias, GetDisplayName(GetConnectionGroupId(id)), server, isSip002); } - const QString ConvertConfigToString(const QString &alias, const CONFIGROOT &server, bool isSip002) + const QString ConvertConfigToString(const QString &alias, const QString &groupName, const CONFIGROOT &server, bool isSip002) { - OUTBOUND outbound = OUTBOUND(server["outbounds"].toArray().first().toObject()); - auto type = outbound["protocol"].toString(); + const auto outbound = OUTBOUND(server["outbounds"].toArray().first().toObject()); + const auto type = outbound["protocol"].toString(); + const auto &settings = outbound["settings"].toObject(); QString sharelink = ""; - if (type == "vmess") { - auto vmessServer = - StructFromJsonString(JsonToString(outbound["settings"].toObject()["vnext"].toArray().first().toObject())); + auto vmessServer = StructFromJsonString(JsonToString(settings["vnext"].toArray().first().toObject())); auto transport = StructFromJsonString(JsonToString(outbound["streamSettings"].toObject())); sharelink = vmess::ConvertConfigToVMessString(transport, vmessServer, alias); } else if (type == "shadowsocks") { - auto ssServer = StructFromJsonString( - JsonToString(outbound["settings"].toObject()["servers"].toArray().first().toObject())); + auto ssServer = StructFromJsonString(JsonToString(settings["servers"].toArray().first().toObject())); sharelink = ss::ConvertConfigToSSString(ssServer, alias, isSip002); } else { - if (!type.isEmpty()) + if (type.isEmpty()) { - // DEBUG(MODULE_CONNECTION, "WARNING: Unsupported outbound type: " + type) + DEBUG(MODULE_CONNECTION, "WARNING: Empty outbound type.") } else { - DEBUG(MODULE_CONNECTION, "WARNING: Empty outbound type.") + bool ok = false; + sharelink = PluginHost->TrySerializeShareLink(type, settings, alias, groupName, &ok); + Q_UNUSED(ok) } } return sharelink; } - QString DecodeSubscriptionString(QByteArray arr) + QString DecodeSubscriptionString(const QByteArray &arr) { // String may start with: vmess:// and ss:// // We only process vmess:// here // Some subscription providers may use plain vmess:// saperated by // lines But others may use base64 of above. auto result = QString::fromUtf8(arr).trimmed(); - return result.startsWith("vmess://") ? result : Base64Decode(result); + return result.contains("://") ? result : Base64Decode(result); } } // namespace Serialization } // namespace Qv2ray::core::connection diff --git a/src/core/connection/Serialization.hpp b/src/core/connection/Serialization.hpp index 06068791..9db716ac 100644 --- a/src/core/connection/Serialization.hpp +++ b/src/core/connection/Serialization.hpp @@ -15,11 +15,11 @@ namespace Qv2ray::core::connection inline auto QV2RAY_SSD_DEFAULT_NAME_PATTERN = QObject::tr("%1 - %2 (rate %3)"); // // General - QString DecodeSubscriptionString(QByteArray arr); + QString DecodeSubscriptionString(const QByteArray &arr); QMultiHash ConvertConfigFromString(const QString &link, QString *aliasPrefix, QString *errMessage, QString *newGroupName = nullptr); const QString ConvertConfigToString(const ConnectionId &id, bool isSip002 = false); - const QString ConvertConfigToString(const QString &alias, const CONFIGROOT &server, bool isSip002); + const QString ConvertConfigToString(const QString &alias, const QString &groupName, const CONFIGROOT &server, bool isSip002); // // VMess URI Protocol namespace vmess diff --git a/src/ui/editors/w_OutboundEditor.cpp b/src/ui/editors/w_OutboundEditor.cpp index 9ff75238..69cc5892 100644 --- a/src/ui/editors/w_OutboundEditor.cpp +++ b/src/ui/editors/w_OutboundEditor.cpp @@ -24,10 +24,12 @@ OutboundEditor::OutboundEditor(QWidget *parent) : QDialog(parent), tag(OUTBOUND_ auto pluginEditorWidgetsInfo = PluginHost->GetOutboundEditorWidgets(); for (const auto &plugin : pluginEditorWidgetsInfo) { - outBoundTypeCombo->addItem(plugin.first.displayName, plugin.first.protocol); - auto widget = PluginHost->GetPluginEditorWidget(plugin.second, UI_TYPE::UI_TYPE_OUTBOUND_EDITOR).release(); - auto index = outboundTypeStackView->addWidget(widget); - pluginWidgets.insert(index, { plugin.first, plugin.second, widget }); + for (const auto &_d : plugin->OutboundCapabilities()) + { + outBoundTypeCombo->addItem(_d.displayName, _d.protocol); + auto index = outboundTypeStackView->addWidget(plugin); + pluginWidgets.insert(index, { _d, plugin }); + } } // outboundType = "vmess"; @@ -117,10 +119,10 @@ OUTBOUND OutboundEditor::GenerateConnectionJson() bool processed = false; for (const auto &plugin : pluginWidgets) { - if (get<0>(plugin).protocol == outboundType) + if (plugin.first.protocol == outboundType) { - get<2>(plugin)->SetHostInfo(address, port); - settings = OUTBOUNDSETTING(get<2>(plugin)->GetContent()); + plugin.second->SetHostInfo(address, port); + settings = OUTBOUNDSETTING(plugin.second->GetContent()); processed = true; break; } @@ -193,11 +195,11 @@ void OutboundEditor::ReloadGUI() for (const auto &index : pluginWidgets.keys()) { const auto &plugin = pluginWidgets.value(index); - if (get<0>(plugin).protocol == outboundType) + if (plugin.first.protocol == outboundType) { - get<2>(plugin)->SetContent(settings); + plugin.second->SetContent(settings); outBoundTypeCombo->setCurrentIndex(index); - auto [_address, _port] = get<2>(plugin)->GetHostInfo(); + auto [_address, _port] = plugin.second->GetHostInfo(); address = _address; port = _port; processed = true; @@ -284,7 +286,7 @@ void OutboundEditor::on_outBoundTypeCombo_currentIndexChanged(int index) } else { - outboundType = get<0>(pluginWidgets.value(index)).protocol; + outboundType = pluginWidgets.value(index).first.protocol; } } diff --git a/src/ui/editors/w_OutboundEditor.hpp b/src/ui/editors/w_OutboundEditor.hpp index 44d13b41..102a4428 100644 --- a/src/ui/editors/w_OutboundEditor.hpp +++ b/src/ui/editors/w_OutboundEditor.hpp @@ -64,5 +64,5 @@ class OutboundEditor // StreamSettingsWidget *streamSettingsWidget; // - QMap> pluginWidgets; + QMap> pluginWidgets; }; From e36aad6afc9c4244fa59d02d8aa42a6fdbf136ba Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Fri, 10 Apr 2020 17:15:16 +0800 Subject: [PATCH 55/92] change: lowered default log level --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index b9945e18..a1eeddcc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -140,7 +140,7 @@ bool initialiseQv2ray() Qv2rayConfig conf; conf.kernelConfig.KernelPath(QString(QV2RAY_DEFAULT_VCORE_PATH)); conf.kernelConfig.AssetsPath(QString(QV2RAY_DEFAULT_VASSETS_PATH)); - conf.logLevel = 2; + conf.logLevel = 3; conf.uiConfig.language = QLocale::system().name(); // // Save initial config. From fa2a3769de4883eba394933669474c607758cbb5 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Fri, 10 Apr 2020 17:50:07 +0800 Subject: [PATCH 56/92] add: added QvPluginInterface version in PreferenceWindow --- src/ui/w_PreferencesWindow.cpp | 2 + src/ui/w_PreferencesWindow.ui | 86 ++++++++++++++++++++-------------- 2 files changed, 52 insertions(+), 36 deletions(-) diff --git a/src/ui/w_PreferencesWindow.cpp b/src/ui/w_PreferencesWindow.cpp index 8f832de6..ee7fafd2 100644 --- a/src/ui/w_PreferencesWindow.cpp +++ b/src/ui/w_PreferencesWindow.cpp @@ -4,6 +4,7 @@ #include "common/QvHelpers.hpp" #include "common/QvTranslator.hpp" #include "components/autolaunch/QvAutoLaunch.hpp" +#include "components/plugins/interface/QvPluginInterface.hpp" #include "components/plugins/toolbar/QvToolbar.hpp" #include "core/connection/ConnectionIO.hpp" #include "core/handler/ConfigHandler.hpp" @@ -69,6 +70,7 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent), Current qvBuildInfo->setText(QV2RAY_BUILD_INFO); qvBuildExInfo->setText(QV2RAY_BUILD_EXTRA_INFO); qvBuildTime->setText(__DATE__ " " __TIME__); + qvPluginInterfaceVersionLabel->setText(tr("Version: %1").arg(QSTRN(QV2RAY_PLUGIN_INTERFACE_VERSION))); // // Deep copy CurrentConfig = GlobalConfig; diff --git a/src/ui/w_PreferencesWindow.ui b/src/ui/w_PreferencesWindow.ui index 5c27bc55..73a8fa40 100644 --- a/src/ui/w_PreferencesWindow.ui +++ b/src/ui/w_PreferencesWindow.ui @@ -1573,31 +1573,14 @@ - - - - - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - License - - - - + - Official Repo + Plugin Interface - + @@ -1619,7 +1602,41 @@ - + + + + Extra Build Info + + + + + + + + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + + + + + @@ -1653,16 +1670,6 @@ - - - - - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - @@ -1670,15 +1677,15 @@ - - + + - Extra Build Info + Official Repo - - + + @@ -1687,6 +1694,13 @@ + + + + License + + + From 19a30e7dcae4f00bfe4eacc3a4171d63189c0560 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Fri, 10 Apr 2020 17:59:12 +0800 Subject: [PATCH 57/92] add: added PluginKernelInteractions --- CMakeLists.txt | 8 +++++--- makespec/BUILDVERSION | 2 +- src/core/handler/ConfigHandler.hpp | 2 +- ...V2rayInstanceHandler.cpp => KernelInstanceHandler.cpp} | 0 src/core/kernel/PluginKernelInteractions.cpp | 0 src/core/kernel/PluginKernelInteractions.hpp | 0 ...KernelInteractions.cpp => V2rayKernelInteractions.cpp} | 2 +- ...KernelInteractions.hpp => V2rayKernelInteractions.hpp} | 0 src/ui/w_ImportConfig.cpp | 2 +- src/ui/w_PreferencesWindow.cpp | 2 +- 10 files changed, 10 insertions(+), 8 deletions(-) rename src/core/handler/{V2rayInstanceHandler.cpp => KernelInstanceHandler.cpp} (100%) create mode 100644 src/core/kernel/PluginKernelInteractions.cpp create mode 100644 src/core/kernel/PluginKernelInteractions.hpp rename src/core/kernel/{KernelInteractions.cpp => V2rayKernelInteractions.cpp} (99%) rename src/core/kernel/{KernelInteractions.hpp => V2rayKernelInteractions.hpp} (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index e4c22b87..94c33be8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -183,9 +183,10 @@ set(QV2RAY_SOURCES src/core/connection/Serialization_vmess.cpp src/core/CoreUtils.cpp src/core/handler/ConfigHandler.cpp - src/core/handler/V2rayInstanceHandler.cpp + src/core/handler/KernelInstanceHandler.cpp src/core/kernel/APIBackend.cpp - src/core/kernel/KernelInteractions.cpp + src/core/kernel/V2rayKernelInteractions.cpp + src/core/kernel/PluginKernelInteractions.cpp src/core/kernel/QvKernelABIChecker.cpp src/core/settings/SettingsBackend.cpp src/core/settings/SettingsUpgrade.cpp @@ -266,7 +267,8 @@ set(QV2RAY_SOURCES src/core/CoreUtils.hpp src/core/handler/ConfigHandler.hpp src/core/kernel/APIBackend.hpp - src/core/kernel/KernelInteractions.hpp + src/core/kernel/V2rayKernelInteractions.hpp + src/core/kernel/PluginKernelInteractions.hpp src/core/kernel/QvKernelABIChecker.hpp src/core/settings/SettingsBackend.hpp src/ui/editors/w_InboundEditor.hpp diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 5f7fc967..c4c65307 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5233 \ No newline at end of file +5239 \ No newline at end of file diff --git a/src/core/handler/ConfigHandler.hpp b/src/core/handler/ConfigHandler.hpp index 827850b0..75c4275c 100644 --- a/src/core/handler/ConfigHandler.hpp +++ b/src/core/handler/ConfigHandler.hpp @@ -6,7 +6,7 @@ #include "core/CoreSafeTypes.hpp" #include "core/CoreUtils.hpp" #include "core/connection/ConnectionIO.hpp" -#include "core/kernel/KernelInteractions.hpp" +#include "core/kernel/V2rayKernelInteractions.hpp" #define CheckIdExistance(type, id, val) \ if (!type.contains(id)) \ diff --git a/src/core/handler/V2rayInstanceHandler.cpp b/src/core/handler/KernelInstanceHandler.cpp similarity index 100% rename from src/core/handler/V2rayInstanceHandler.cpp rename to src/core/handler/KernelInstanceHandler.cpp diff --git a/src/core/kernel/PluginKernelInteractions.cpp b/src/core/kernel/PluginKernelInteractions.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/core/kernel/PluginKernelInteractions.hpp b/src/core/kernel/PluginKernelInteractions.hpp new file mode 100644 index 00000000..e69de29b diff --git a/src/core/kernel/KernelInteractions.cpp b/src/core/kernel/V2rayKernelInteractions.cpp similarity index 99% rename from src/core/kernel/KernelInteractions.cpp rename to src/core/kernel/V2rayKernelInteractions.cpp index c4b5f3a8..d7bc3b83 100644 --- a/src/core/kernel/KernelInteractions.cpp +++ b/src/core/kernel/V2rayKernelInteractions.cpp @@ -1,4 +1,4 @@ -#include "KernelInteractions.hpp" +#include "V2rayKernelInteractions.hpp" #include "APIBackend.hpp" #include "common/QvHelpers.hpp" diff --git a/src/core/kernel/KernelInteractions.hpp b/src/core/kernel/V2rayKernelInteractions.hpp similarity index 100% rename from src/core/kernel/KernelInteractions.hpp rename to src/core/kernel/V2rayKernelInteractions.hpp diff --git a/src/ui/w_ImportConfig.cpp b/src/ui/w_ImportConfig.cpp index 0190c470..e3814b38 100644 --- a/src/ui/w_ImportConfig.cpp +++ b/src/ui/w_ImportConfig.cpp @@ -5,7 +5,7 @@ #include "core/connection/ConnectionIO.hpp" #include "core/connection/Serialization.hpp" #include "core/handler/ConfigHandler.hpp" -#include "core/kernel/KernelInteractions.hpp" +#include "core/kernel/V2rayKernelInteractions.hpp" #include "ui/editors/w_JsonEditor.hpp" #include "ui/editors/w_OutboundEditor.hpp" #include "ui/editors/w_RoutesEditor.hpp" diff --git a/src/ui/w_PreferencesWindow.cpp b/src/ui/w_PreferencesWindow.cpp index ee7fafd2..af813b84 100644 --- a/src/ui/w_PreferencesWindow.cpp +++ b/src/ui/w_PreferencesWindow.cpp @@ -8,7 +8,7 @@ #include "components/plugins/toolbar/QvToolbar.hpp" #include "core/connection/ConnectionIO.hpp" #include "core/handler/ConfigHandler.hpp" -#include "core/kernel/KernelInteractions.hpp" +#include "core/kernel/V2rayKernelInteractions.hpp" #include "core/settings/SettingsBackend.hpp" #include "ui/widgets/RouteSettingsMatrix.hpp" From 0ec1bdc40cd7cefb8658d923cdd915d23aa87aeb Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Fri, 10 Apr 2020 23:32:03 +0800 Subject: [PATCH 58/92] refactor: refactor for KernelPlugin-V2rayRoutingEngine support --- CMakeLists.txt | 1 + makespec/BUILDVERSION | 2 +- src/base/models/QvSettingsObject.hpp | 3 +- src/core/connection/Serialization_ss.cpp | 1 - src/core/handler/ConfigHandler.cpp | 77 ++++++------ src/core/handler/ConfigHandler.hpp | 32 ++--- src/core/handler/KernelInstanceHandler.cpp | 124 +++++++++++-------- src/core/handler/KernelInstanceHandler.hpp | 47 +++++++ src/core/kernel/PluginKernelInteractions.cpp | 1 + src/core/kernel/PluginKernelInteractions.hpp | 6 + src/core/kernel/V2rayKernelInteractions.cpp | 31 +++-- src/core/kernel/V2rayKernelInteractions.hpp | 14 +-- src/ui/w_ImportConfig.cpp | 5 +- src/ui/w_MainWindow.cpp | 4 +- src/ui/w_PreferencesWindow.cpp | 15 ++- src/ui/w_PreferencesWindow.hpp | 2 + src/ui/w_PreferencesWindow.ui | 84 ++++++++++++- 17 files changed, 309 insertions(+), 140 deletions(-) create mode 100644 src/core/handler/KernelInstanceHandler.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 94c33be8..aa1b08c7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -266,6 +266,7 @@ set(QV2RAY_SOURCES src/core/CoreSafeTypes.hpp src/core/CoreUtils.hpp src/core/handler/ConfigHandler.hpp + src/core/handler/KernelInstanceHandler.hpp src/core/kernel/APIBackend.hpp src/core/kernel/V2rayKernelInteractions.hpp src/core/kernel/PluginKernelInteractions.hpp diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index c4c65307..c6b8d110 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5239 \ No newline at end of file +5242 \ No newline at end of file diff --git a/src/base/models/QvSettingsObject.hpp b/src/base/models/QvSettingsObject.hpp index 55cb4728..42521f81 100644 --- a/src/base/models/QvSettingsObject.hpp +++ b/src/base/models/QvSettingsObject.hpp @@ -131,7 +131,8 @@ namespace Qv2ray::base::config struct Qv2rayPluginConfig { QMap pluginStates; - XTOSTRUCT(O(pluginStates)) + bool v2rayIntegration; + XTOSTRUCT(O(pluginStates, v2rayIntegration)) }; struct Qv2rayConnectionConfig diff --git a/src/core/connection/Serialization_ss.cpp b/src/core/connection/Serialization_ss.cpp index 4f7c5e39..86259b59 100644 --- a/src/core/connection/Serialization_ss.cpp +++ b/src/core/connection/Serialization_ss.cpp @@ -2,7 +2,6 @@ #include "Serialization.hpp" #include "common/QvHelpers.hpp" #include "core/CoreUtils.hpp" -#include "core/handler/ConfigHandler.hpp" namespace Qv2ray::core::connection { diff --git a/src/core/handler/ConfigHandler.cpp b/src/core/handler/ConfigHandler.cpp index 42bd36da..b75573ec 100644 --- a/src/core/handler/ConfigHandler.cpp +++ b/src/core/handler/ConfigHandler.cpp @@ -76,15 +76,16 @@ namespace Qv2ray::core::handlers groups[DefaultGroupId].displayName = tr("Default Group"); groups[DefaultGroupId].isSubscription = false; // - vCoreInstance = new V2rayKernelInstance(); - connect(vCoreInstance, &V2rayKernelInstance::OnProcessErrored, this, &QvConfigHandler::OnVCoreCrashed); - connect(vCoreInstance, &V2rayKernelInstance::OnNewStatsDataArrived, this, &QvConfigHandler::OnStatsDataArrived); - // Directly connected to a signal. - connect(vCoreInstance, &V2rayKernelInstance::OnProcessOutputReadyRead, this, &QvConfigHandler::OnVCoreLogAvailable); + kernelHandler = new KernelInstanceHandler(this); + connect(kernelHandler, &KernelInstanceHandler::OnCrashed, this, &QvConfigHandler::OnKernelCrashed_p); + connect(kernelHandler, &KernelInstanceHandler::OnStatsDataAvailable, this, &QvConfigHandler::OnStatsDataArrived_p); + connect(kernelHandler, &KernelInstanceHandler::OnKernelLogAvailable, this, &QvConfigHandler::OnKernelLogAvailable); + connect(kernelHandler, &KernelInstanceHandler::OnConnected, this, &QvConfigHandler::OnConnected); + connect(kernelHandler, &KernelInstanceHandler::OnDisconnected, this, &QvConfigHandler::OnDisconnected); // tcpingHelper = new QvTCPingHelper(5, this); httpHelper = new QvHttpRequestHelper(this); - connect(tcpingHelper, &QvTCPingHelper::OnLatencyTestCompleted, this, &QvConfigHandler::OnLatencyDataArrived); + connect(tcpingHelper, &QvTCPingHelper::OnLatencyTestCompleted, this, &QvConfigHandler::OnLatencyDataArrived_p); // // Save per 2 minutes. saveTimerId = startTimer(2 * 60 * 1000); @@ -139,9 +140,10 @@ namespace Qv2ray::core::handlers } else if (event->timerId() == pingConnectionTimerId) { - if (currentConnectionId != NullConnectionId) + auto id = kernelHandler->CurrentConnection(); + if (id != NullConnectionId) { - StartLatencyTest(currentConnectionId); + StartLatencyTest(id); } } } @@ -215,7 +217,7 @@ namespace Qv2ray::core::handlers connections[id].upLinkData = 0; connections[id].downLinkData = 0; emit OnStatsAvailable(id, 0, 0, 0, 0); - PluginHost->Send_ConnectionStatsEvent({ GetDisplayName(currentConnectionId), 0, 0, 0, 0 }); + PluginHost->Send_ConnectionStatsEvent({ GetDisplayName(id), 0, 0, 0, 0 }); return {}; } @@ -329,55 +331,41 @@ namespace Qv2ray::core::handlers const optional QvConfigHandler::StartConnection(const ConnectionId &id) { CheckConnectionExistance(id); - - if (currentConnectionId != NullConnectionId) - { - StopConnection(); - } - + connections[id].lastConnected = system_clock::to_time_t(system_clock::now()); CONFIGROOT root = GetConnectionRoot(id); - return CHStartConnection_p(id, root); + return kernelHandler->StartConnection(id, root); } void QvConfigHandler::RestartConnection() // const ConnectionId &id { - auto conn = currentConnectionId; - if (conn != NullConnectionId) - { - StopConnection(); - StartConnection(conn); - } + kernelHandler->RestartConnection(); } void QvConfigHandler::StopConnection() // const ConnectionId &id { - // Currently just simply stop it. - //_UNUSED(id) - // if (currentConnectionId == id) { - //} - CHStopConnection_p(); + kernelHandler->StopConnection(); CHSaveConfigData_p(); } bool QvConfigHandler::IsConnected(const ConnectionId &id) const { - CheckConnectionExistanceEx(id, false); - return currentConnectionId == id; + return kernelHandler->isConnected(id); + } + + void QvConfigHandler::OnKernelCrashed_p(const ConnectionId &id) + { + LOG(MODULE_CORE_HANDLER, "V2ray core crashed!") + emit OnDisconnected(id); + PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), {}, QvConnecticity_Disconnected }); + emit OnKernelCrashed(id); } QvConfigHandler::~QvConfigHandler() { LOG(MODULE_CORE_HANDLER, "Triggering save settings from destructor") - CHSaveConfigData_p(); - - if (vCoreInstance->KernelStarted) - { - vCoreInstance->StopConnection(); - LOG(MODULE_CORE_HANDLER, "Stopped connection from destructor.") - } - - delete vCoreInstance; + delete kernelHandler; delete httpHelper; + CHSaveConfigData_p(); } const CONFIGROOT QvConfigHandler::GetConnectionRoot(const ConnectionId &id) const @@ -386,7 +374,7 @@ namespace Qv2ray::core::handlers return connectionRootCache.value(id); } - void QvConfigHandler::OnLatencyDataArrived(const QvTCPingResultObject &result) + void QvConfigHandler::OnLatencyDataArrived_p(const QvTCPingResultObject &result) { CheckConnectionExistanceEx(result.connectionId, nothing); connections[result.connectionId].latency = result.avg; @@ -408,7 +396,7 @@ namespace Qv2ray::core::handlers // emit OnConnectionModified(id); PluginHost->Send_ConnectionEvent({ connections[id].displayName, "", ConnectionEvent_Updated }); - if (!skipRestart && id == currentConnectionId) + if (!skipRestart && kernelHandler->isConnected(id)) { emit RestartConnection(); } @@ -594,6 +582,15 @@ namespace Qv2ray::core::handlers return hasErrorOccured; } + void QvConfigHandler::OnStatsDataArrived_p(const ConnectionId &id, const quint64 uploadSpeed, const quint64 downloadSpeed) + { + connections[id].upLinkData += uploadSpeed; + connections[id].downLinkData += downloadSpeed; + emit OnStatsAvailable(id, uploadSpeed, downloadSpeed, connections[id].upLinkData, connections[id].downLinkData); + PluginHost->Send_ConnectionStatsEvent( + { GetDisplayName(id), uploadSpeed, downloadSpeed, connections[id].upLinkData, connections[id].downLinkData }); + } + const ConnectionId QvConfigHandler::CreateConnection(const QString &displayName, const GroupId &groupId, const CONFIGROOT &root) { LOG(MODULE_CORE_HANDLER, "Creating new connection: " + displayName) diff --git a/src/core/handler/ConfigHandler.hpp b/src/core/handler/ConfigHandler.hpp index 75c4275c..9da0342c 100644 --- a/src/core/handler/ConfigHandler.hpp +++ b/src/core/handler/ConfigHandler.hpp @@ -6,7 +6,7 @@ #include "core/CoreSafeTypes.hpp" #include "core/CoreUtils.hpp" #include "core/connection/ConnectionIO.hpp" -#include "core/kernel/V2rayKernelInteractions.hpp" +#include "core/handler/KernelInstanceHandler.hpp" #define CheckIdExistance(type, id, val) \ if (!type.contains(id)) \ @@ -21,7 +21,6 @@ #define CheckConnectionExistance(id) CheckConnectionExistanceEx(id, tr("Connection does not exist")) namespace Qv2ray::core::handlers { - // class QvConfigHandler : public QObject { Q_OBJECT @@ -33,7 +32,7 @@ namespace Qv2ray::core::handlers // inline const ConnectionId CurrentConnection() const { - return currentConnectionId; + return kernelHandler->CurrentConnection(); } inline const QList Connections() const { @@ -107,10 +106,7 @@ namespace Qv2ray::core::handlers const tuple GetSubscriptionData(const GroupId &id) const; signals: - void OnCrashed(); - void OnConnected(const ConnectionId &id); - void OnDisconnected(const ConnectionId &id); - void OnVCoreLogAvailable(const ConnectionId &id, const QString &log); + void OnKernelLogAvailable(const ConnectionId &id, const QString &log); void OnStatsAvailable(const ConnectionId &id, const quint64 upS, const quint64 downS, const quint64 upD, const quint64 downD); // void OnConnectionCreated(const ConnectionId &id, const QString &displayName); @@ -127,20 +123,20 @@ namespace Qv2ray::core::handlers void OnGroupDeleted(const GroupId &id, const QList &connections); // void OnSubscriptionUpdateFinished(const GroupId &id); - + void OnConnected(const ConnectionId &id); + void OnDisconnected(const ConnectionId &id); + void OnKernelCrashed(const ConnectionId &id); + // private slots: - void OnStatsDataArrived(const ConnectionId &id, const quint64 uploadSpeed, const quint64 downloadSpeed); - void OnVCoreCrashed(const ConnectionId &id); - void OnLatencyDataArrived(const QvTCPingResultObject &data); + void OnKernelCrashed_p(const ConnectionId &id); + void OnLatencyDataArrived_p(const QvTCPingResultObject &data); + void OnStatsDataArrived_p(const ConnectionId &id, const quint64 uploadSpeed, const quint64 downloadSpeed); protected: void timerEvent(QTimerEvent *event) override; private: void CHSaveConfigData_p(); - // - optional CHStartConnection_p(const ConnectionId &id, const CONFIGROOT &root); - void CHStopConnection_p(); bool CHUpdateSubscription_p(const GroupId &id, const QByteArray &subscriptionData); private: @@ -155,13 +151,7 @@ namespace Qv2ray::core::handlers QvHttpRequestHelper *httpHelper; bool isHttpRequestInProgress = false; QvTCPingHelper *tcpingHelper; - // We only support one cuncurrent connection currently. -#ifdef QV2RAY_MULTIPlE_ONNECTION - QHash kernelInstances; -#else - ConnectionId currentConnectionId = NullConnectionId; - V2rayKernelInstance *vCoreInstance = nullptr; -#endif + KernelInstanceHandler *kernelHandler; }; inline ::Qv2ray::core::handlers::QvConfigHandler *ConnectionManager = nullptr; diff --git a/src/core/handler/KernelInstanceHandler.cpp b/src/core/handler/KernelInstanceHandler.cpp index 733fec80..57d3e29e 100644 --- a/src/core/handler/KernelInstanceHandler.cpp +++ b/src/core/handler/KernelInstanceHandler.cpp @@ -1,62 +1,88 @@ -#include "ConfigHandler.hpp" +#include "KernelInstanceHandler.hpp" + #include "components/plugins/QvPluginHost.hpp" +#include "core/CoreUtils.hpp" #include "core/connection/Generation.hpp" - -optional QvConfigHandler::CHStartConnection_p(const ConnectionId &id, const CONFIGROOT &root) +namespace Qv2ray::core::handlers { - connections[id].lastConnected = system_clock::to_time_t(system_clock::now()); - // - auto fullConfig = GenerateRuntimeConfig(root); - auto inboundPorts = GetInboundPorts(fullConfig); - // - PluginHost->Send_ConnectivityEvent(QvConnectivityEventObject{ GetDisplayName(id), inboundPorts, QvConnecticity_Connecting }); - auto result = vCoreInstance->StartConnection(id, fullConfig); - - if (!result.has_value()) + KernelInstanceHandler::KernelInstanceHandler(QObject *parent) : QObject(parent) { - currentConnectionId = id; - emit OnConnected(currentConnectionId); - PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), inboundPorts, QvConnecticity_Connected }); + vCoreInstance = new V2rayKernelInstance(this); + connect(vCoreInstance, &V2rayKernelInstance::OnNewStatsDataArrived, this, &KernelInstanceHandler::OnStatsDataArrived_p); + connect(vCoreInstance, &V2rayKernelInstance::OnProcessOutputReadyRead, this, &KernelInstanceHandler::OnKernelLogAvailable_p); + connect(vCoreInstance, &V2rayKernelInstance::OnProcessErrored, this, &KernelInstanceHandler::OnKernelCrashed_p); } - else - { - PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), {}, QvConnecticity_Disconnected }); - } - return result; -} -void QvConfigHandler::CHStopConnection_p() -{ - if (vCoreInstance->KernelStarted) + KernelInstanceHandler::~KernelInstanceHandler() { - PluginHost->Send_ConnectivityEvent({ GetDisplayName(currentConnectionId), {}, QvConnecticity_Disconnecting }); - vCoreInstance->StopConnection(); - // Copy - ConnectionId id = currentConnectionId; + } + + std::optional KernelInstanceHandler::StartConnection(const ConnectionId &id, const CONFIGROOT &root) + { + if (vCoreInstance->KernelStarted) + { + StopConnection(); + } + this->root = root; + auto fullConfig = GenerateRuntimeConfig(root); + auto inboundPorts = GetInboundPorts(fullConfig); + // + PluginHost->Send_ConnectivityEvent(QvConnectivityEventObject{ GetDisplayName(id), inboundPorts, QvConnecticity_Connecting }); + auto result = vCoreInstance->StartConnection(fullConfig); + + if (!result.has_value()) + { + currentConnectionId = id; + lastConnectionId = id; + emit OnConnected(currentConnectionId); + PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), inboundPorts, QvConnecticity_Connected }); + } + else + { + PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), {}, QvConnecticity_Disconnected }); + } + return result; + } + + void KernelInstanceHandler::RestartConnection() + { + StopConnection(); + StartConnection(lastConnectionId, root); + } + + void KernelInstanceHandler::OnKernelCrashed_p() + { + emit OnCrashed(currentConnectionId); + emit OnDisconnected(currentConnectionId); + lastConnectionId = currentConnectionId; currentConnectionId = NullConnectionId; - emit OnDisconnected(id); - PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), {}, QvConnecticity_Disconnected }); } - else + + void KernelInstanceHandler::OnKernelLogAvailable_p(const QString &log) { - LOG(MODULE_CORE_HANDLER, "VCore is not started, not disconnecting") + emit OnKernelLogAvailable(currentConnectionId, log); } -} -void QvConfigHandler::OnStatsDataArrived(const ConnectionId &id, const quint64 uploadSpeed, const quint64 downloadSpeed) -{ - connections[id].upLinkData += uploadSpeed; - connections[id].downLinkData += downloadSpeed; - emit OnStatsAvailable(id, uploadSpeed, downloadSpeed, connections[id].upLinkData, connections[id].downLinkData); - PluginHost->Send_ConnectionStatsEvent({ GetDisplayName(currentConnectionId), // - uploadSpeed, downloadSpeed, connections[id].upLinkData, connections[id].downLinkData }); -} + void KernelInstanceHandler::StopConnection() + { + if (vCoreInstance->KernelStarted) + { + PluginHost->Send_ConnectivityEvent({ GetDisplayName(currentConnectionId), {}, QvConnecticity_Disconnecting }); + vCoreInstance->StopConnection(); + // Copy + ConnectionId id = currentConnectionId; + currentConnectionId = NullConnectionId; + emit OnDisconnected(id); + PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), {}, QvConnecticity_Disconnected }); + } + else + { + LOG(MODULE_CORE_HANDLER, "VCore is not started, not disconnecting") + } + } -void QvConfigHandler::OnVCoreCrashed(const ConnectionId &id) -{ - LOG(MODULE_CORE_HANDLER, "V2ray core crashed!") - currentConnectionId = NullConnectionId; - emit OnDisconnected(id); - PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), {}, QvConnecticity_Disconnected }); - emit OnCrashed(); -} + void KernelInstanceHandler::OnStatsDataArrived_p(const quint64 uploadSpeed, const quint64 downloadSpeed) + { + emit OnStatsDataAvailable(currentConnectionId, uploadSpeed, downloadSpeed); + } +} // namespace Qv2ray::core::handlers diff --git a/src/core/handler/KernelInstanceHandler.hpp b/src/core/handler/KernelInstanceHandler.hpp new file mode 100644 index 00000000..f2dceae1 --- /dev/null +++ b/src/core/handler/KernelInstanceHandler.hpp @@ -0,0 +1,47 @@ +#pragma once +#include "core/CoreSafeTypes.hpp" +#include "core/kernel/V2rayKernelInteractions.hpp" + +#include +#include + +namespace Qv2ray::core::handlers +{ + class KernelInstanceHandler : public QObject + { + Q_OBJECT + public: + explicit KernelInstanceHandler(QObject *parent = nullptr); + ~KernelInstanceHandler(); + + std::optional StartConnection(const ConnectionId &id, const CONFIGROOT &root); + void RestartConnection(); + void StopConnection(); + const ConnectionId CurrentConnection() const + { + return currentConnectionId; + } + bool isConnected(const ConnectionId &id) const + { + return id == currentConnectionId; + } + + signals: + void OnConnected(const ConnectionId &id); + void OnDisconnected(const ConnectionId &id); + void OnCrashed(const ConnectionId &id); + void OnStatsDataAvailable(const ConnectionId &id, const quint64 uploadSpeed, const quint64 downloadSpeed); + void OnKernelLogAvailable(const ConnectionId &id, const QString &log); + + private slots: + void OnKernelCrashed_p(); + void OnKernelLogAvailable_p(const QString &log); + void OnStatsDataArrived_p(const quint64 uploadSpeed, const quint64 downloadSpeed); + + private: + CONFIGROOT root; + V2rayKernelInstance *vCoreInstance = nullptr; + ConnectionId currentConnectionId = NullConnectionId; + ConnectionId lastConnectionId = NullConnectionId; + }; +} // namespace Qv2ray::core::handlers diff --git a/src/core/kernel/PluginKernelInteractions.cpp b/src/core/kernel/PluginKernelInteractions.cpp index e69de29b..8b520237 100644 --- a/src/core/kernel/PluginKernelInteractions.cpp +++ b/src/core/kernel/PluginKernelInteractions.cpp @@ -0,0 +1 @@ +#include "PluginKernelInteractions.hpp" diff --git a/src/core/kernel/PluginKernelInteractions.hpp b/src/core/kernel/PluginKernelInteractions.hpp index e69de29b..2ccd2667 100644 --- a/src/core/kernel/PluginKernelInteractions.hpp +++ b/src/core/kernel/PluginKernelInteractions.hpp @@ -0,0 +1,6 @@ +#pragma once +#include "components/plugins/QvPluginHost.hpp" +namespace Qv2ray::core::kernel +{ + +} diff --git a/src/core/kernel/V2rayKernelInteractions.cpp b/src/core/kernel/V2rayKernelInteractions.cpp index d7bc3b83..72242e7c 100644 --- a/src/core/kernel/V2rayKernelInteractions.cpp +++ b/src/core/kernel/V2rayKernelInteractions.cpp @@ -49,14 +49,24 @@ namespace Qv2ray::core::kernel switch (kernel::abi::checkCompatibility(compiledABI, ACCESS_OPTIONAL_VALUE(abi))) { case kernel::abi::ABI_NOPE: + { LOG(MODULE_VCORE, "Host is incompatible with core") *message = tr("V2Ray core is incompatible with your platform.\r\n" // "Expected core ABI is %1, but got actual %2.\r\n" // "Maybe you have downloaded the wrong core?") .arg(kernel::abi::abiToString(compiledABI), kernel::abi::abiToString(ACCESS_OPTIONAL_VALUE(abi))); return false; - case kernel::abi::ABI_MAYBE: LOG(MODULE_VCORE, "WARNING: Host maybe incompatible with core"); [[fallthrough]]; - case kernel::abi::ABI_PERFECT: LOG(MODULE_VCORE, "Host is compatible with core"); + } + case kernel::abi::ABI_MAYBE: + { + LOG(MODULE_VCORE, "WARNING: Host maybe incompatible with core"); + break; + } + case kernel::abi::ABI_PERFECT: + { + LOG(MODULE_VCORE, "Host is compatible with core"); + break; + } } // @@ -155,17 +165,18 @@ namespace Qv2ray::core::kernel } else { - QvMessageBoxWarn(nullptr, tr("Cannot start V2ray"), - tr("V2ray core settings is incorrect.") + NEWLINE + NEWLINE + tr("The error is: ") + NEWLINE + v2rayCheckResult); + QvMessageBoxWarn(nullptr, tr("Cannot start V2ray"), // + tr("V2ray core settings is incorrect.") + NEWLINE + NEWLINE + // + tr("The error is: ") + NEWLINE + v2rayCheckResult); return false; } } - V2rayKernelInstance::V2rayKernelInstance() + V2rayKernelInstance::V2rayKernelInstance(QObject *parent) : QObject(parent) { vProcess = new QProcess(); connect(vProcess, &QProcess::readyReadStandardOutput, this, - [&]() { emit OnProcessOutputReadyRead(id, vProcess->readAllStandardOutput().trimmed()); }); + [&]() { emit OnProcessOutputReadyRead(vProcess->readAllStandardOutput().trimmed()); }); connect(vProcess, &QProcess::stateChanged, [&](QProcess::ProcessState state) { DEBUG(MODULE_VCORE, "V2ray kernel process status changed: " + QVariant::fromValue(state).toString()) @@ -174,7 +185,7 @@ namespace Qv2ray::core::kernel { LOG(MODULE_VCORE, "V2ray kernel crashed.") StopConnection(); - emit OnProcessErrored(id); + emit OnProcessErrored(); } }); apiWorker = new APIWorker(); @@ -182,7 +193,7 @@ namespace Qv2ray::core::kernel KernelStarted = false; } - optional V2rayKernelInstance::StartConnection(const ConnectionId &id, const CONFIGROOT &root) + optional V2rayKernelInstance::StartConnection(const CONFIGROOT &root) { if (KernelStarted) { @@ -205,8 +216,6 @@ namespace Qv2ray::core::kernel vProcess->waitForStarted(); DEBUG(MODULE_VCORE, "V2ray core started.") KernelStarted = true; - // Set Connection ID - this->id = id; QStringList inboundTags; for (auto item : root["inbounds"].toArray()) @@ -284,6 +293,6 @@ namespace Qv2ray::core::kernel void V2rayKernelInstance::onAPIDataReady(const quint64 speedUp, const quint64 speedDown) { - emit OnNewStatsDataArrived(id, speedUp, speedDown); + emit OnNewStatsDataArrived(speedUp, speedDown); } } // namespace Qv2ray::core::kernel diff --git a/src/core/kernel/V2rayKernelInteractions.hpp b/src/core/kernel/V2rayKernelInteractions.hpp index 825c15f0..dc97fe17 100644 --- a/src/core/kernel/V2rayKernelInteractions.hpp +++ b/src/core/kernel/V2rayKernelInteractions.hpp @@ -12,7 +12,7 @@ namespace Qv2ray::core::kernel { Q_OBJECT public: - explicit V2rayKernelInstance(); + explicit V2rayKernelInstance(QObject *parent = nullptr); ~V2rayKernelInstance() override; // // Speed @@ -21,7 +21,7 @@ namespace Qv2ray::core::kernel qulonglong getAllSpeedUp(); qulonglong getAllSpeedDown(); // - optional StartConnection(const ConnectionId &id, const CONFIGROOT &root); + optional StartConnection(const CONFIGROOT &root); void StopConnection(); bool KernelStarted = false; // @@ -29,19 +29,17 @@ namespace Qv2ray::core::kernel static bool ValidateKernel(const QString &vCorePath, const QString &vAssetsPath, QString *message); signals: - void OnProcessErrored(const ConnectionId &id); - void OnProcessOutputReadyRead(const ConnectionId &id, const QString &output); - void OnNewStatsDataArrived(const ConnectionId &id, const quint64 speedUp, const quint64 speedDown); + void OnProcessErrored(); + void OnProcessOutputReadyRead(const QString &output); + void OnNewStatsDataArrived(const quint64 speedUp, const quint64 speedDown); - public slots: + private slots: void onAPIDataReady(const quint64 speedUp, const quint64 speedDown); private: APIWorker *apiWorker; QProcess *vProcess; bool apiEnabled; - // - ConnectionId id = NullConnectionId; }; } // namespace Qv2ray::core::kernel diff --git a/src/ui/w_ImportConfig.cpp b/src/ui/w_ImportConfig.cpp index e3814b38..30a191ec 100644 --- a/src/ui/w_ImportConfig.cpp +++ b/src/ui/w_ImportConfig.cpp @@ -37,7 +37,10 @@ QvMessageBusSlotImpl(ImportConfigWindow) { switch (msg) { - MBShowDefaultImpl MBHideDefaultImpl MBRetranslateDefaultImpl MBUpdateColorSchemeDefaultImpl + MBShowDefaultImpl; + MBHideDefaultImpl; + MBRetranslateDefaultImpl; + MBUpdateColorSchemeDefaultImpl; } } diff --git a/src/ui/w_MainWindow.cpp b/src/ui/w_MainWindow.cpp index dc02c409..e1fe0da7 100644 --- a/src/ui/w_MainWindow.cpp +++ b/src/ui/w_MainWindow.cpp @@ -132,7 +132,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) UpdateColorScheme(); // // - connect(ConnectionManager, &QvConfigHandler::OnCrashed, [&] { + connect(ConnectionManager, &QvConfigHandler::OnKernelCrashed, [&] { this->show(); QvMessageBoxWarn(this, tr("V2ray vcore terminated."), tr("V2ray vcore terminated unexpectedly.") + NEWLINE + NEWLINE + @@ -142,7 +142,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) connect(ConnectionManager, &QvConfigHandler::OnConnected, this, &MainWindow::OnConnected); connect(ConnectionManager, &QvConfigHandler::OnDisconnected, this, &MainWindow::OnDisconnected); connect(ConnectionManager, &QvConfigHandler::OnStatsAvailable, this, &MainWindow::OnStatsAvailable); - connect(ConnectionManager, &QvConfigHandler::OnVCoreLogAvailable, this, &MainWindow::OnVCoreLogAvailable); + connect(ConnectionManager, &QvConfigHandler::OnKernelLogAvailable, this, &MainWindow::OnVCoreLogAvailable); // connect(ConnectionManager, &QvConfigHandler::OnConnectionDeleted, this, &MainWindow::OnConnectionDeleted); connect(ConnectionManager, &QvConfigHandler::OnConnectionCreated, this, &MainWindow::OnConnectionCreated); diff --git a/src/ui/w_PreferencesWindow.cpp b/src/ui/w_PreferencesWindow.cpp index af813b84..fbc4bd5f 100644 --- a/src/ui/w_PreferencesWindow.cpp +++ b/src/ui/w_PreferencesWindow.cpp @@ -128,12 +128,13 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent), Current // localDNSCb->setChecked(CurrentConfig.connectionConfig.withLocalDNS); // + pluginKernelV2rayIntegrationCB->setChecked(CurrentConfig.pluginConfig.v2rayIntegration); + // + // DNSListTxt->clear(); - for (auto dnsStr : CurrentConfig.connectionConfig.dnsList) { auto str = dnsStr.trimmed(); - if (!str.isEmpty()) { DNSListTxt->appendPlainText(str); @@ -144,11 +145,11 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent), Current updateSettingsGroupBox->setEnabled(false); updateSettingsGroupBox->setToolTip(tr("Update is disabled by your vendor.")); #endif - + // updateChannelCombo->setCurrentIndex(CurrentConfig.updateConfig.updateChannel); cancelIgnoreVersionBtn->setEnabled(!CurrentConfig.updateConfig.ignoredVersion.isEmpty()); ignoredNextVersion->setText(CurrentConfig.updateConfig.ignoredVersion); - + // for (auto i = 0; i < CurrentConfig.toolBarConfig.Pages.size(); i++) { nsBarPagesList->addItem(tr("Page") + QSTRN(i + 1) + ": " + QSTRN(CurrentConfig.toolBarConfig.Pages[i].Lines.size()) + " " + @@ -1099,3 +1100,9 @@ void PreferencesWindow::on_updateChannelCombo_currentIndexChanged(int index) CurrentConfig.updateConfig.updateChannel = index; CurrentConfig.updateConfig.ignoredVersion.clear(); } + +void PreferencesWindow::on_pluginKernelV2rayIntegrationCB_stateChanged(int arg1) +{ + LOADINGCHECK + CurrentConfig.pluginConfig.v2rayIntegration = arg1 == Qt::Checked; +} diff --git a/src/ui/w_PreferencesWindow.hpp b/src/ui/w_PreferencesWindow.hpp index 73e6ec68..622810b4 100644 --- a/src/ui/w_PreferencesWindow.hpp +++ b/src/ui/w_PreferencesWindow.hpp @@ -156,6 +156,8 @@ class PreferencesWindow void on_updateChannelCombo_currentIndexChanged(int index); + void on_pluginKernelV2rayIntegrationCB_stateChanged(int arg1); + private: // RouteSettingsMatrixWidget *routeSettingsWidget; diff --git a/src/ui/w_PreferencesWindow.ui b/src/ui/w_PreferencesWindow.ui index 73a8fa40..15008b0b 100644 --- a/src/ui/w_PreferencesWindow.ui +++ b/src/ui/w_PreferencesWindow.ui @@ -490,7 +490,7 @@ 0 0 - 964 + 505 603 @@ -1100,6 +1100,88 @@ + + + Plugin Settings + + + + + + QFrame::NoFrame + + + true + + + + + 0 + 0 + 964 + 506 + + + + + + + Kernel Plugin Behavior + + + + + + Enabling V2ray Integration will allow the kernel benifit from the V2ray routing engine. +Otherwise, these features will be disabled: + +Advanced Route Settings. +ByPass CN +Forcing Direct of Local LAN Address +Custom DNS Settings + + + + + + + + + V2ray Integration + + + + + + + Enabled + + + + + + + + + Qt::Vertical + + + + 20 + 299 + + + + + + + + + + + + + true From 99998e26f9e8a7f3e60334405bbcea5e3450bd77 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Sat, 11 Apr 2020 13:00:13 +0800 Subject: [PATCH 59/92] Support macOS 10.13 --- .github/workflows/build-qv2ray-cmake.yml | 4 ++-- src/ui/widgets/RouteSettingsMatrix.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-qv2ray-cmake.yml b/.github/workflows/build-qv2ray-cmake.yml index f2bd6fe4..9b51bf09 100644 --- a/.github/workflows/build-qv2ray-cmake.yml +++ b/.github/workflows/build-qv2ray-cmake.yml @@ -102,7 +102,7 @@ jobs: run: | mkdir build cd build - cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_DEPLOYMENT_TARGET=10.14 + cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_DEPLOYMENT_TARGET=10.13 sudo cmake --build . --target package --parallel $(sysctl -n hw.logicalcpu) cp qv2ray-*.dmg ../ - name: macOS - Get package name @@ -174,7 +174,7 @@ jobs: if: matrix.platform == 'macos-latest' uses: actions/upload-artifact@master with: - name: ${{ steps.get_package.outputs.NAME }} + name: Qv2ray-${{ github.sha }}.macOS-${{ matrix.arch }}.qt${{ matrix.qt_version }}.dmg path: ${{ steps.get_package.outputs.NAME }} - name: macOS - ${{ matrix.qt_version }} - Upload binaries to release uses: svenstaro/upload-release-action@v1-release diff --git a/src/ui/widgets/RouteSettingsMatrix.cpp b/src/ui/widgets/RouteSettingsMatrix.cpp index 0869b2b6..1a6ff3d7 100644 --- a/src/ui/widgets/RouteSettingsMatrix.cpp +++ b/src/ui/widgets/RouteSettingsMatrix.cpp @@ -103,7 +103,7 @@ void RouteSettingsMatrixWidget::on_importSchemeBtn_clicked() // read the file and parse back to struct. // if error occurred on parsing, an exception will be thrown. - auto content = StringFromFile(filePath.value()); + auto content = StringFromFile(ACCESS_OPTIONAL_VALUE(filePath)); auto scheme = StructFromJsonString(content); // show the information of this scheme to user, @@ -175,7 +175,7 @@ void RouteSettingsMatrixWidget::on_exportSchemeBtn_clicked() // serialize and write out auto content = StructToJsonString(scheme); - StringToFile(content, savePath.value()); + StringToFile(content, ACCESS_OPTIONAL_VALUE(savePath)); // done // TODO: Give some success as Notification From f7b1a1fdb5387b61d2679fa9914bb639863ca9ed Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Sat, 11 Apr 2020 14:50:16 +0800 Subject: [PATCH 60/92] add: added kernel plugin support --- makespec/BUILDVERSION | 2 +- src/components/plugins/QvPluginHost.cpp | 17 +++ src/components/plugins/QvPluginHost.hpp | 1 + src/components/plugins/interface | 2 +- src/core/handler/KernelInstanceHandler.cpp | 132 ++++++++++++++++++++- src/core/handler/KernelInstanceHandler.hpp | 2 + 6 files changed, 150 insertions(+), 6 deletions(-) diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index c6b8d110..006ac3e1 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5242 \ No newline at end of file +5243 \ No newline at end of file diff --git a/src/components/plugins/QvPluginHost.cpp b/src/components/plugins/QvPluginHost.cpp index 366eda21..7a12f3d1 100644 --- a/src/components/plugins/QvPluginHost.cpp +++ b/src/components/plugins/QvPluginHost.cpp @@ -284,6 +284,23 @@ namespace Qv2ray::components::plugins return ""; } + const QMap> QvPluginHost::GetPluginKernels() const + { + QMap> kernels; + for (const auto &plugin : plugins) + { + if (plugin.isLoaded && plugin.metadata.SpecialPluginType.contains(SPECIAL_TYPE_KERNEL)) + { + auto kern = plugin.pluginInterface->GetKernel(); + for (const auto &cap : kern->KernelOutboundCapabilities()) + { + kernels.insert(cap.protocol, kern); + } + } + } + return kernels; + } + const QString GetPluginTypeString(const SPECIAL_TYPE_FLAGS &types) { QStringList typesList; diff --git a/src/components/plugins/QvPluginHost.hpp b/src/components/plugins/QvPluginHost.hpp index 3e05bf9c..dbef9791 100644 --- a/src/components/plugins/QvPluginHost.hpp +++ b/src/components/plugins/QvPluginHost.hpp @@ -58,6 +58,7 @@ namespace Qv2ray::components::plugins { return plugins.value(internalName).metadata; } + const QMap> GetPluginKernels() const; // const QMultiHash> TryDeserializeShareLink(const QString &sharelink, // QString *prefix, // diff --git a/src/components/plugins/interface b/src/components/plugins/interface index 71dab933..c7c435ba 160000 --- a/src/components/plugins/interface +++ b/src/components/plugins/interface @@ -1 +1 @@ -Subproject commit 71dab9339510d7e766bff00c89159f0407cff910 +Subproject commit c7c435ba060c753900ae279cc8156814c64478b6 diff --git a/src/core/handler/KernelInstanceHandler.cpp b/src/core/handler/KernelInstanceHandler.cpp index 57d3e29e..758f8162 100644 --- a/src/core/handler/KernelInstanceHandler.cpp +++ b/src/core/handler/KernelInstanceHandler.cpp @@ -1,6 +1,5 @@ #include "KernelInstanceHandler.hpp" -#include "components/plugins/QvPluginHost.hpp" #include "core/CoreUtils.hpp" #include "core/connection/Generation.hpp" namespace Qv2ray::core::handlers @@ -23,17 +22,136 @@ namespace Qv2ray::core::handlers { StopConnection(); } + pluginKernels.clear(); this->root = root; + bool isComplex = IsComplexConfig(root); auto fullConfig = GenerateRuntimeConfig(root); - auto inboundPorts = GetInboundPorts(fullConfig); // + if (!isComplex) + { + QList> inboundInfo; + for (const auto &inbound_v : fullConfig["inbounds"].toArray()) + { + const auto &inbound = inbound_v.toObject(); + inboundInfo.push_back({ inbound["protocol"].toString(), inbound["port"].toInt(), inbound["tag"].toString() }); + } + // + auto kernels = PluginHost->GetPluginKernels(); + + auto pluginPort = 18900; + OUTBOUNDS new_outbounds; + auto rules = fullConfig["routing"].toObject()["rules"].toArray(); + QJsonArray newRules; + QJsonArray newRulesAppend; + + for (const auto &outbound_v : fullConfig["outbounds"].toArray()) + { + const auto &outbound = outbound_v.toObject(); + const auto &outProtocol = outbound["protocol"].toString(); + const auto &outTag = outbound["tag"].toString(); + // + if (!kernels.contains(outProtocol)) + { + new_outbounds.push_back(outbound); + continue; + } + LOG(MODULE_CONNECTION, "Get kernel plugin: " + outProtocol) + auto kernel = kernels[outProtocol].get(); + connect(kernel, &QvPluginKernel::OnKernelCrashed, this, &KernelInstanceHandler::OnKernelCrashed_p); + // connect(kernel, &QvPluginKernel::OnKernelStatsAvailable, this, &KernelInstanceHandler::OnStatsDataArrived_p); + connect(kernel, &QvPluginKernel::OnKernelLogAvaliable, this, &KernelInstanceHandler::OnKernelLogAvailable_p); + pluginKernels.insert(outProtocol, kernel); + // + auto pluginOutboundSettings = outbound["settings"].toObject(); + // + QMap pluginInboundPort; + for (const auto &[inProtocol, inPort, inTag] : inboundInfo) + { + if (!QStringList{ "http", "socks" }.contains(inProtocol)) + continue; + // + pluginInboundPort.insert(inProtocol, pluginPort); + LOG(MODULE_VCORE, "Plugin Integration: " + QSTRN(pluginPort) + " = " + inProtocol + "(" + inTag + ") --> " + outProtocol) + const auto &freedomTag = "plugin_" + inTag + "_" + inProtocol + ":" + QSTRN(inPort) + "_" + QSTRN(pluginPort); + const auto &pluginOutSettings = GenerateHTTPSOCKSOut("127.0.0.1", pluginPort, false, "", ""); + const auto &direct = GenerateOutboundEntry(inProtocol, pluginOutSettings, {}, {}, "0.0.0.0", freedomTag); + // + // + // + // Add the integration outbound to the list. + new_outbounds.push_back(direct); + // + bool hasRuleProcessed = false; + auto copyRules = rules; + for (auto i = 0; i < rules.count(); i++) + { + const auto &rule = rules.at(i).toObject(); + if (rule["outboundTag"].toString() == outTag) + { + auto newRule = rule; + newRule["outboundTag"] = freedomTag; + newRule["inboundTag"] = QJsonArray{ inTag }; + newRulesAppend.push_back(newRule); + hasRuleProcessed = true; + } + else + { + auto val = copyRules.takeAt(i); + if (!(val.isNull() || val.isUndefined())) + { + newRules.push_back(val); + } + } + } + rules = copyRules; + if (!hasRuleProcessed) + { + QJsonObject integrationRule; + integrationRule["outboundTag"] = freedomTag; + integrationRule["inboundTag"] = QJsonArray{ inTag }; + integrationRule["type"] = "field"; + newRulesAppend.push_back(integrationRule); + } + // + pluginPort++; + } + kernel->SetConnectionSettings(GlobalConfig.inboundConfig.listenip, pluginInboundPort, pluginOutboundSettings); + } + auto routing = fullConfig["routing"].toObject(); + for (const auto &appended : newRulesAppend) + { + newRules.append(appended); + } + routing["rules"] = newRules; + fullConfig["routing"] = routing; + fullConfig["outbounds"] = new_outbounds; + } + // + auto inboundPorts = GetInboundPorts(fullConfig); PluginHost->Send_ConnectivityEvent(QvConnectivityEventObject{ GetDisplayName(id), inboundPorts, QvConnecticity_Connecting }); + // + currentConnectionId = id; + lastConnectionId = id; + bool success = true; + for (auto &kernel : pluginKernels.keys()) + { + bool status = pluginKernels[kernel]->StartKernel(); + success = success && status; + if (!status) + { + LOG(MODULE_CONNECTION, "Plugin Kernel: " + kernel + " failed to start.") + break; + } + } + if (!success) + { + return tr("A plugin kernel failed to start. Please check the outbound settings."); + } + // auto result = vCoreInstance->StartConnection(fullConfig); if (!result.has_value()) { - currentConnectionId = id; - lastConnectionId = id; emit OnConnected(currentConnectionId); PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), inboundPorts, QvConnecticity_Connected }); } @@ -69,6 +187,12 @@ namespace Qv2ray::core::handlers { PluginHost->Send_ConnectivityEvent({ GetDisplayName(currentConnectionId), {}, QvConnecticity_Disconnecting }); vCoreInstance->StopConnection(); + // + for (auto &kernel : pluginKernels.keys()) + { + LOG(MODULE_CONNECTION, "Stopping plugin kernel: " + kernel) + pluginKernels[kernel]->StopKernel(); + } // Copy ConnectionId id = currentConnectionId; currentConnectionId = NullConnectionId; diff --git a/src/core/handler/KernelInstanceHandler.hpp b/src/core/handler/KernelInstanceHandler.hpp index f2dceae1..b1f0358a 100644 --- a/src/core/handler/KernelInstanceHandler.hpp +++ b/src/core/handler/KernelInstanceHandler.hpp @@ -1,4 +1,5 @@ #pragma once +#include "components/plugins/QvPluginHost.hpp" #include "core/CoreSafeTypes.hpp" #include "core/kernel/V2rayKernelInteractions.hpp" @@ -39,6 +40,7 @@ namespace Qv2ray::core::handlers void OnStatsDataArrived_p(const quint64 uploadSpeed, const quint64 downloadSpeed); private: + QMap pluginKernels; CONFIGROOT root; V2rayKernelInstance *vCoreInstance = nullptr; ConnectionId currentConnectionId = NullConnectionId; From d9650d604dd108778519204f274f09cfd23318c2 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Sat, 11 Apr 2020 21:23:20 +0800 Subject: [PATCH 61/92] add: added KernelPlugin<->V2ray integration support --- src/base/models/QvSettingsObject.hpp | 2 + src/components/plugins/QvPluginHost.cpp | 12 +- src/components/plugins/QvPluginHost.hpp | 2 +- src/core/handler/KernelInstanceHandler.cpp | 308 ++++++++++++--------- src/core/handler/KernelInstanceHandler.hpp | 3 +- src/main.cpp | 4 +- src/ui/w_MainWindow.cpp | 5 - src/ui/w_PreferencesWindow.cpp | 7 + src/ui/w_PreferencesWindow.hpp | 2 + src/ui/w_PreferencesWindow.ui | 39 ++- 10 files changed, 240 insertions(+), 144 deletions(-) diff --git a/src/base/models/QvSettingsObject.hpp b/src/base/models/QvSettingsObject.hpp index 42521f81..8c54f737 100644 --- a/src/base/models/QvSettingsObject.hpp +++ b/src/base/models/QvSettingsObject.hpp @@ -132,6 +132,8 @@ namespace Qv2ray::base::config { QMap pluginStates; bool v2rayIntegration; + int portAllocationStart; + Qv2rayPluginConfig() : pluginStates(), v2rayIntegration(true), portAllocationStart(15000){}; XTOSTRUCT(O(pluginStates, v2rayIntegration)) }; diff --git a/src/components/plugins/QvPluginHost.cpp b/src/components/plugins/QvPluginHost.cpp index 7a12f3d1..6d5a2ef5 100644 --- a/src/components/plugins/QvPluginHost.cpp +++ b/src/components/plugins/QvPluginHost.cpp @@ -11,9 +11,17 @@ namespace Qv2ray::components::plugins { QvPluginHost::QvPluginHost(QObject *parent) : QObject(parent) { - if (auto dir = QDir(QV2RAY_PLUGIN_SETTINGS_DIR); !dir.exists()) + if (!StartupOption.noPlugins) { - dir.mkpath(QV2RAY_PLUGIN_SETTINGS_DIR); + if (auto dir = QDir(QV2RAY_PLUGIN_SETTINGS_DIR); !dir.exists()) + { + dir.mkpath(QV2RAY_PLUGIN_SETTINGS_DIR); + } + InitializePluginHost(); + } + else + { + LOG(MODULE_PLUGINHOST, "PluginHost initilization skipped by command line option.") } } diff --git a/src/components/plugins/QvPluginHost.hpp b/src/components/plugins/QvPluginHost.hpp index dbef9791..c57eb08c 100644 --- a/src/components/plugins/QvPluginHost.hpp +++ b/src/components/plugins/QvPluginHost.hpp @@ -25,7 +25,6 @@ namespace Qv2ray::components::plugins public: explicit QvPluginHost(QObject *parent = nullptr); ~QvPluginHost(); - void InitializePluginHost(); // bool GetPluginEnableState(const QString &internalName) const; void SetPluginEnableState(const QString &internalName, bool isEnabled); @@ -83,6 +82,7 @@ namespace Qv2ray::components::plugins void QvPluginMessageBox(const QString &message); private: + void InitializePluginHost(); int RefreshPluginList(); bool InitializePlugin(const QString &internalName); void ClearPlugins(); diff --git a/src/core/handler/KernelInstanceHandler.cpp b/src/core/handler/KernelInstanceHandler.cpp index 758f8162..9d543d82 100644 --- a/src/core/handler/KernelInstanceHandler.cpp +++ b/src/core/handler/KernelInstanceHandler.cpp @@ -10,6 +10,13 @@ namespace Qv2ray::core::handlers connect(vCoreInstance, &V2rayKernelInstance::OnNewStatsDataArrived, this, &KernelInstanceHandler::OnStatsDataArrived_p); connect(vCoreInstance, &V2rayKernelInstance::OnProcessOutputReadyRead, this, &KernelInstanceHandler::OnKernelLogAvailable_p); connect(vCoreInstance, &V2rayKernelInstance::OnProcessErrored, this, &KernelInstanceHandler::OnKernelCrashed_p); + // + kernels = PluginHost->GetPluginKernels(); + for (const auto &kernel : kernels) + { + connect(kernel.get(), &QvPluginKernel::OnKernelCrashed, this, &KernelInstanceHandler::OnKernelCrashed_p); + connect(kernel.get(), &QvPluginKernel::OnKernelLogAvaliable, this, &KernelInstanceHandler::OnKernelLogAvailable_p); + } } KernelInstanceHandler::~KernelInstanceHandler() @@ -22,144 +29,187 @@ namespace Qv2ray::core::handlers { StopConnection(); } - pluginKernels.clear(); + activeKernels.clear(); this->root = root; - bool isComplex = IsComplexConfig(root); auto fullConfig = GenerateRuntimeConfig(root); - // - if (!isComplex) - { - QList> inboundInfo; - for (const auto &inbound_v : fullConfig["inbounds"].toArray()) - { - const auto &inbound = inbound_v.toObject(); - inboundInfo.push_back({ inbound["protocol"].toString(), inbound["port"].toInt(), inbound["tag"].toString() }); - } - // - auto kernels = PluginHost->GetPluginKernels(); - - auto pluginPort = 18900; - OUTBOUNDS new_outbounds; - auto rules = fullConfig["routing"].toObject()["rules"].toArray(); - QJsonArray newRules; - QJsonArray newRulesAppend; - - for (const auto &outbound_v : fullConfig["outbounds"].toArray()) - { - const auto &outbound = outbound_v.toObject(); - const auto &outProtocol = outbound["protocol"].toString(); - const auto &outTag = outbound["tag"].toString(); - // - if (!kernels.contains(outProtocol)) - { - new_outbounds.push_back(outbound); - continue; - } - LOG(MODULE_CONNECTION, "Get kernel plugin: " + outProtocol) - auto kernel = kernels[outProtocol].get(); - connect(kernel, &QvPluginKernel::OnKernelCrashed, this, &KernelInstanceHandler::OnKernelCrashed_p); - // connect(kernel, &QvPluginKernel::OnKernelStatsAvailable, this, &KernelInstanceHandler::OnStatsDataArrived_p); - connect(kernel, &QvPluginKernel::OnKernelLogAvaliable, this, &KernelInstanceHandler::OnKernelLogAvailable_p); - pluginKernels.insert(outProtocol, kernel); - // - auto pluginOutboundSettings = outbound["settings"].toObject(); - // - QMap pluginInboundPort; - for (const auto &[inProtocol, inPort, inTag] : inboundInfo) - { - if (!QStringList{ "http", "socks" }.contains(inProtocol)) - continue; - // - pluginInboundPort.insert(inProtocol, pluginPort); - LOG(MODULE_VCORE, "Plugin Integration: " + QSTRN(pluginPort) + " = " + inProtocol + "(" + inTag + ") --> " + outProtocol) - const auto &freedomTag = "plugin_" + inTag + "_" + inProtocol + ":" + QSTRN(inPort) + "_" + QSTRN(pluginPort); - const auto &pluginOutSettings = GenerateHTTPSOCKSOut("127.0.0.1", pluginPort, false, "", ""); - const auto &direct = GenerateOutboundEntry(inProtocol, pluginOutSettings, {}, {}, "0.0.0.0", freedomTag); - // - // - // - // Add the integration outbound to the list. - new_outbounds.push_back(direct); - // - bool hasRuleProcessed = false; - auto copyRules = rules; - for (auto i = 0; i < rules.count(); i++) - { - const auto &rule = rules.at(i).toObject(); - if (rule["outboundTag"].toString() == outTag) - { - auto newRule = rule; - newRule["outboundTag"] = freedomTag; - newRule["inboundTag"] = QJsonArray{ inTag }; - newRulesAppend.push_back(newRule); - hasRuleProcessed = true; - } - else - { - auto val = copyRules.takeAt(i); - if (!(val.isNull() || val.isUndefined())) - { - newRules.push_back(val); - } - } - } - rules = copyRules; - if (!hasRuleProcessed) - { - QJsonObject integrationRule; - integrationRule["outboundTag"] = freedomTag; - integrationRule["inboundTag"] = QJsonArray{ inTag }; - integrationRule["type"] = "field"; - newRulesAppend.push_back(integrationRule); - } - // - pluginPort++; - } - kernel->SetConnectionSettings(GlobalConfig.inboundConfig.listenip, pluginInboundPort, pluginOutboundSettings); - } - auto routing = fullConfig["routing"].toObject(); - for (const auto &appended : newRulesAppend) - { - newRules.append(appended); - } - routing["rules"] = newRules; - fullConfig["routing"] = routing; - fullConfig["outbounds"] = new_outbounds; - } - // auto inboundPorts = GetInboundPorts(fullConfig); PluginHost->Send_ConnectivityEvent(QvConnectivityEventObject{ GetDisplayName(id), inboundPorts, QvConnecticity_Connecting }); - // - currentConnectionId = id; - lastConnectionId = id; - bool success = true; - for (auto &kernel : pluginKernels.keys()) + QList> inboundInfo; + for (const auto &inbound_v : fullConfig["inbounds"].toArray()) { - bool status = pluginKernels[kernel]->StartKernel(); - success = success && status; - if (!status) + const auto &inbound = inbound_v.toObject(); + inboundInfo.push_back({ inbound["protocol"].toString(), inbound["port"].toInt(), inbound["tag"].toString() }); + } + // + bool isComplex = IsComplexConfig(root); + if (GlobalConfig.pluginConfig.v2rayIntegration) + { + if (isComplex) { - LOG(MODULE_CONNECTION, "Plugin Kernel: " + kernel + " failed to start.") - break; + LOG(MODULE_CONNECTION, "WARNING: Complex connection config support of this feature has not been tested.") } - } - if (!success) - { - return tr("A plugin kernel failed to start. Please check the outbound settings."); - } - // - auto result = vCoreInstance->StartConnection(fullConfig); + QList> pluginProcessedOutboundList; + // + // Process outbounds. + { + OUTBOUNDS new_outbounds; + auto pluginPort = GlobalConfig.pluginConfig.portAllocationStart; + // + /// Key = Original Outbound Tag, Value = QStringList containing new outbound lists. + for (const auto &outbound_v : fullConfig["outbounds"].toArray()) + { + const auto &outbound = outbound_v.toObject(); + const auto &outProtocol = outbound["protocol"].toString(); + // + if (!kernels.contains(outProtocol)) + { + // Normal outbound, or the one without a plugin supported. + new_outbounds.push_back(outbound); + continue; + } + LOG(MODULE_CONNECTION, "Get kernel plugin: " + outProtocol) + auto kernel = kernels[outProtocol].get(); + disconnect(kernel, &QvPluginKernel::OnKernelStatsAvailable, this, &KernelInstanceHandler::OnStatsDataArrived_p); + activeKernels.insert(outProtocol, kernel); + // + QMap pluginInboundPort; + const auto &originalOutboundTag = outbound["tag"].toString(); + for (const auto &[inProtocol, inPort, inTag] : inboundInfo) + { + if (!QStringList{ "http", "socks" }.contains(inProtocol)) + continue; + pluginInboundPort.insert(inProtocol, pluginPort); + LOG(MODULE_VCORE, "Plugin Integration: " + QSTRN(pluginPort) + " = " + inProtocol + "(" + inTag + ") --> " + outProtocol) + // + const auto &freedomTag = "plugin_" + inTag + "_" + inProtocol + "-" + QSTRN(inPort) + "_" + QSTRN(pluginPort); + const auto &pluginOutSettings = GenerateHTTPSOCKSOut("127.0.0.1", pluginPort, false, "", ""); + const auto &direct = GenerateOutboundEntry(inProtocol, pluginOutSettings, {}, {}, "0.0.0.0", freedomTag); + // + // Add the integration outbound to the list. + new_outbounds.push_back(direct); + pluginProcessedOutboundList.append({ originalOutboundTag, inTag, freedomTag }); + pluginPort++; + } + kernel->SetConnectionSettings(GlobalConfig.inboundConfig.listenip, pluginInboundPort, outbound["settings"].toObject()); + } + fullConfig["outbounds"] = new_outbounds; + } + // + // Process routing entries + { + QJsonArray newRules; + auto unprocessedOutbound = pluginProcessedOutboundList; + const auto rules = fullConfig["routing"].toObject()["rules"].toArray(); + for (auto i = 0; i < rules.count(); i++) + { + const auto rule = rules.at(i).toObject(); + // + bool ruleProcessed = false; + for (const auto &[originalTag, inboundTag, newOutboundTag] : pluginProcessedOutboundList) + { + // Check if a rule corresponds to the plugin outbound. + if (rule["outboundTag"] == originalTag) + { + auto newRule = rule; + newRule["outboundTag"] = newOutboundTag; + newRule["inboundTag"] = QJsonArray{ inboundTag }; + newRules.push_back(newRule); + ruleProcessed = true; + unprocessedOutbound.removeOne({ originalTag, inboundTag, newOutboundTag }); + } + } + if (!ruleProcessed) + { + newRules.append(rule); + } + } - if (!result.has_value()) - { - emit OnConnected(currentConnectionId); - PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), inboundPorts, QvConnecticity_Connected }); - } + for (const auto &[originalTag, inboundTag, newOutboundTag] : unprocessedOutbound) + { + QJsonObject integrationRule; + integrationRule["type"] = "field"; + integrationRule["outboundTag"] = newOutboundTag; + integrationRule["inboundTag"] = QJsonArray{ inboundTag }; + newRules.push_back(integrationRule); + } + auto routing = fullConfig["routing"].toObject(); + routing["rules"] = newRules; + fullConfig["routing"] = routing; + } + // + currentConnectionId = id; + lastConnectionId = id; + bool success = true; + for (auto &kernel : activeKernels.keys()) + { + bool status = activeKernels[kernel]->StartKernel(); + success = success && status; + if (!status) + { + LOG(MODULE_CONNECTION, "Plugin Kernel: " + kernel + " failed to start.") + break; + } + } + if (!success) + { + return tr("A plugin kernel failed to start. Please check the outbound settings."); + } + // + auto result = vCoreInstance->StartConnection(fullConfig); + + if (!result.has_value()) + { + emit OnConnected(currentConnectionId); + PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), inboundPorts, QvConnecticity_Connected }); + } + else + { + PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), {}, QvConnecticity_Disconnected }); + } + return result; + } // namespace Qv2ray::core::handlers else { - PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), {}, QvConnecticity_Disconnected }); + auto firstOutbound = fullConfig["outbounds"].toArray().first().toObject(); + if (kernels.contains(firstOutbound["protocol"].toString())) + { + auto kernel = kernels[firstOutbound["protocol"].toString()].get(); + QMap pluginInboundPort; + for (const auto &[_protocol, _port, _tag] : inboundInfo) + { + pluginInboundPort[_protocol] = _port; + } + connect(kernel, &QvPluginKernel::OnKernelStatsAvailable, this, &KernelInstanceHandler::OnStatsDataArrived_p); + currentConnectionId = id; + lastConnectionId = id; + kernel->SetConnectionSettings(GlobalConfig.inboundConfig.listenip, pluginInboundPort, firstOutbound["settings"].toObject()); + bool result = kernel->StartKernel(); + if (!result) + { + return tr("A plugin kernel failed to start. Please check the outbound settings."); + } + else + { + return {}; + } + } + else + { + currentConnectionId = id; + lastConnectionId = id; + auto result = vCoreInstance->StartConnection(fullConfig); + if (!result.has_value()) + { + emit OnConnected(currentConnectionId); + PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), inboundPorts, QvConnecticity_Connected }); + } + else + { + PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), {}, QvConnecticity_Disconnected }); + } + return result; + } } - return result; } void KernelInstanceHandler::RestartConnection() @@ -188,10 +238,10 @@ namespace Qv2ray::core::handlers PluginHost->Send_ConnectivityEvent({ GetDisplayName(currentConnectionId), {}, QvConnecticity_Disconnecting }); vCoreInstance->StopConnection(); // - for (auto &kernel : pluginKernels.keys()) + for (auto &kernel : activeKernels.keys()) { LOG(MODULE_CONNECTION, "Stopping plugin kernel: " + kernel) - pluginKernels[kernel]->StopKernel(); + activeKernels[kernel]->StopKernel(); } // Copy ConnectionId id = currentConnectionId; diff --git a/src/core/handler/KernelInstanceHandler.hpp b/src/core/handler/KernelInstanceHandler.hpp index b1f0358a..edc4815d 100644 --- a/src/core/handler/KernelInstanceHandler.hpp +++ b/src/core/handler/KernelInstanceHandler.hpp @@ -40,7 +40,8 @@ namespace Qv2ray::core::handlers void OnStatsDataArrived_p(const quint64 uploadSpeed, const quint64 downloadSpeed); private: - QMap pluginKernels; + QMap> kernels; + QMap activeKernels; CONFIGROOT root; V2rayKernelInstance *vCoreInstance = nullptr; ConnectionId currentConnectionId = NullConnectionId; diff --git a/src/main.cpp b/src/main.cpp index a1eeddcc..56e007e6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -408,8 +408,8 @@ int main(int argc, char *argv[]) #endif //_qApp.setAttribute(Qt::AA_DontUseNativeMenuBar); // Initialise Connection Handler - ConnectionManager = new QvConfigHandler(); PluginHost = new QvPluginHost(); + ConnectionManager = new QvConfigHandler(); // Handler for session logout, shutdown, etc. // Will not block. QGuiApplication::setFallbackSessionManagementEnabled(false); @@ -427,8 +427,8 @@ int main(int argc, char *argv[]) signal(SIGUSR2, [](int) { emit MainWindow::mwInstance->StopConnection(); }); #endif auto rcode = _qApp.exec(); - delete PluginHost; delete ConnectionManager; + delete PluginHost; LOG(MODULE_INIT, "Quitting normally") return rcode; #ifndef QT_DEBUG diff --git a/src/ui/w_MainWindow.cpp b/src/ui/w_MainWindow.cpp index e1fe0da7..601bcf6a 100644 --- a/src/ui/w_MainWindow.cpp +++ b/src/ui/w_MainWindow.cpp @@ -300,11 +300,6 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) StartProcessingPlugins(); } - if (!StartupOption.noPlugins) - { - PluginHost->InitializePluginHost(); - } - CheckSubscriptionsUpdate(); // splitter->setSizes(QList() << 100 << 300); diff --git a/src/ui/w_PreferencesWindow.cpp b/src/ui/w_PreferencesWindow.cpp index fbc4bd5f..2a391da3 100644 --- a/src/ui/w_PreferencesWindow.cpp +++ b/src/ui/w_PreferencesWindow.cpp @@ -129,6 +129,7 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent), Current localDNSCb->setChecked(CurrentConfig.connectionConfig.withLocalDNS); // pluginKernelV2rayIntegrationCB->setChecked(CurrentConfig.pluginConfig.v2rayIntegration); + pluginKernelPortAllocateCB->setValue(CurrentConfig.pluginConfig.portAllocationStart); // // DNSListTxt->clear(); @@ -1106,3 +1107,9 @@ void PreferencesWindow::on_pluginKernelV2rayIntegrationCB_stateChanged(int arg1) LOADINGCHECK CurrentConfig.pluginConfig.v2rayIntegration = arg1 == Qt::Checked; } + +void PreferencesWindow::on_pluginKernelPortAllocateCB_valueChanged(int arg1) +{ + LOADINGCHECK + CurrentConfig.pluginConfig.portAllocationStart = arg1; +} diff --git a/src/ui/w_PreferencesWindow.hpp b/src/ui/w_PreferencesWindow.hpp index 622810b4..679b5c14 100644 --- a/src/ui/w_PreferencesWindow.hpp +++ b/src/ui/w_PreferencesWindow.hpp @@ -158,6 +158,8 @@ class PreferencesWindow void on_pluginKernelV2rayIntegrationCB_stateChanged(int arg1); + void on_pluginKernelPortAllocateCB_valueChanged(int arg1); + private: // RouteSettingsMatrixWidget *routeSettingsWidget; diff --git a/src/ui/w_PreferencesWindow.ui b/src/ui/w_PreferencesWindow.ui index 15008b0b..dd705f05 100644 --- a/src/ui/w_PreferencesWindow.ui +++ b/src/ui/w_PreferencesWindow.ui @@ -490,7 +490,7 @@ 0 0 - 505 + 964 603 @@ -1135,9 +1135,9 @@ Enabling V2ray Integration will allow the kernel benifit from the V2ray routing engine. Otherwise, these features will be disabled: -Advanced Route Settings. -ByPass CN -Forcing Direct of Local LAN Address +Advanced Route Settings +Bypass CN websites and IPs +Forcing direct connection of Local LAN addresses Custom DNS Settings @@ -1160,6 +1160,37 @@ Custom DNS Settings + + + + Qv2ray will allocate two ports, for HTTP and SOCKS respectively, if enabled, for each kernel plugin. + + + + + + + + + Port Allocation Start + + + + + + + 1 + + + 65535 + + + 13000 + + + + + From a1bf0e9a54964dc9437570d32d663ab279a506b5 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Sat, 11 Apr 2020 21:43:43 +0800 Subject: [PATCH 62/92] update: updated label text, fixed a spelling misATke --- src/ui/w_PreferencesWindow.ui | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ui/w_PreferencesWindow.ui b/src/ui/w_PreferencesWindow.ui index dd705f05..40251149 100644 --- a/src/ui/w_PreferencesWindow.ui +++ b/src/ui/w_PreferencesWindow.ui @@ -1132,12 +1132,12 @@ - Enabling V2ray Integration will allow the kernel benifit from the V2ray routing engine. + Enabling V2ray Integration will allow the kernel benefit from the V2ray routing engine. Otherwise, these features will be disabled: -Advanced Route Settings +Advanced Routing Settings Bypass CN websites and IPs -Forcing direct connection of Local LAN addresses +Direct connection of Local LAN addresses Custom DNS Settings @@ -1163,7 +1163,7 @@ Custom DNS Settings - Qv2ray will allocate two ports, for HTTP and SOCKS respectively, if enabled, for each kernel plugin. + Qv2ray will allocate ports, for HTTP and SOCKS respectively, if enabled, for each kernel plugin. From 25d78592e28144ddb26dbf312fe210f5f89fef39 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Sat, 11 Apr 2020 21:46:41 +0800 Subject: [PATCH 63/92] fix: fixed a missing signal emit --- src/core/handler/KernelInstanceHandler.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/core/handler/KernelInstanceHandler.cpp b/src/core/handler/KernelInstanceHandler.cpp index 9d543d82..a27c8744 100644 --- a/src/core/handler/KernelInstanceHandler.cpp +++ b/src/core/handler/KernelInstanceHandler.cpp @@ -184,13 +184,14 @@ namespace Qv2ray::core::handlers lastConnectionId = id; kernel->SetConnectionSettings(GlobalConfig.inboundConfig.listenip, pluginInboundPort, firstOutbound["settings"].toObject()); bool result = kernel->StartKernel(); - if (!result) + if (result) { - return tr("A plugin kernel failed to start. Please check the outbound settings."); + emit OnConnected(currentConnectionId); + return {}; } else { - return {}; + return tr("A plugin kernel failed to start. Please check the outbound settings."); } } else From e12090417114cb0040ddc0067168cd8abb09750d Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Sat, 11 Apr 2020 21:53:37 +0800 Subject: [PATCH 64/92] add: ignore updating subscription by setting interval to 0 --- src/ui/w_MainWindow_extra.cpp | 16 ++++++++++------ src/ui/w_SubscriptionManager.ui | 3 --- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/ui/w_MainWindow_extra.cpp b/src/ui/w_MainWindow_extra.cpp index adef24ed..c9245741 100644 --- a/src/ui/w_MainWindow_extra.cpp +++ b/src/ui/w_MainWindow_extra.cpp @@ -61,19 +61,23 @@ void MainWindow::CheckSubscriptionsUpdate() auto subscriptions = ConnectionManager->Subscriptions(); for (auto entry : subscriptions) { - auto into = ConnectionManager->GetGroupMetaObject(entry); + const auto info = ConnectionManager->GetGroupMetaObject(entry); // - auto lastRenewDate = QDateTime::fromTime_t(into.lastUpdated); - auto renewTime = lastRenewDate.addSecs(into.updateInterval * 86400); + // The update is ignored. + if (info.updateInterval == 0) + continue; + // + auto lastRenewDate = QDateTime::fromTime_t(info.lastUpdated); + auto renewTime = lastRenewDate.addSecs(info.updateInterval * 86400); LOG(MODULE_SUBSCRIPTION, // - "Subscription \"" + entry.toString() + "\": " + // + "Subscription \"" + info.displayName + "\": " + // NEWLINE + " --> Last renewal time: " + lastRenewDate.toString() + // - NEWLINE + " --> Renew interval: " + QSTRN(into.updateInterval) + // + NEWLINE + " --> Renew interval: " + QSTRN(info.updateInterval) + // NEWLINE + " --> Ideal renew time: " + renewTime.toString()) // if (renewTime <= QDateTime::currentDateTime()) { - LOG(MODULE_SUBSCRIPTION, "Subscription: " + entry.toString() + " needs to be updated.") + LOG(MODULE_SUBSCRIPTION, "Subscription: " + info.displayName + " needs to be updated.") updateList.append(entry.toString()); } } diff --git a/src/ui/w_SubscriptionManager.ui b/src/ui/w_SubscriptionManager.ui index 78daa6bc..57eddf13 100644 --- a/src/ui/w_SubscriptionManager.ui +++ b/src/ui/w_SubscriptionManager.ui @@ -181,9 +181,6 @@ - - 0.500000000000000 - 365.000000000000000 From a46a466d24620cf6c0e44be1389e5d0c6eed77db Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Sat, 11 Apr 2020 21:58:08 +0800 Subject: [PATCH 65/92] fix: partially resolved #507 --- src/ui/w_MainWindow_extra.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/w_MainWindow_extra.cpp b/src/ui/w_MainWindow_extra.cpp index c9245741..22107b1c 100644 --- a/src/ui/w_MainWindow_extra.cpp +++ b/src/ui/w_MainWindow_extra.cpp @@ -23,7 +23,7 @@ void MainWindow::MWSetSystemProxy() // Not use PAC, System proxy should use HTTP or SOCKS LOG(MODULE_PROXY, "Setting up system proxy.") // A 'proxy host' should be a host WITHOUT `http://` uri scheme - proxyAddress = "localhost"; + proxyAddress = "127.0.0.1"; } else { From 5dafd294fde376d01f407114f57db350269143c9 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Sat, 11 Apr 2020 22:25:42 +0800 Subject: [PATCH 66/92] fix: fixed SSD:// import feature --- src/core/connection/Serialization_ssd.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/core/connection/Serialization_ssd.cpp b/src/core/connection/Serialization_ssd.cpp index 8c43523b..f207c3d4 100644 --- a/src/core/connection/Serialization_ssd.cpp +++ b/src/core/connection/Serialization_ssd.cpp @@ -83,7 +83,7 @@ namespace Qv2ray::core::connection::Serialization } // decode base64 - const auto ssdURIBody = QStringRef(&uri, 6, uri.length() - 7); + const auto ssdURIBody = QStringRef(&uri, 6, uri.length() - 6); const auto decodedJSON = QByteArray::fromBase64(ssdURIBody.toUtf8()); if (decodedJSON.length() == 0) @@ -161,13 +161,12 @@ namespace Qv2ray::core::connection::Serialization { ssObject.port = port; } - else if (auto currPort = serverObject["port"].toInt(-1); port >= 0 && port <= 65535) + else if (auto currPort = serverObject["port"].toInt(-1); (currPort >= 0 && currPort <= 65535)) { ssObject.port = currPort; } else { - *logList << QObject::tr("Invalid port encountered. using fallback value."); ssObject.port = port; } @@ -186,7 +185,6 @@ namespace Qv2ray::core::connection::Serialization } else { - *logList << QObject::tr("Invalid name encountered. using fallback value."); nodeName = QString("%1:%2").arg(ssObject.address).arg(ssObject.port); } @@ -201,7 +199,7 @@ namespace Qv2ray::core::connection::Serialization } else if (!serverObject["ratio"].isUndefined()) { - *logList << QObject::tr("Invalid ratio encountered. using fallback value."); + //*logList << QObject::tr("Invalid ratio encountered. using fallback value."); } // format the total name of the node. @@ -209,10 +207,8 @@ namespace Qv2ray::core::connection::Serialization // appending to the total list CONFIGROOT root; OUTBOUNDS outbounds; - outbounds.append( - GenerateOutboundEntry("shadowsocks", GenerateShadowSocksOUT(QList{ ssObject }), QJsonObject())); + outbounds.append(GenerateOutboundEntry("shadowsocks", GenerateShadowSocksOUT({ ssObject }), {})); JADD(outbounds) - serverList.insertMulti(totalName, root); } From 33b31e3390a255335a0daa935fc4f8dfc8eac747 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Sat, 11 Apr 2020 22:42:06 +0800 Subject: [PATCH 67/92] fix: skip saving config when importing multiple connection configs --- src/core/handler/ConfigHandler.cpp | 8 ++++++-- src/core/handler/ConfigHandler.hpp | 3 ++- src/ui/w_ImportConfig.cpp | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/core/handler/ConfigHandler.cpp b/src/core/handler/ConfigHandler.cpp index b75573ec..6dc67556 100644 --- a/src/core/handler/ConfigHandler.cpp +++ b/src/core/handler/ConfigHandler.cpp @@ -591,7 +591,8 @@ namespace Qv2ray::core::handlers { GetDisplayName(id), uploadSpeed, downloadSpeed, connections[id].upLinkData, connections[id].downLinkData }); } - const ConnectionId QvConfigHandler::CreateConnection(const QString &displayName, const GroupId &groupId, const CONFIGROOT &root) + const ConnectionId QvConfigHandler::CreateConnection(const QString &displayName, const GroupId &groupId, const CONFIGROOT &root, + bool skipSaveConfig) { LOG(MODULE_CORE_HANDLER, "Creating new connection: " + displayName) ConnectionId newId(GenerateUuid()); @@ -602,7 +603,10 @@ namespace Qv2ray::core::handlers emit OnConnectionCreated(newId, displayName); PluginHost->Send_ConnectionEvent({ displayName, "", ConnectionEvent_Created }); UpdateConnection(newId, root); - CHSaveConfigData_p(); + if (!skipSaveConfig) + { + CHSaveConfigData_p(); + } return newId; } diff --git a/src/core/handler/ConfigHandler.hpp b/src/core/handler/ConfigHandler.hpp index 9da0342c..c0e71d91 100644 --- a/src/core/handler/ConfigHandler.hpp +++ b/src/core/handler/ConfigHandler.hpp @@ -83,7 +83,8 @@ namespace Qv2ray::core::handlers const optional DeleteConnection(const ConnectionId &id); const optional RenameConnection(const ConnectionId &id, const QString &newName); const optional MoveConnectionGroup(const ConnectionId &id, const GroupId &newGroupId); - const ConnectionId CreateConnection(const QString &displayName, const GroupId &groupId, const CONFIGROOT &root); + const ConnectionId CreateConnection(const QString &displayName, const GroupId &groupId, const CONFIGROOT &root, + bool skipSaveConfig = false); // // Get Conncetion Property const CONFIGROOT GetConnectionRoot(const ConnectionId &id) const; diff --git a/src/ui/w_ImportConfig.cpp b/src/ui/w_ImportConfig.cpp index 30a191ec..7da70f5e 100644 --- a/src/ui/w_ImportConfig.cpp +++ b/src/ui/w_ImportConfig.cpp @@ -80,7 +80,7 @@ int ImportConfigWindow::ImportConnection() { connName = protocol + "/" + host + ":" + QSTRN(port) + "-" + GenerateRandomString(5); } - ConnectionManager->CreateConnection(connName, groupId, connConf); + ConnectionManager->CreateConnection(connName, groupId, connConf, true); } } From 86e187b8c4f47c87b009cfabe7d2836333befa46 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Sat, 11 Apr 2020 23:06:15 +0800 Subject: [PATCH 68/92] version: bumping version (test) --- makespec/VERSION | 2 +- makespec/VERSIONSUFFIX | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/makespec/VERSION b/makespec/VERSION index 005119ba..437459cd 100644 --- a/makespec/VERSION +++ b/makespec/VERSION @@ -1 +1 @@ -2.4.1 +2.5.0 diff --git a/makespec/VERSIONSUFFIX b/makespec/VERSIONSUFFIX index 8b137891..39a5a5a1 100644 --- a/makespec/VERSIONSUFFIX +++ b/makespec/VERSIONSUFFIX @@ -1 +1 @@ - +-pre1 From 0f81f0ef39e6218044b889c4148d17262f024729 Mon Sep 17 00:00:00 2001 From: ymshenyu Date: Sat, 11 Apr 2020 23:14:45 +0800 Subject: [PATCH 69/92] debian: bump version --- debian/changelog | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/debian/changelog b/debian/changelog index f48e5fa8..31a21954 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,13 @@ +qv2ray (2.5.0~pre1-1) unstable; urgency=medium + + * fix: skip saving config when importing multiple connection configs + * add: ignore updating subscription by setting interval to 0 + * fix: fixed a missing signal emit + * update: updated label text, fixed a spelling misATke + * add: added kernel plugin support + + -- Guobang Bi Sat, 11 Apr 2020 23:12:20 +0800 + qv2ray (2.4.1-1) unstable; urgency=medium * add: add new semver checker From e88413d42d45d7641210b299d16dfb36cea32b5b Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Sat, 11 Apr 2020 23:31:27 +0800 Subject: [PATCH 70/92] version: get rid of version 5243 --- makespec/BUILDVERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 006ac3e1..8f08884b 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5243 \ No newline at end of file +5244 From 796ada817da6dda4aa41d83a0adc98a3b7a68d17 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Sun, 12 Apr 2020 17:57:45 +0800 Subject: [PATCH 71/92] change: PreferencesWindow refactor and added custom Qv2ray proxy --- src/base/models/QvSettingsObject.hpp | 48 +- src/common/HTTPRequestHelper.cpp | 113 +- src/common/HTTPRequestHelper.hpp | 18 +- src/components/update/UpdateChecker.cpp | 4 +- src/core/handler/ConfigHandler.cpp | 2 +- src/ui/w_PreferencesWindow.cpp | 36 + src/ui/w_PreferencesWindow.hpp | 10 + src/ui/w_PreferencesWindow.ui | 1741 +++++++++++------------ src/ui/widgets/RouteSettingsMatrix.ui | 163 +-- 9 files changed, 1061 insertions(+), 1074 deletions(-) diff --git a/src/base/models/QvSettingsObject.hpp b/src/base/models/QvSettingsObject.hpp index 8c54f737..155e6ce9 100644 --- a/src/base/models/QvSettingsObject.hpp +++ b/src/base/models/QvSettingsObject.hpp @@ -214,6 +214,17 @@ namespace Qv2ray::base::config XTOSTRUCT(O(ignoredVersion, updateChannel)) }; + struct Qv2rayNetworkConfig + { + bool useCustomProxy; + QString address; + QString type; + int port; + QString userAgent; + Qv2rayNetworkConfig() : address(""), type("http"), port(8000), userAgent("Qv2ray/" QV2RAY_VERSION_STRING " WebRequestHelper"){}; + XTOSTRUCT(O(useCustomProxy, type, address, port, userAgent)) + }; + struct Qv2rayConfig { int config_version; @@ -233,18 +244,47 @@ namespace Qv2ray::base::config Qv2rayPluginConfig pluginConfig; Qv2rayKernelConfig kernelConfig; Qv2rayUpdateConfig updateConfig; + Qv2rayNetworkConfig networkConfig; Qv2rayToolBarConfig toolBarConfig; Qv2rayInboundsConfig inboundConfig; Qv2rayConnectionConfig connectionConfig; Qv2rayConfig() - : config_version(QV2RAY_CONFIG_VERSION), tProxySupport(false), logLevel(), autoStartId("null"), groups(), subscriptions(), - connections(), uiConfig(), apiConfig(), pluginConfig(), kernelConfig(), updateConfig(), toolBarConfig(), inboundConfig(), + : config_version(QV2RAY_CONFIG_VERSION), // + tProxySupport(false), // + logLevel(), // + autoStartId("null"), // + groups(), // + subscriptions(), // + connections(), // + uiConfig(), // + apiConfig(), // + pluginConfig(), // + kernelConfig(), // + updateConfig(), // + networkConfig(), // + toolBarConfig(), // + inboundConfig(), // connectionConfig() { } - XTOSTRUCT(O(config_version, tProxySupport, logLevel, uiConfig, pluginConfig, updateConfig, kernelConfig, groups, connections, - subscriptions, autoStartId, inboundConfig, connectionConfig, toolBarConfig, apiConfig)) + XTOSTRUCT(O(config_version, // + tProxySupport, // + logLevel, // + uiConfig, // + pluginConfig, // + updateConfig, // + kernelConfig, // + networkConfig, // + groups, // + connections, // + subscriptions, // + autoStartId, // + inboundConfig, // + connectionConfig, // + toolBarConfig, // + apiConfig // + )) }; } // namespace Qv2ray::base::config diff --git a/src/common/HTTPRequestHelper.cpp b/src/common/HTTPRequestHelper.cpp index 240f3b6e..04797596 100644 --- a/src/common/HTTPRequestHelper.cpp +++ b/src/common/HTTPRequestHelper.cpp @@ -16,112 +16,71 @@ namespace Qv2ray::common accessManager.disconnect(); } - bool QvHttpRequestHelper::setUrl(const QString &url) - { - QUrl qUrl = QUrl(url); - - if (!qUrl.isValid()) - { - LOG(MODULE_NETWORK, "Provided URL is invalid: " + url) - return false; - } - - request.setUrl(qUrl); - return true; - } - void QvHttpRequestHelper::setHeader(const QByteArray &key, const QByteArray &value) { DEBUG(MODULE_NETWORK, "Adding HTTP request header: " + key + ":" + value) request.setRawHeader(key, value); } - QByteArray QvHttpRequestHelper::syncget(const QString &url, bool useProxy) + QByteArray QvHttpRequestHelper::Get(const QString &url, bool useProxy) { - this->setUrl(url); - + request.setUrl({ url }); if (useProxy) { - auto proxy = QNetworkProxyFactory::systemProxyForQuery(); - accessManager.setProxy(proxy.first()); - LOG(MODULE_NETWORK, "Sync get is using system proxy settings") + auto p = GlobalConfig.networkConfig.useCustomProxy ? + QNetworkProxy{ + GlobalConfig.networkConfig.type == "http" ? QNetworkProxy::HttpProxy : QNetworkProxy::Socks5Proxy, // + GlobalConfig.networkConfig.address, // + quint16(GlobalConfig.networkConfig.port) // + } : + QNetworkProxyFactory::systemProxyForQuery().first(); + accessManager.setProxy(p); } else { + LOG(MODULE_NETWORK, "Get without proxy.") accessManager.setProxy(QNetworkProxy(QNetworkProxy::ProxyType::NoProxy)); } request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy); request.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, true); - request.setHeader(QNetworkRequest::KnownHeaders::UserAgentHeader, "Mozilla/5.0 (rv:71.0) Gecko/20100101 Firefox/71.0"); + request.setHeader(QNetworkRequest::KnownHeaders::UserAgentHeader, GlobalConfig.networkConfig.userAgent); reply = accessManager.get(request); - connect(reply, &QNetworkReply::finished, this, &QvHttpRequestHelper::onRequestFinished_p); // QEventLoop loop; connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); loop.exec(); + // // Data or timeout? auto data = reply->readAll(); return data; } - void QvHttpRequestHelper::get(const QString &url) + void QvHttpRequestHelper::AsyncGet(const QString &url) { - this->setUrl(url); - // request.setRawHeader("Content-Type", - // "application/x-www-form-urlencoded"); + request.setUrl({ url }); + if (GlobalConfig.networkConfig.useCustomProxy) + { + QNetworkProxy p{ + GlobalConfig.networkConfig.type == "http" ? QNetworkProxy::HttpProxy : QNetworkProxy::Socks5Proxy, // + GlobalConfig.networkConfig.address, // + quint16(GlobalConfig.networkConfig.port) // + }; + accessManager.setProxy(p); + } + else + { + LOG(MODULE_NETWORK, "Get without proxy.") + accessManager.setProxy(QNetworkProxy(QNetworkProxy::ProxyType::NoProxy)); + } + request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy); + request.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, true); + request.setHeader(QNetworkRequest::KnownHeaders::UserAgentHeader, GlobalConfig.networkConfig.userAgent); reply = accessManager.get(request); connect(reply, &QNetworkReply::finished, this, &QvHttpRequestHelper::onRequestFinished_p); - connect(reply, &QNetworkReply::readyRead, this, &QvHttpRequestHelper::onReadyRead); + connect(reply, &QNetworkReply::readyRead, this, &QvHttpRequestHelper::onReadyRead_p); } - // void QvHttpRequestHelper::post(const QString &url, const QByteArray - // &data) - //{ - // this->setUrl(url); - // request.setRawHeader("Content-Type", "application/json"); - // reply = accessManager.post(request, data); - // connect(reply, &QNetworkReply::finished, this, - // &QvHttpRequestHelper::onRequestFinished); connect(reply, - // &QNetworkReply::readyRead, this, &QvHttpRequestHelper::onReadyRead); - //} - - // void QvHttpRequestHelper::put(const QString &url, const QByteArray - // &data) - // { - // this->setUrl(url); - // request.setRawHeader("Content-Type", "application/json"); - // reply = accessManager.put(request, data); - // connect(reply, &QNetworkReply::finished, this, - // &QvHttpRequestHelper::onRequestFinished); connect(reply, - // &QNetworkReply::readyRead, this, - // &QvHttpRequestHelper::onReadyRead); - // } - - // void QvHttpRequestHelper::del(const QString &url) - // { - // this->setUrl(url); - // request.setRawHeader("Content-Type", "application/json"); - // reply = accessManager.deleteResource(request); - // connect(reply, &QNetworkReply::finished, this, - // &QvHttpRequestHelper::onRequestFinished); connect(reply, - // &QNetworkReply::readyRead, this, - // &QvHttpRequestHelper::onReadyRead); - // } - - // void QvHttpRequestHelper::login(const QString &url, const QByteArray - // &data) - // { - // this->setUrl(url); - // request.setRawHeader("Content-Type", - // "application/x-www-form-urlencoded"); reply = - // accessManager.post(request, data); connect(reply, - // &QNetworkReply::finished, this, - // &QvHttpRequestHelper::onRequestFinished); connect(reply, - // &QNetworkReply::readyRead, this, - // &QvHttpRequestHelper::onReadyRead); - // } - void QvHttpRequestHelper::onRequestFinished_p() { if (reply->attribute(QNetworkRequest::HTTP2WasUsedAttribute).toBool()) @@ -134,15 +93,15 @@ namespace Qv2ray::common QString error = QMetaEnum::fromType().key(reply->error()); LOG(MODULE_NETWORK, "Network request error string: " + error) QByteArray empty; - emit httpRequestFinished(empty); + emit OnRequestFinished(empty); } else { - emit httpRequestFinished(this->data); + emit OnRequestFinished(this->data); } } - void QvHttpRequestHelper::onReadyRead() + void QvHttpRequestHelper::onReadyRead_p() { DEBUG(MODULE_NETWORK, "A request is now ready read") this->data += reply->readAll(); diff --git a/src/common/HTTPRequestHelper.hpp b/src/common/HTTPRequestHelper.hpp index c2ebc159..a2f8f14b 100644 --- a/src/common/HTTPRequestHelper.hpp +++ b/src/common/HTTPRequestHelper.hpp @@ -31,26 +31,18 @@ namespace Qv2ray::common public: explicit QvHttpRequestHelper(QObject *parent = nullptr); ~QvHttpRequestHelper(); - bool setUrl(const QString &url); - void setHeader(const QByteArray &key, const QByteArray &value); // get - QByteArray syncget(const QString &url, bool useProxy); - void get(const QString &url); - //// insert - // void post(const QString &url, const QByteArray &data); - //// update - // void put(const QString &url, const QByteArray &data); - //// delete - // void del(const QString &url); - // void login(const QString &url, const QByteArray &data); + void AsyncGet(const QString &url); + QByteArray Get(const QString &url, bool useProxy); signals: - void httpRequestFinished(QByteArray &data); + void OnRequestFinished(QByteArray &data); private slots: void onRequestFinished_p(); - void onReadyRead(); + void onReadyRead_p(); private: + void setHeader(const QByteArray &key, const QByteArray &value); QByteArray data; QUrl url; QNetworkReply *reply; diff --git a/src/components/update/UpdateChecker.cpp b/src/components/update/UpdateChecker.cpp index 93bac127..0662fb93 100644 --- a/src/components/update/UpdateChecker.cpp +++ b/src/components/update/UpdateChecker.cpp @@ -19,7 +19,7 @@ namespace Qv2ray::components QvUpdateChecker::QvUpdateChecker(QObject *parent) : QObject(parent) { requestHelper = new QvHttpRequestHelper(this); - connect(requestHelper, &QvHttpRequestHelper::httpRequestFinished, this, &QvUpdateChecker::VersionUpdate); + connect(requestHelper, &QvHttpRequestHelper::OnRequestFinished, this, &QvUpdateChecker::VersionUpdate); } QvUpdateChecker::~QvUpdateChecker() { @@ -29,7 +29,7 @@ namespace Qv2ray::components #ifndef DISABLE_AUTO_UPDATE auto updateChannel = GlobalConfig.updateConfig.updateChannel; LOG(MODULE_NETWORK, "Start checking update for channel ID: " + QSTRN(updateChannel)) - requestHelper->get(UpdateChannelLink[updateChannel]); + requestHelper->AsyncGet(UpdateChannelLink[updateChannel]); #endif } void QvUpdateChecker::VersionUpdate(QByteArray &data) diff --git a/src/core/handler/ConfigHandler.cpp b/src/core/handler/ConfigHandler.cpp index 6dc67556..ae39900a 100644 --- a/src/core/handler/ConfigHandler.cpp +++ b/src/core/handler/ConfigHandler.cpp @@ -467,7 +467,7 @@ namespace Qv2ray::core::handlers return false; } isHttpRequestInProgress = true; - auto data = httpHelper->syncget(groups[id].address, useSystemProxy); + auto data = httpHelper->Get(groups[id].address, useSystemProxy); isHttpRequestInProgress = false; return CHUpdateSubscription_p(id, data); } diff --git a/src/ui/w_PreferencesWindow.cpp b/src/ui/w_PreferencesWindow.cpp index 2a391da3..d375f16d 100644 --- a/src/ui/w_PreferencesWindow.cpp +++ b/src/ui/w_PreferencesWindow.cpp @@ -131,6 +131,11 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent), Current pluginKernelV2rayIntegrationCB->setChecked(CurrentConfig.pluginConfig.v2rayIntegration); pluginKernelPortAllocateCB->setValue(CurrentConfig.pluginConfig.portAllocationStart); // + qvProxyPortCB->setValue(CurrentConfig.networkConfig.port); + qvProxyAddressTxt->setText(CurrentConfig.networkConfig.address); + qvProxyTypeCombo->setCurrentText(CurrentConfig.networkConfig.type); + qvNetworkUATxt->setText(CurrentConfig.networkConfig.userAgent); + qvUseProxyCB->setChecked(CurrentConfig.networkConfig.useCustomProxy); // DNSListTxt->clear(); for (auto dnsStr : CurrentConfig.connectionConfig.dnsList) @@ -1113,3 +1118,34 @@ void PreferencesWindow::on_pluginKernelPortAllocateCB_valueChanged(int arg1) LOADINGCHECK CurrentConfig.pluginConfig.portAllocationStart = arg1; } + +void PreferencesWindow::on_qvProxyAddressTxt_textEdited(const QString &arg1) +{ + LOADINGCHECK + CurrentConfig.networkConfig.address = arg1; +} + +void PreferencesWindow::on_qvProxyTypeCombo_currentTextChanged(const QString &arg1) +{ + LOADINGCHECK + CurrentConfig.networkConfig.type = arg1.toLower(); +} + +void PreferencesWindow::on_qvProxyPortCB_valueChanged(int arg1) +{ + LOADINGCHECK + CurrentConfig.networkConfig.port = arg1; +} + +void PreferencesWindow::on_qvNetworkUATxt_textEdited(const QString &arg1) +{ + LOADINGCHECK + CurrentConfig.networkConfig.userAgent = arg1; +} + +void PreferencesWindow::on_qvUseProxyCB_stateChanged(int arg1) +{ + + LOADINGCHECK + CurrentConfig.networkConfig.useCustomProxy = arg1 == Qt::Checked; +} diff --git a/src/ui/w_PreferencesWindow.hpp b/src/ui/w_PreferencesWindow.hpp index 679b5c14..7ead3d9a 100644 --- a/src/ui/w_PreferencesWindow.hpp +++ b/src/ui/w_PreferencesWindow.hpp @@ -160,6 +160,16 @@ class PreferencesWindow void on_pluginKernelPortAllocateCB_valueChanged(int arg1); + void on_qvProxyAddressTxt_textEdited(const QString &arg1); + + void on_qvProxyTypeCombo_currentTextChanged(const QString &arg1); + + void on_qvProxyPortCB_valueChanged(int arg1); + + void on_qvNetworkUATxt_textEdited(const QString &arg1); + + void on_qvUseProxyCB_stateChanged(int arg1); + private: // RouteSettingsMatrixWidget *routeSettingsWidget; diff --git a/src/ui/w_PreferencesWindow.ui b/src/ui/w_PreferencesWindow.ui index 40251149..61839397 100644 --- a/src/ui/w_PreferencesWindow.ui +++ b/src/ui/w_PreferencesWindow.ui @@ -9,8 +9,8 @@ 0 0 - 1010 - 622 + 890 + 617 @@ -19,457 +19,560 @@ true - + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + - 0 + 6 General Settings - - - - - QFrame::NoFrame + + + + + + + Appearance + + + + + + Darkmode UI Icons + + + + + + + Enabled + + + + + + + Darkmode Tray Icon + + + + + + + Enabled + + + + + + + UI Theme + + + + + + + + 200 + 0 + + + + + + + + Language + + + + + + + + 0 + 0 + + + + + 100 + 0 + + + + + + + + Maximum log lines + + + + + + + + 0 + 0 + + + + true + + + lines + + + 10 + + + 2500 + + + 200 + + + + + + + + + + Behavior + + + + + + Launch at Login + + + + + + + Enabled + + + + + + + Transparent Proxy + + + + + + + Enabled + + + + + + + Auto Connect + + + + + + + + + Group + + + + + + + + 180 + 0 + + + + + + + + + + + + + Connection + + + + + + + + + + + + + + + Network Settings + + + + + + Use Custom Proxy + + + + + + + Enabled + + + + + + + Custom Proxy Server + + + + + + + + + + + + : + + + Qt::AlignCenter + + + + + + + 0 + + + 65535 + + + + + + + + + + http + + + + + socks + + + + + + + + Proxy Type + + + + + + + User-Agent + + + + + + + Qv2ray/2 NetworkRequestHelper + + + + + + + + + + + + Qt::Vertical - - true + + + 20 + 40 + - - - - 0 - 0 - 964 - 546 - - - - - - - - - General Settings - - - - - - - - - - - - 0 - 0 - - - - true - - - lines - - - 10 - - - 2500 - - - 200 - - - - - - - UI Theme - - - - - - - Darkmode UI Icons - - - - - - - QFrame::Plain - - - 2 - - - Qt::Vertical - - - - - - - Language - - - - - - - - 0 - 0 - - - - - 100 - 0 - - - - - - - - Darkmode Tray Icon - - - - - - - Enabled - - - - - - - Maximum log lines - - - - - - - Enabled - - - - - - - - 200 - 0 - - - - - - - - Enabled - - - - - - - Launch at Login - - - - - - - Transparent Proxy Support - - - - - - - Enabled - - - - - - - - - - - Connection - - - - - - - Group/Subscription - - - - - - - - - - - 180 - 0 - - - - - - - - - - - - - QFrame::Plain - - - 2 - - - Qt::Vertical - - - - - - - - - - - V2ray Settings - - - - - - - - - - - Check V2ray Core Settings - - - - - - - Core Executable Path - - - - - - - - - - - - - - - - - Select - - - - - - - V2ray Assets Directory - - - - - - - Select - - - - - - - Log Level - - - - - - - - 0 - 0 - - - - - 150 - 0 - - - - - none - - - - - debug - - - - - info - - - - - warning - - - - - error - - - - - - - - QFrame::Plain - - - 2 - - - Qt::Vertical - - - - - - - - - - - API Subsystem - - - - - - - - - - - API Port - - - - - - - Status - - - - - - - - 116 - 30 - - - - 1024 - - - 65535 - - - 15934 - - - - - - - Enabled - - - - - - - QFrame::Plain - - - 2 - - - Qt::Vertical - - - - - - - - - Qt::Vertical + + + + + + + Kernel Settings + + + + + + V2ray Settings + + + + + + Log Level + + + + + + + + 0 + 0 + + + + + 150 + 0 + + + + + none - - - 20 - 40 - + + + + debug - - - - - - - - Auto Connect - - - - - - - + + + + info + + + + + warning + + + + + error + + + + + + + + Core Executable Path + + + + + + + + + + + + + + + + Select + + + + + + + + + V2ray Assets Directory + + + + + + + + + + + + Select + + + + + + + + + API SubSystem + + + + + + + Enabled + + + + + + + API Port + + + + + + + + 116 + 30 + + + + 1024 + + + 65535 + + + 15934 + + + + + + + Check V2ray Core Settings + + + + + + + + Plugin Kernel Settings + + + + + + Enabling V2ray Integration will allow the kernel benefit from the V2ray routing engine. + + + + + + + V2ray Integration + + + + + + + If not checked, these features will be disabled: + +Advanced Routing Settings +Bypass CN websites and IPs +Direct connection of Local LAN addresses +Custom DNS Settings + + + Enabled + + + + + + + Qv2ray will allocate ports, for HTTP and SOCKS respectively, if enabled, for each kernel plugin. + + + + + + + Port Allocation Start + + + + + + + 1 + + + 65535 + + + 13000 + + + + + + + + + + Qt::Vertical + + + + 20 + 106 + + + + @@ -490,7 +593,7 @@ 0 0 - 964 + 844 603 @@ -735,7 +838,7 @@ - All settings below will only be applied onto simple connection. + All settings below will only be applied on simple connection. @@ -1100,119 +1203,6 @@ - - - Plugin Settings - - - - - - QFrame::NoFrame - - - true - - - - - 0 - 0 - 964 - 506 - - - - - - - Kernel Plugin Behavior - - - - - - Enabling V2ray Integration will allow the kernel benefit from the V2ray routing engine. -Otherwise, these features will be disabled: - -Advanced Routing Settings -Bypass CN websites and IPs -Direct connection of Local LAN addresses -Custom DNS Settings - - - - - - - - - V2ray Integration - - - - - - - Enabled - - - - - - - - - Qv2ray will allocate ports, for HTTP and SOCKS respectively, if enabled, for each kernel plugin. - - - - - - - - - Port Allocation Start - - - - - - - 1 - - - 65535 - - - 13000 - - - - - - - - - Qt::Vertical - - - - 20 - 299 - - - - - - - - - - - - - true @@ -1233,7 +1223,7 @@ Custom DNS Settings Items - + @@ -1275,7 +1265,7 @@ Custom DNS Settings - Page Y Offset + Page Y Axis Offset @@ -1396,6 +1386,18 @@ Custom DNS Settings + + 0 + + + 0 + + + 0 + + + 0 + @@ -1483,67 +1485,67 @@ Custom DNS Settings - - + + A: - - - - 255 - - - - - - - R: - - - - - - - 255 - - - - + G: - + + + + 255 + + + + 255 - - - - B: - - - - + 255 - - + + + + 255 + + + + + - ... + R: + + + + + + + Select Color + + + + + + + B: @@ -1560,14 +1562,7 @@ Custom DNS Settings - - - Apply Network Speed Bar UI Settings - - - - - + Qt::Vertical @@ -1579,6 +1574,13 @@ Custom DNS Settings + + + + Apply Network Speed Bar UI Settings + + + @@ -1623,332 +1625,297 @@ Custom DNS Settings - - - - - - - - - - 24 - - - - Qv2ray - - - - - - - - - - 15 - - + + + + + - Version: + Ignored Version - - - - - 15 - - - - IBeamCursor - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - Plugin Interface - - - - - - - - 0 - 0 - - - - <html><head/><body><p><a href="https://github.com/Qv2ray/Qv2ray"><span style="text-decoration: underline; color:#2980b9;">https://github.com/Qv2ray/Qv2ray</span></a></p></body></html> - - - Qt::RichText - - - false - - - true - - - - - - - Extra Build Info - - - - - + + - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - + - - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - - - - - - - - - 0 - 0 - - - - - 10 - - - - <html><head/><body><p><a href="https://www.gnu.org/licenses/gpl-3.0.txt"><span style="text-decoration: underline; color:#2980b9;">GPLv3 (https://www.gnu.org/licenses/gpl-3.0.txt)</span></a></p></body></html> - - - Qt::RichText - - - false - - - true + Cancel - + - Built Time + Update Channel - - - - Build Info - - - - - - - Official Repo - - - - - - - - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - License - + + + + + Stable Release + + + + + Testing + + - - - - - QTextEdit::NoWrap - - - true - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> + + + + + + + + + + + 26 + + + + Qv2ray + + + + + + + + + + 14 + + + + Version: + + + + + + + + 14 + + + + IBeamCursor + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Plugin Interface + + + + + + + + 0 + 0 + + + + <html><head/><body><p><a href="https://github.com/Qv2ray/Qv2ray"><span style="text-decoration: underline; color:#2980b9;">https://github.com/Qv2ray/Qv2ray</span></a></p></body></html> + + + Qt::RichText + + + false + + + true + + + + + + + Extra Build Info + + + + + + + + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + + + + + + + + + 0 + 0 + + + + + 10 + + + + <html><head/><body><p><a href="https://www.gnu.org/licenses/gpl-3.0.txt"><span style="text-decoration: underline; color:#2980b9;">GPLv3 (https://www.gnu.org/licenses/gpl-3.0.txt)</span></a></p></body></html> + + + Qt::RichText + + + false + + + true + + + + + + + Built Time + + + + + + + Build Info + + + + + + + Official Repo + + + + + + + + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + License + + + + + + + + + QTextEdit::NoWrap + + + true + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'WenQuanYi Micro Hei'; font-size:10pt; font-weight:400; font-style:normal;"> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> - - - - - - - - - - Update Settings - - - - - - Update Channel - - - - - - - Ignore Next Version - - - - - - - - - Cancel - - - - - - - IBeamCursor - - - - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - - - - Stable Release - - - - - Testing - - - - - - - - - - - - 206 - 0 - - - - About Qt - - - - - + + + + + + + + + + + 206 + 0 + + + + About Qt + + + + - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - tabWidget - scrollArea darkThemeCB darkTrayCB themeCombo languageComboBox - startWithLoginCB - tProxyCheckBox - maxLogLinesSB autoStartSubsCombo autoStartConnCombo logLevelComboBox - vCorePathTxt - selectVCoreBtn - vCoreAssetsPathTxt - selectVAssetBtn checkVCoreSettings - enableAPI - statsPortBox scrollArea_2 listenIPTxt setSysProxyCB @@ -2001,15 +1968,7 @@ p, li { white-space: pre-wrap; } nsBarFontItalicCB nsBarFontSizeSB nsBarFontASB - nsBarFontRSB - nsBarFontGSB - nsBarFontBSB - chooseColorBtn applyNSBarSettingsBtn - textBrowser - updateChannelCombo - cancelIgnoreVersionBtn - aboutQt diff --git a/src/ui/widgets/RouteSettingsMatrix.ui b/src/ui/widgets/RouteSettingsMatrix.ui index c8094b5b..9fd91a20 100644 --- a/src/ui/widgets/RouteSettingsMatrix.ui +++ b/src/ui/widgets/RouteSettingsMatrix.ui @@ -6,8 +6,8 @@ 0 0 - 600 - 409 + 582 + 404 @@ -45,93 +45,84 @@ - - - Route Settings + + + Lines start with "geoip:" or "geosite:" will have its autocompletion from geoip.dat and geosite.dat - - - - - Lines start with "geoip:" or "geosite:" will have its autocompletion from geoip.dat and geosite.dat - - - - - - - - - Block - - - Qt::AlignCenter - - - - - - - - - - Direct - - - Qt::AlignCenter - - - - - - - Domain - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - - - Proxy - - - Qt::AlignCenter - - - - - - - - - - - - - IP - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - + + + + + + Block + + + Qt::AlignCenter + + + + + + + + + + Direct + + + Qt::AlignCenter + + + + + + + Domain + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + + + Proxy + + + Qt::AlignCenter + + + + + + + + + + + + + IP + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + From 7924a562aa33f1ef739c2da6fa1e04d0c7b2375c Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Sun, 12 Apr 2020 18:02:02 +0800 Subject: [PATCH 72/92] add: added capabilities to resolve DNS via SOCKS5 proxy --- src/common/HTTPRequestHelper.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/common/HTTPRequestHelper.cpp b/src/common/HTTPRequestHelper.cpp index 04797596..fee30437 100644 --- a/src/common/HTTPRequestHelper.cpp +++ b/src/common/HTTPRequestHelper.cpp @@ -38,10 +38,14 @@ namespace Qv2ray::common } else { - LOG(MODULE_NETWORK, "Get without proxy.") + DEBUG(MODULE_NETWORK, "Get without proxy.") accessManager.setProxy(QNetworkProxy(QNetworkProxy::ProxyType::NoProxy)); } - + if (accessManager.proxy().type() == QNetworkProxy::Socks5Proxy) + { + DEBUG(MODULE_NETWORK, "Adding HostNameLookupCapability to proxy.") + accessManager.proxy().setCapabilities(accessManager.proxy().capabilities() | QNetworkProxy::HostNameLookupCapability); + } request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy); request.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, true); request.setHeader(QNetworkRequest::KnownHeaders::UserAgentHeader, GlobalConfig.networkConfig.userAgent); @@ -70,9 +74,14 @@ namespace Qv2ray::common } else { - LOG(MODULE_NETWORK, "Get without proxy.") + DEBUG(MODULE_NETWORK, "Get without proxy.") accessManager.setProxy(QNetworkProxy(QNetworkProxy::ProxyType::NoProxy)); } + if (accessManager.proxy().type() == QNetworkProxy::Socks5Proxy) + { + DEBUG(MODULE_NETWORK, "Adding HostNameLookupCapability to proxy.") + accessManager.proxy().setCapabilities(accessManager.proxy().capabilities() | QNetworkProxy::HostNameLookupCapability); + } request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy); request.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, true); request.setHeader(QNetworkRequest::KnownHeaders::UserAgentHeader, GlobalConfig.networkConfig.userAgent); From 9963185132cd414e79bcd57dd9d516ee982502cc Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Sun, 12 Apr 2020 20:13:15 +0800 Subject: [PATCH 73/92] fix: default page index --- makespec/BUILDVERSION | 2 +- src/ui/w_PreferencesWindow.ui | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 8f08884b..87d9fa61 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5244 +5245 diff --git a/src/ui/w_PreferencesWindow.ui b/src/ui/w_PreferencesWindow.ui index 61839397..b17ffb97 100644 --- a/src/ui/w_PreferencesWindow.ui +++ b/src/ui/w_PreferencesWindow.ui @@ -33,7 +33,7 @@ - 6 + 0 From ea2d7dcb5747c414f5ab198eb3dc5f3fec1e4ce1 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Sun, 12 Apr 2020 22:28:04 +0800 Subject: [PATCH 74/92] update: update submodule --- src/components/plugins/interface | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/plugins/interface b/src/components/plugins/interface index c7c435ba..1fede69e 160000 --- a/src/components/plugins/interface +++ b/src/components/plugins/interface @@ -1 +1 @@ -Subproject commit c7c435ba060c753900ae279cc8156814c64478b6 +Subproject commit 1fede69e7ff553b31e59f6e13cc40d4aeeee21d9 From 85c865c950dda9fa5da7aef4bc3767a9754f5249 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Sun, 12 Apr 2020 22:39:58 +0800 Subject: [PATCH 75/92] fix: added UI for #506 #491 #451 --- src/base/models/QvSettingsObject.hpp | 10 ++++ src/components/plugins/QvPluginHost.cpp | 1 + src/ui/w_ImportConfig.cpp | 12 ++--- src/ui/w_PreferencesWindow.cpp | 19 ++++++- src/ui/w_PreferencesWindow.hpp | 4 ++ src/ui/w_PreferencesWindow.ui | 71 +++++++++++++++++++++++-- 6 files changed, 106 insertions(+), 11 deletions(-) diff --git a/src/base/models/QvSettingsObject.hpp b/src/base/models/QvSettingsObject.hpp index 155e6ce9..db3c6e8c 100644 --- a/src/base/models/QvSettingsObject.hpp +++ b/src/base/models/QvSettingsObject.hpp @@ -214,6 +214,13 @@ namespace Qv2ray::base::config XTOSTRUCT(O(ignoredVersion, updateChannel)) }; + struct Qv2rayAdvancedConfig + { + bool setAllowInsecure; + bool testLatencyPeriodcally; + XTOSTRUCT(O(setAllowInsecure, testLatencyPeriodcally)) + }; + struct Qv2rayNetworkConfig { bool useCustomProxy; @@ -247,6 +254,7 @@ namespace Qv2ray::base::config Qv2rayNetworkConfig networkConfig; Qv2rayToolBarConfig toolBarConfig; Qv2rayInboundsConfig inboundConfig; + Qv2rayAdvancedConfig advancedConfig; Qv2rayConnectionConfig connectionConfig; Qv2rayConfig() @@ -265,6 +273,7 @@ namespace Qv2ray::base::config networkConfig(), // toolBarConfig(), // inboundConfig(), // + advancedConfig(), // connectionConfig() { } @@ -284,6 +293,7 @@ namespace Qv2ray::base::config inboundConfig, // connectionConfig, // toolBarConfig, // + advancedConfig, // apiConfig // )) }; diff --git a/src/components/plugins/QvPluginHost.cpp b/src/components/plugins/QvPluginHost.cpp index 6d5a2ef5..c1861d7c 100644 --- a/src/components/plugins/QvPluginHost.cpp +++ b/src/components/plugins/QvPluginHost.cpp @@ -120,6 +120,7 @@ namespace Qv2ray::components::plugins { // Load plugin if it haven't been loaded. InitializePlugin(internalName); + QvMessageBoxInfo(nullptr, tr("Enabling a plugin"), tr("The plugin will become fully functional after restarting Qv2ray.")); } } diff --git a/src/ui/w_ImportConfig.cpp b/src/ui/w_ImportConfig.cpp index 7da70f5e..95f6d991 100644 --- a/src/ui/w_ImportConfig.cpp +++ b/src/ui/w_ImportConfig.cpp @@ -56,7 +56,7 @@ QMultiHash ImportConfigWindow::SelectConnection(bool outbou routeEditBtn->setEnabled(!outboundsOnly); this->exec(); QMultiHash conn; - for (const auto connEntry : connections.values()) + for (const auto &connEntry : connections.values()) { conn += connEntry; } @@ -67,11 +67,11 @@ int ImportConfigWindow::ImportConnection() { this->exec(); int count = 0; - for (const auto groupName : connections.keys()) + for (const auto &groupName : connections.keys()) { GroupId groupId = groupName.isEmpty() ? DefaultGroupId : ConnectionManager->CreateGroup(groupName, false); const auto groupObject = connections[groupName]; - for (const auto connConf : groupObject) + for (const auto &connConf : groupObject) { auto connName = groupObject.key(connConf); @@ -167,7 +167,7 @@ void ImportConfigWindow::on_beginImportBtn_clicked() } else { - for (auto conf : config) + for (const auto &conf : config) { AddToGroup(newGroupName, config.key(conf), conf); } @@ -176,7 +176,7 @@ void ImportConfigWindow::on_beginImportBtn_clicked() if (!linkErrors.isEmpty()) { - for (auto item : linkErrors) + for (const auto &item : linkErrors) { vmessConnectionStringTxt->appendPlainText(linkErrors.key(item)); errorsList->addItem(item); @@ -288,7 +288,7 @@ void ImportConfigWindow::on_cancelImportBtn_clicked() void ImportConfigWindow::on_subscriptionButton_clicked() { hide(); - SubscriptionEditor w; + SubscriptionEditor w(this); w.exec(); auto importToComplex = !keepImportedInboundCheckBox->isEnabled(); connections.clear(); diff --git a/src/ui/w_PreferencesWindow.cpp b/src/ui/w_PreferencesWindow.cpp index d375f16d..5ab4b16b 100644 --- a/src/ui/w_PreferencesWindow.cpp +++ b/src/ui/w_PreferencesWindow.cpp @@ -137,6 +137,10 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent), Current qvNetworkUATxt->setText(CurrentConfig.networkConfig.userAgent); qvUseProxyCB->setChecked(CurrentConfig.networkConfig.useCustomProxy); // + // Advanced config. + setAllowInsecureCB->setChecked(CurrentConfig.advancedConfig.setAllowInsecure); + setTestLatenctCB->setChecked(CurrentConfig.advancedConfig.testLatencyPeriodcally); + // DNSListTxt->clear(); for (auto dnsStr : CurrentConfig.connectionConfig.dnsList) { @@ -1145,7 +1149,20 @@ void PreferencesWindow::on_qvNetworkUATxt_textEdited(const QString &arg1) void PreferencesWindow::on_qvUseProxyCB_stateChanged(int arg1) { - LOADINGCHECK CurrentConfig.networkConfig.useCustomProxy = arg1 == Qt::Checked; } + +void PreferencesWindow::on_setAllowInsecureCB_stateChanged(int arg1) +{ + LOADINGCHECK + QvMessageBoxWarn(this, tr("Dangerous Operation"), tr("You may under MITM attack, which is just what TLS is protective for.")); + CurrentConfig.advancedConfig.setAllowInsecure = arg1 == Qt::Checked; +} + +void PreferencesWindow::on_setTestLatenctCB_stateChanged(int arg1) +{ + LOADINGCHECK + QvMessageBoxWarn(this, tr("Dangerous Operation"), tr("This will (probably) makes it easy to fingerprint your connection.")); + CurrentConfig.advancedConfig.testLatencyPeriodcally = arg1 == Qt::Checked; +} diff --git a/src/ui/w_PreferencesWindow.hpp b/src/ui/w_PreferencesWindow.hpp index 7ead3d9a..62e4c1ad 100644 --- a/src/ui/w_PreferencesWindow.hpp +++ b/src/ui/w_PreferencesWindow.hpp @@ -170,6 +170,10 @@ class PreferencesWindow void on_qvUseProxyCB_stateChanged(int arg1); + void on_setAllowInsecureCB_stateChanged(int arg1); + + void on_setTestLatenctCB_stateChanged(int arg1); + private: // RouteSettingsMatrixWidget *routeSettingsWidget; diff --git a/src/ui/w_PreferencesWindow.ui b/src/ui/w_PreferencesWindow.ui index b17ffb97..819f0cb3 100644 --- a/src/ui/w_PreferencesWindow.ui +++ b/src/ui/w_PreferencesWindow.ui @@ -9,8 +9,8 @@ 0 0 - 890 - 617 + 840 + 566 @@ -316,7 +316,70 @@ - Qv2ray/2 NetworkRequestHelper + + + + + + + + + + + Advanced Behavior + + + + + + AllowInsecure By Default + + + + + + + Enable "AllowInsecure" settings for all connections when importing. +This could resolve the certificate issues, but also could let one performing TLS MITM attack. + + + Enabled + + + + + + + Test Latency Periodcally + + + + + + + Run TCPing or ICMPing periodcally after connecting to a server. +Qv2ray will give a more accurate latency value if Enabled, but makes it easy to fingerprint the connection. + + + Enabled + + + + + + + + 50 + true + false + + + + color: red; + + + These settings may be useful. +But could damage your server if improperly used. @@ -593,7 +656,7 @@ Custom DNS Settings 0 0 - 844 + 794 603 From 9001f3fad8fc6aaaabbf2378dcd4f271a6ba7b5d Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Mon, 13 Apr 2020 09:44:12 +0800 Subject: [PATCH 76/92] add: update macOS DMG background --- assets/CMakeDMGBackground.tif | Bin 95690 -> 2336942 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/assets/CMakeDMGBackground.tif b/assets/CMakeDMGBackground.tif index 91c4b1309be02edcd057985b52355b2ed1168424..6e3eb7dd8603d0426324de8046fdd26fbeedf085 100644 GIT binary patch literal 2336942 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 literal 95690 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 D+gpN` From af6ef87023bde5292f76674b3adec4d52bf353de Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Mon, 13 Apr 2020 10:03:51 +0800 Subject: [PATCH 77/92] add: update DMG background settings --- assets/CMakeDMGBackground.png | Bin 0 -> 21019 bytes cmake/deployment.cmake | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 assets/CMakeDMGBackground.png diff --git a/assets/CMakeDMGBackground.png b/assets/CMakeDMGBackground.png new file mode 100644 index 0000000000000000000000000000000000000000..28f3d3ed7657a08db11daeabf48d2a591f5e9286 GIT binary patch literal 21019 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 literal 0 HcmV?d00001 diff --git a/cmake/deployment.cmake b/cmake/deployment.cmake index 49a4e2cf..9c76eb02 100644 --- a/cmake/deployment.cmake +++ b/cmake/deployment.cmake @@ -67,7 +67,7 @@ if(APPLE) set(CPACK_DMG_DS_STORE "${CMAKE_SOURCE_DIR}/assets/DS_Store") endif() - set(CPACK_DMG_BACKGROUND_IMAGE "${CMAKE_SOURCE_DIR}/assets/CMakeDMGBackground.tif") + set(CPACK_DMG_BACKGROUND_IMAGE "${CMAKE_SOURCE_DIR}/assets/CMakeDMGBackground.png") endif() include(CPack) From b16140c879c1fd85e6a42a21d7b08479c9fa7850 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Mon, 13 Apr 2020 10:46:35 +0800 Subject: [PATCH 78/92] add: update DMG background settings 2 --- cmake/CMakeDMGSetup.scpt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/CMakeDMGSetup.scpt b/cmake/CMakeDMGSetup.scpt index 3789fb69..2ec34563 100644 --- a/cmake/CMakeDMGSetup.scpt +++ b/cmake/CMakeDMGSetup.scpt @@ -24,7 +24,7 @@ on run argv open set current view of container window to icon view set theViewOptions to the icon view options of container window - set background picture of theViewOptions to file ".background:background.tif" + set background picture of theViewOptions to file ".background:background.png" set arrangement of theViewOptions to not arranged set icon size of theViewOptions to 128 delay 5 @@ -54,4 +54,4 @@ on run argv end tell delay 1 end tell -end run \ No newline at end of file +end run From cca5a56643aeb6beeb1adafc0b884e8a0f214fa1 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Mon, 13 Apr 2020 16:03:29 +0800 Subject: [PATCH 79/92] add: test adding allowInsecure allowInsecureCipher --- src/base/models/CoreObjectModels.hpp | 5 +++-- src/base/models/QvSettingsObject.hpp | 3 ++- src/core/connection/Serialization.cpp | 16 ++++++++++++++++ src/core/handler/ConfigHandler.cpp | 9 ++++----- src/ui/w_PreferencesWindow.cpp | 21 +++++++++++++++++++-- src/ui/w_PreferencesWindow.hpp | 2 ++ src/ui/w_PreferencesWindow.ui | 18 ++++++++++++++++-- src/ui/widgets/StreamSettingsWidget.cpp | 12 +++++++++--- src/ui/widgets/StreamSettingsWidget.hpp | 4 +++- src/ui/widgets/StreamSettingsWidget.ui | 20 ++++++++++---------- 10 files changed, 84 insertions(+), 26 deletions(-) diff --git a/src/base/models/CoreObjectModels.hpp b/src/base/models/CoreObjectModels.hpp index 14d86f04..3c4e5a68 100644 --- a/src/base/models/CoreObjectModels.hpp +++ b/src/base/models/CoreObjectModels.hpp @@ -257,13 +257,14 @@ namespace Qv2ray::base::objects { QString serverName; bool allowInsecure; + bool allowInsecureCiphers; QList alpn; QList certificates; bool disableSystemRoot; - TLSObject() : serverName(), allowInsecure(), certificates(), disableSystemRoot() + TLSObject() : serverName(), allowInsecure(), allowInsecureCiphers(), certificates(), disableSystemRoot() { } - XTOSTRUCT(O(serverName, allowInsecure, alpn, certificates, disableSystemRoot)) + XTOSTRUCT(O(serverName, allowInsecure, allowInsecureCiphers, alpn, certificates, disableSystemRoot)) }; } // namespace transfer // diff --git a/src/base/models/QvSettingsObject.hpp b/src/base/models/QvSettingsObject.hpp index db3c6e8c..6c216d86 100644 --- a/src/base/models/QvSettingsObject.hpp +++ b/src/base/models/QvSettingsObject.hpp @@ -217,8 +217,9 @@ namespace Qv2ray::base::config struct Qv2rayAdvancedConfig { bool setAllowInsecure; + bool setAllowInsecureCiphers; bool testLatencyPeriodcally; - XTOSTRUCT(O(setAllowInsecure, testLatencyPeriodcally)) + XTOSTRUCT(O(setAllowInsecure, setAllowInsecureCiphers, testLatencyPeriodcally)) }; struct Qv2rayNetworkConfig diff --git a/src/core/connection/Serialization.cpp b/src/core/connection/Serialization.cpp index 56a3445f..b9aa8908 100644 --- a/src/core/connection/Serialization.cpp +++ b/src/core/connection/Serialization.cpp @@ -16,6 +16,22 @@ namespace Qv2ray::core::connection if (link.startsWith("vmess://")) { auto conf = ConvertConfigFromVMessString(link, prefix, errMessage); + // + if (GlobalConfig.advancedConfig.setAllowInsecureCiphers || GlobalConfig.advancedConfig.setAllowInsecure) + { + auto outbound = conf["outbounds"].toArray().first().toObject(); + auto streamSettings = outbound["streamSettings"].toObject(); + auto tlsSettings = streamSettings["tlsSettings"].toObject(); + tlsSettings["allowInsecure"] = GlobalConfig.advancedConfig.setAllowInsecure; + tlsSettings["allowInsecureCiphers"] = GlobalConfig.advancedConfig.setAllowInsecureCiphers; + streamSettings["tlsSettings"] = tlsSettings; + outbound["streamSettings"] = streamSettings; + // + auto outbounds = conf["outbounds"].toArray(); + outbounds[0] = outbound; + conf["outbounds"] = outbounds; + } + // connectionConf.insert(*prefix, conf); } else if (link.startsWith("ss://")) diff --git a/src/core/handler/ConfigHandler.cpp b/src/core/handler/ConfigHandler.cpp index ae39900a..9771bba0 100644 --- a/src/core/handler/ConfigHandler.cpp +++ b/src/core/handler/ConfigHandler.cpp @@ -13,7 +13,7 @@ namespace Qv2ray::core::handlers DEBUG(MODULE_CORE_HANDLER, "ConnectionHandler Constructor.") // Do we need to check how many of them are loaded? - // Do not use: for (const auto &key : connections) + // Do not use: for (const auto &key : connections), why? for (auto i = 0; i < GlobalConfig.connections.count(); i++) { auto const &id = ConnectionId(GlobalConfig.connections.keys().at(i)); @@ -87,10 +87,9 @@ namespace Qv2ray::core::handlers httpHelper = new QvHttpRequestHelper(this); connect(tcpingHelper, &QvTCPingHelper::OnLatencyTestCompleted, this, &QvConfigHandler::OnLatencyDataArrived_p); // - // Save per 2 minutes. - saveTimerId = startTimer(2 * 60 * 1000); + // Save per 1 minutes. + saveTimerId = startTimer(1 * 60 * 1000); // Do not ping all... - // pingAllTimerId = startTimer(5 * 60 * 1000); pingConnectionTimerId = startTimer(60 * 1000); } @@ -141,7 +140,7 @@ namespace Qv2ray::core::handlers else if (event->timerId() == pingConnectionTimerId) { auto id = kernelHandler->CurrentConnection(); - if (id != NullConnectionId) + if (id != NullConnectionId && GlobalConfig.advancedConfig.testLatencyPeriodcally) { StartLatencyTest(id); } diff --git a/src/ui/w_PreferencesWindow.cpp b/src/ui/w_PreferencesWindow.cpp index 5ab4b16b..f28f0867 100644 --- a/src/ui/w_PreferencesWindow.cpp +++ b/src/ui/w_PreferencesWindow.cpp @@ -139,6 +139,7 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent), Current // // Advanced config. setAllowInsecureCB->setChecked(CurrentConfig.advancedConfig.setAllowInsecure); + setAllowInsecureCiphersCB->setChecked(CurrentConfig.advancedConfig.setAllowInsecureCiphers); setTestLatenctCB->setChecked(CurrentConfig.advancedConfig.testLatencyPeriodcally); // DNSListTxt->clear(); @@ -1156,13 +1157,29 @@ void PreferencesWindow::on_qvUseProxyCB_stateChanged(int arg1) void PreferencesWindow::on_setAllowInsecureCB_stateChanged(int arg1) { LOADINGCHECK - QvMessageBoxWarn(this, tr("Dangerous Operation"), tr("You may under MITM attack, which is just what TLS is protective for.")); + if (arg1 == Qt::Checked) + { + QvMessageBoxWarn(this, tr("Dangerous Operation"), tr("You will lose the advantage of TLS and make your connection under MITM attack.")); + } CurrentConfig.advancedConfig.setAllowInsecure = arg1 == Qt::Checked; } void PreferencesWindow::on_setTestLatenctCB_stateChanged(int arg1) { LOADINGCHECK - QvMessageBoxWarn(this, tr("Dangerous Operation"), tr("This will (probably) makes it easy to fingerprint your connection.")); + if (arg1 == Qt::Checked) + { + QvMessageBoxWarn(this, tr("Dangerous Operation"), tr("This will (probably) makes it easy to fingerprint your connection.")); + } CurrentConfig.advancedConfig.testLatencyPeriodcally = arg1 == Qt::Checked; } + +void PreferencesWindow::on_setAllowInsecureCiphersCB_stateChanged(int arg1) +{ + LOADINGCHECK + if (arg1 == Qt::Checked) + { + QvMessageBoxWarn(this, tr("Dangerous Operation"), tr("You will lose the advantage of TLS and make your connection under MITM attack.")); + } + CurrentConfig.advancedConfig.setAllowInsecureCiphers = arg1 == Qt::Checked; +} diff --git a/src/ui/w_PreferencesWindow.hpp b/src/ui/w_PreferencesWindow.hpp index 62e4c1ad..fc5dec20 100644 --- a/src/ui/w_PreferencesWindow.hpp +++ b/src/ui/w_PreferencesWindow.hpp @@ -174,6 +174,8 @@ class PreferencesWindow void on_setTestLatenctCB_stateChanged(int arg1); + void on_setAllowInsecureCiphersCB_stateChanged(int arg1); + private: // RouteSettingsMatrixWidget *routeSettingsWidget; diff --git a/src/ui/w_PreferencesWindow.ui b/src/ui/w_PreferencesWindow.ui index 819f0cb3..98720403 100644 --- a/src/ui/w_PreferencesWindow.ui +++ b/src/ui/w_PreferencesWindow.ui @@ -347,14 +347,14 @@ This could resolve the certificate issues, but also could let one performing TLS - + Test Latency Periodcally - + Run TCPing or ICMPing periodcally after connecting to a server. @@ -383,6 +383,20 @@ But could damage your server if improperly used. + + + + AllowInsecureCiphers By Default + + + + + + + Enabled + + + diff --git a/src/ui/widgets/StreamSettingsWidget.cpp b/src/ui/widgets/StreamSettingsWidget.cpp index a0f1825c..eea7fa3e 100644 --- a/src/ui/widgets/StreamSettingsWidget.cpp +++ b/src/ui/widgets/StreamSettingsWidget.cpp @@ -22,7 +22,7 @@ QvMessageBusSlotImpl(StreamSettingsWidget) } } -StreamSettingsObject StreamSettingsWidget::GetStreamSettings() +StreamSettingsObject StreamSettingsWidget::GetStreamSettings() const { return stream; } @@ -36,6 +36,7 @@ void StreamSettingsWidget::SetStreamObject(const StreamSettingsObject &sso) tlsCB->setChecked(stream.security == "tls"); serverNameTxt->setText(stream.tlsSettings.serverName); allowInsecureCB->setChecked(stream.tlsSettings.allowInsecure); + allowInsecureCiphersCB->setChecked(stream.tlsSettings.allowInsecureCiphers); alpnTxt->setPlainText(stream.tlsSettings.alpn.join(NEWLINE)); // TCP tcpHeaderTypeCB->setCurrentText(stream.tcpSettings.header.type); @@ -47,9 +48,9 @@ void StreamSettingsWidget::SetStreamObject(const StreamSettingsObject &sso) // WS wsPathTxt->setText(stream.wsSettings.path); QString wsHeaders; - for (auto index = 0; index < stream.wsSettings.headers.count(); index++) + for (auto i = 0; i < stream.wsSettings.headers.count(); i++) { - wsHeaders = wsHeaders % stream.wsSettings.headers.keys().at(index) % "|" % stream.wsSettings.headers.values().at(index) % NEWLINE; + wsHeaders = wsHeaders % stream.wsSettings.headers.keys().at(i) % "|" % stream.wsSettings.headers.values().at(i) % NEWLINE; } wsHeadersTxt->setPlainText(wsHeaders); @@ -284,3 +285,8 @@ void StreamSettingsWidget::on_alpnTxt_textChanged() { stream.tlsSettings.alpn = SplitLines(alpnTxt->toPlainText()); } + +void StreamSettingsWidget::on_allowInsecureCiphersCB_stateChanged(int arg1) +{ + stream.tlsSettings.allowInsecureCiphers = arg1 == Qt::Checked; +} diff --git a/src/ui/widgets/StreamSettingsWidget.hpp b/src/ui/widgets/StreamSettingsWidget.hpp index 32c309bb..8393a9a4 100644 --- a/src/ui/widgets/StreamSettingsWidget.hpp +++ b/src/ui/widgets/StreamSettingsWidget.hpp @@ -14,7 +14,7 @@ class StreamSettingsWidget public: explicit StreamSettingsWidget(QWidget *parent = nullptr); void SetStreamObject(const StreamSettingsObject &sso); - StreamSettingsObject GetStreamSettings(); + StreamSettingsObject GetStreamSettings() const; private slots: void on_httpPathTxt_textEdited(const QString &arg1); @@ -77,6 +77,8 @@ class StreamSettingsWidget void on_alpnTxt_textChanged(); + void on_allowInsecureCiphersCB_stateChanged(int arg1); + private: QvMessageBusSlotDecl; StreamSettingsObject stream; diff --git a/src/ui/widgets/StreamSettingsWidget.ui b/src/ui/widgets/StreamSettingsWidget.ui index 66620515..0b401485 100644 --- a/src/ui/widgets/StreamSettingsWidget.ui +++ b/src/ui/widgets/StreamSettingsWidget.ui @@ -635,37 +635,37 @@ - + Server - + - + ALPN - + - - + + - TLS + Enable TLS - - + + - Enabled + Allow Insecure Ciphers From 7866c1a2901678c5541f9804a6e564e64c467579 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Mon, 13 Apr 2020 19:30:20 +0800 Subject: [PATCH 80/92] Update QvProxyConfigurator.cpp --- src/components/proxy/QvProxyConfigurator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/proxy/QvProxyConfigurator.cpp b/src/components/proxy/QvProxyConfigurator.cpp index 64fb6d4f..7de750cd 100644 --- a/src/components/proxy/QvProxyConfigurator.cpp +++ b/src/components/proxy/QvProxyConfigurator.cpp @@ -212,7 +212,7 @@ namespace Qv2ray::components::proxy } #ifdef Q_OS_WIN - QString __a = (hasHTTP ? "http://" : "socks5://") + address + ":" + QSTRN(httpPort); + QString __a = (hasHTTP ? "http://" : "socks5://") + address + ":" + QSTRN(hasHTTP ? httpPort : socksPort); LOG(MODULE_PROXY, "Windows proxy string: " + __a) auto proxyStrW = new WCHAR[__a.length() + 1]; From 761961f44b69f9830daa4ffeb83fcad2266e9371 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Tue, 14 Apr 2020 12:15:18 +0800 Subject: [PATCH 81/92] Update main.cpp --- src/main.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 56e007e6..f642e15c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -410,10 +410,6 @@ int main(int argc, char *argv[]) // Initialise Connection Handler PluginHost = new QvPluginHost(); ConnectionManager = new QvConfigHandler(); - // Handler for session logout, shutdown, etc. - // Will not block. - QGuiApplication::setFallbackSessionManagementEnabled(false); - QObject::connect(&_qApp, &QGuiApplication::commitDataRequest, [] { LOG(MODULE_INIT, "Quit triggered by session manager.") }); // Show MainWindow MainWindow w; QObject::connect(&_qApp, &SingleApplication::instanceStarted, [&]() { From eba9641b873e98f525ea752546701ae6ab5fa1fb Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Tue, 14 Apr 2020 13:22:51 +0800 Subject: [PATCH 82/92] update: formatted CMakeLists.txt --- CMakeLists.txt | 508 +++++++++++++++++++++--------------------- makespec/BUILDVERSION | 2 +- 2 files changed, 255 insertions(+), 255 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index aa1b08c7..882b120b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,7 +42,7 @@ set(CMAKE_AUTOUIC ON) cmake_policy(SET CMP0071 NEW) if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.17.0") - cmake_policy(SET CMP0100 NEW) + cmake_policy(SET CMP0100 NEW) endif() message(" ") @@ -63,21 +63,21 @@ message("|-------------------------------------------------|") message(" ") if(WIN32) - add_compile_options("/std:c++17") - add_definitions(-DUNICODE -D_UNICODE) - add_definitions(-D_HAS_STD_BYTE=0 -D_WIN32_WINNT=0x600 -D_SCL_SECURE_NO_WARNINGS -D_CRT_SECURE_NO_WARNINGS) - set(GUI_TYPE WIN32) - if(NOT DEFINED CMAKE_TOOLCHAIN_FILE) - if(CMAKE_CL_64) - include(${CMAKE_SOURCE_DIR}/libs/x64-windows/scripts/buildsystems/vcpkg.cmake) - else() - include(${CMAKE_SOURCE_DIR}/libs/x86-windows/scripts/buildsystems/vcpkg.cmake) - endif() - endif() + add_compile_options("/std:c++17") + add_definitions(-DUNICODE -D_UNICODE) + add_definitions(-D_HAS_STD_BYTE=0 -D_WIN32_WINNT=0x600 -D_SCL_SECURE_NO_WARNINGS -D_CRT_SECURE_NO_WARNINGS) + set(GUI_TYPE WIN32) + if(NOT DEFINED CMAKE_TOOLCHAIN_FILE) + if(CMAKE_CL_64) + include(${CMAKE_SOURCE_DIR}/libs/x64-windows/scripts/buildsystems/vcpkg.cmake) + else() + include(${CMAKE_SOURCE_DIR}/libs/x86-windows/scripts/buildsystems/vcpkg.cmake) + endif() + endif() endif() if(UNIX AND NOT APPLE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") endif() set(QV2RAY_QNODEEDITOR_PROVIDER "module" CACHE STRING "qnodeeditor provider") @@ -120,185 +120,185 @@ set(QV2RAY_DEFAULT_VCORE_PATH "unset" CACHE STRING "v2ray core path") set(QV2RAY_TRANSLATION_PATH "unset" CACHE STRING "Qv2ray translations path") set(QV2RAY_DISABLE_AUTO_UPDATE OFF CACHE BOOL "Disable update checker") if(UNIX AND NOT APPLE) - set(QV2RAY_USE_BUILTIN_DARKTHEME OFF CACHE BOOL "Use built-in dark theme instead of followed by the system settings") + set(QV2RAY_USE_BUILTIN_DARKTHEME OFF CACHE BOOL "Use built-in dark theme instead of followed by the system settings") else() - set(QV2RAY_USE_BUILTIN_DARKTHEME ON CACHE BOOL "Use built-in dark theme instead of followed by the system settings") + set(QV2RAY_USE_BUILTIN_DARKTHEME ON CACHE BOOL "Use built-in dark theme instead of followed by the system settings") endif() set(EMBED_TRANSLATIONS OFF CACHE BOOL "Embed translations") if(QV2RAY_DEFAULT_VASSETS_PATH AND NOT QV2RAY_DEFAULT_VASSETS_PATH STREQUAL "unset") - add_definitions(-DQV2RAY_DEFAULT_VASSETS_PATH="${QV2RAY_DEFAULT_VASSETS_PATH}") + add_definitions(-DQV2RAY_DEFAULT_VASSETS_PATH="${QV2RAY_DEFAULT_VASSETS_PATH}") endif() if(QV2RAY_DEFAULT_VCORE_PATH AND NOT QV2RAY_DEFAULT_VCORE_PATH STREQUAL "unset") - add_definitions(-DQV2RAY_DEFAULT_VCORE_PATH="${QV2RAY_DEFAULT_VCORE_PATH}") + add_definitions(-DQV2RAY_DEFAULT_VCORE_PATH="${QV2RAY_DEFAULT_VCORE_PATH}") endif() if(QV2RAY_TRANSLATION_PATH AND NOT QV2RAY_TRANSLATION_PATH STREQUAL "unset") - add_definitions(-DQV2RAY_TRANSLATION_PATH="${QV2RAY_TRANSLATION_PATH}") + add_definitions(-DQV2RAY_TRANSLATION_PATH="${QV2RAY_TRANSLATION_PATH}") endif() if(QV2RAY_USE_BUILTIN_DARKTHEME) - add_definitions(-DQV2RAY_USE_BUILTIN_DARKTHEME=true) + add_definitions(-DQV2RAY_USE_BUILTIN_DARKTHEME=true) endif() if(QV2RAY_DISABLE_AUTO_UPDATE) - add_definitions(-DDISABLE_AUTO_UPDATE) + add_definitions(-DDISABLE_AUTO_UPDATE) endif() set(QVPLUGIN_INTERFACE_INCLUDE_DIR "src/components/plugins/interface") include(src/components/plugins/interface/QvPluginInterface.cmake) set(QV2RAY_SOURCES - ${QVPLUGIN_INTERFACE_HEADERS} - 3rdparty/libsemver/version.cpp - src/base/Qv2rayLog.cpp - src/common/CommandArgs.cpp - src/common/HTTPRequestHelper.cpp - src/common/LogHighlighter.cpp - src/common/JsonHighlighter.cpp - src/common/QJsonModel.cpp - src/common/QvHelpers.cpp - src/common/QvTranslator.cpp - src/common/QRCodeHelper.cpp - src/components/autolaunch/QvAutoLaunch.cpp - src/components/geosite/QvGeositeReader.cpp - src/components/latency/win/ICMPPinger.cpp - src/components/latency/QvTCPing.cpp - src/components/plugins/toolbar/QvToolbar.cpp - src/components/plugins/toolbar/QvToolbar_linux.cpp - src/components/plugins/toolbar/QvToolbar_win.cpp - src/components/plugins/QvPluginHost.cpp - src/components/proxy/QvProxyConfigurator.cpp - src/components/route/RouteSchemeIO.cpp - src/components/speedchart/speedplotview.cpp - src/components/speedchart/speedwidget.cpp - src/components/darkmode/DarkmodeDetector.cpp - src/components/update/UpdateChecker.cpp - src/core/connection/ConnectionIO.cpp - src/core/connection/Generation.cpp - src/core/connection/Serialization.cpp - src/core/connection/Serialization_ss.cpp - src/core/connection/Serialization_ssd.cpp - src/core/connection/Serialization_vmess.cpp - src/core/CoreUtils.cpp - src/core/handler/ConfigHandler.cpp - src/core/handler/KernelInstanceHandler.cpp - src/core/kernel/APIBackend.cpp - src/core/kernel/V2rayKernelInteractions.cpp - src/core/kernel/PluginKernelInteractions.cpp - src/core/kernel/QvKernelABIChecker.cpp - src/core/settings/SettingsBackend.cpp - src/core/settings/SettingsUpgrade.cpp - src/main.cpp - src/ui/editors/w_InboundEditor.cpp - src/ui/editors/w_JsonEditor.cpp - src/ui/editors/w_OutboundEditor.cpp - src/ui/editors/w_RoutesEditor.cpp - src/ui/editors/w_RoutesEditor_extra.cpp - src/ui/messaging/QvMessageBus.cpp - src/ui/models/InboundNodeModel.cpp - src/ui/models/OutboundNodeModel.cpp - src/ui/models/RuleNodeModel.cpp - src/ui/widgets/ConnectionInfoWidget.cpp - src/ui/widgets/ConnectionItemWidget.cpp - src/ui/widgets/QvAutoCompleteTextEdit.cpp - src/ui/widgets/StreamSettingsWidget.cpp - src/ui/widgets/RouteSettingsMatrix.cpp - src/ui/w_ImportConfig.cpp - src/ui/w_MainWindow.cpp - src/ui/w_MainWindow_extra.cpp - src/ui/w_PreferencesWindow.cpp - src/ui/w_PluginManager.cpp - src/ui/w_ScreenShot_Core.cpp - src/ui/w_SubscriptionManager.cpp - # ui files - src/ui/w_SubscriptionManager.ui - src/ui/editors/w_OutboundEditor.ui - src/ui/editors/w_InboundEditor.ui - src/ui/editors/w_JsonEditor.ui - src/ui/editors/w_RoutesEditor.ui - src/ui/w_ImportConfig.ui - src/ui/widgets/StreamSettingsWidget.ui - src/ui/widgets/ConnectionInfoWidget.ui - src/ui/widgets/ConnectionItemWidget.ui - src/ui/widgets/RouteSettingsMatrix.ui - src/ui/w_MainWindow.ui - src/ui/w_PreferencesWindow.ui - src/ui/w_PluginManager.ui - src/ui/w_ScreenShot_Core.ui - # headers - 3rdparty/libsemver/version.hpp - src/base/JsonHelpers.hpp - src/base/models/CoreObjectModels.hpp - src/base/models/QvConfigIdentifier.hpp - src/base/models/QvRuntimeConfig.hpp - src/base/models/QvSafeType.hpp - src/base/models/QvSettingsObject.hpp - src/base/models/QvStartupConfig.hpp - src/base/Qv2rayBase.hpp - src/base/Qv2rayFeatures.hpp - src/base/Qv2rayLog.hpp - src/common/CommandArgs.hpp - src/common/HTTPRequestHelper.hpp - src/common/JsonHighlighter.hpp - src/common/LogHighlighter.hpp - src/common/QJsonModel.hpp - src/common/QvHelpers.hpp - src/common/QvTranslator.hpp - src/common/QRCodeHelper.hpp - src/components/autolaunch/QvAutoLaunch.hpp - src/components/darkmode/DarkmodeDetector.hpp - src/components/geosite/QvGeositeReader.hpp - src/components/latency/win/ICMPPinger.hpp - src/components/latency/QvTCPing.hpp - src/components/plugins/toolbar/QvToolbar.hpp - src/components/plugins/QvPluginHost.hpp - src/components/proxy/QvProxyConfigurator.hpp - src/components/route/RouteSchemeIO.hpp - src/components/route/presets/RouteScheme_V2rayN.hpp - src/components/speedchart/speedplotview.hpp - src/components/speedchart/speedwidget.hpp - src/components/update/UpdateChecker.hpp - src/core/connection/ConnectionIO.hpp - src/core/connection/Generation.hpp - src/core/connection/Serialization.hpp - src/core/CoreSafeTypes.hpp - src/core/CoreUtils.hpp - src/core/handler/ConfigHandler.hpp - src/core/handler/KernelInstanceHandler.hpp - src/core/kernel/APIBackend.hpp - src/core/kernel/V2rayKernelInteractions.hpp - src/core/kernel/PluginKernelInteractions.hpp - src/core/kernel/QvKernelABIChecker.hpp - src/core/settings/SettingsBackend.hpp - src/ui/editors/w_InboundEditor.hpp - src/ui/editors/w_JsonEditor.hpp - src/ui/editors/w_OutboundEditor.hpp - src/ui/editors/w_RoutesEditor.hpp - src/ui/messaging/QvMessageBus.hpp - src/ui/models/InboundNodeModel.hpp - src/ui/models/NodeModelsBase.hpp - src/ui/models/OutboundNodeModel.hpp - src/ui/models/RuleNodeModel.hpp - src/ui/widgets/ConnectionInfoWidget.hpp - src/ui/widgets/ConnectionItemWidget.hpp - src/ui/widgets/QvAutoCompleteTextEdit.hpp - src/ui/widgets/StreamSettingsWidget.hpp - src/ui/widgets/RouteSettingsMatrix.hpp - src/ui/w_ImportConfig.hpp - src/ui/w_MainWindow.hpp - src/ui/w_PreferencesWindow.hpp - src/ui/w_PluginManager.hpp - src/ui/w_ScreenShot_Core.hpp - src/ui/w_SubscriptionManager.hpp - assets/qv2ray.rc -) + ${QVPLUGIN_INTERFACE_HEADERS} + 3rdparty/libsemver/version.cpp + src/base/Qv2rayLog.cpp + src/common/CommandArgs.cpp + src/common/HTTPRequestHelper.cpp + src/common/LogHighlighter.cpp + src/common/JsonHighlighter.cpp + src/common/QJsonModel.cpp + src/common/QvHelpers.cpp + src/common/QvTranslator.cpp + src/common/QRCodeHelper.cpp + src/components/autolaunch/QvAutoLaunch.cpp + src/components/geosite/QvGeositeReader.cpp + src/components/latency/win/ICMPPinger.cpp + src/components/latency/QvTCPing.cpp + src/components/plugins/toolbar/QvToolbar.cpp + src/components/plugins/toolbar/QvToolbar_linux.cpp + src/components/plugins/toolbar/QvToolbar_win.cpp + src/components/plugins/QvPluginHost.cpp + src/components/proxy/QvProxyConfigurator.cpp + src/components/route/RouteSchemeIO.cpp + src/components/speedchart/speedplotview.cpp + src/components/speedchart/speedwidget.cpp + src/components/darkmode/DarkmodeDetector.cpp + src/components/update/UpdateChecker.cpp + src/core/connection/ConnectionIO.cpp + src/core/connection/Generation.cpp + src/core/connection/Serialization.cpp + src/core/connection/Serialization_ss.cpp + src/core/connection/Serialization_ssd.cpp + src/core/connection/Serialization_vmess.cpp + src/core/CoreUtils.cpp + src/core/handler/ConfigHandler.cpp + src/core/handler/KernelInstanceHandler.cpp + src/core/kernel/APIBackend.cpp + src/core/kernel/V2rayKernelInteractions.cpp + src/core/kernel/PluginKernelInteractions.cpp + src/core/kernel/QvKernelABIChecker.cpp + src/core/settings/SettingsBackend.cpp + src/core/settings/SettingsUpgrade.cpp + src/main.cpp + src/ui/editors/w_InboundEditor.cpp + src/ui/editors/w_JsonEditor.cpp + src/ui/editors/w_OutboundEditor.cpp + src/ui/editors/w_RoutesEditor.cpp + src/ui/editors/w_RoutesEditor_extra.cpp + src/ui/messaging/QvMessageBus.cpp + src/ui/models/InboundNodeModel.cpp + src/ui/models/OutboundNodeModel.cpp + src/ui/models/RuleNodeModel.cpp + src/ui/widgets/ConnectionInfoWidget.cpp + src/ui/widgets/ConnectionItemWidget.cpp + src/ui/widgets/QvAutoCompleteTextEdit.cpp + src/ui/widgets/StreamSettingsWidget.cpp + src/ui/widgets/RouteSettingsMatrix.cpp + src/ui/w_ImportConfig.cpp + src/ui/w_MainWindow.cpp + src/ui/w_MainWindow_extra.cpp + src/ui/w_PreferencesWindow.cpp + src/ui/w_PluginManager.cpp + src/ui/w_ScreenShot_Core.cpp + src/ui/w_SubscriptionManager.cpp + # ui files + src/ui/w_SubscriptionManager.ui + src/ui/editors/w_OutboundEditor.ui + src/ui/editors/w_InboundEditor.ui + src/ui/editors/w_JsonEditor.ui + src/ui/editors/w_RoutesEditor.ui + src/ui/w_ImportConfig.ui + src/ui/widgets/StreamSettingsWidget.ui + src/ui/widgets/ConnectionInfoWidget.ui + src/ui/widgets/ConnectionItemWidget.ui + src/ui/widgets/RouteSettingsMatrix.ui + src/ui/w_MainWindow.ui + src/ui/w_PreferencesWindow.ui + src/ui/w_PluginManager.ui + src/ui/w_ScreenShot_Core.ui + # headers + 3rdparty/libsemver/version.hpp + src/base/JsonHelpers.hpp + src/base/models/CoreObjectModels.hpp + src/base/models/QvConfigIdentifier.hpp + src/base/models/QvRuntimeConfig.hpp + src/base/models/QvSafeType.hpp + src/base/models/QvSettingsObject.hpp + src/base/models/QvStartupConfig.hpp + src/base/Qv2rayBase.hpp + src/base/Qv2rayFeatures.hpp + src/base/Qv2rayLog.hpp + src/common/CommandArgs.hpp + src/common/HTTPRequestHelper.hpp + src/common/JsonHighlighter.hpp + src/common/LogHighlighter.hpp + src/common/QJsonModel.hpp + src/common/QvHelpers.hpp + src/common/QvTranslator.hpp + src/common/QRCodeHelper.hpp + src/components/autolaunch/QvAutoLaunch.hpp + src/components/darkmode/DarkmodeDetector.hpp + src/components/geosite/QvGeositeReader.hpp + src/components/latency/win/ICMPPinger.hpp + src/components/latency/QvTCPing.hpp + src/components/plugins/toolbar/QvToolbar.hpp + src/components/plugins/QvPluginHost.hpp + src/components/proxy/QvProxyConfigurator.hpp + src/components/route/RouteSchemeIO.hpp + src/components/route/presets/RouteScheme_V2rayN.hpp + src/components/speedchart/speedplotview.hpp + src/components/speedchart/speedwidget.hpp + src/components/update/UpdateChecker.hpp + src/core/connection/ConnectionIO.hpp + src/core/connection/Generation.hpp + src/core/connection/Serialization.hpp + src/core/CoreSafeTypes.hpp + src/core/CoreUtils.hpp + src/core/handler/ConfigHandler.hpp + src/core/handler/KernelInstanceHandler.hpp + src/core/kernel/APIBackend.hpp + src/core/kernel/V2rayKernelInteractions.hpp + src/core/kernel/PluginKernelInteractions.hpp + src/core/kernel/QvKernelABIChecker.hpp + src/core/settings/SettingsBackend.hpp + src/ui/editors/w_InboundEditor.hpp + src/ui/editors/w_JsonEditor.hpp + src/ui/editors/w_OutboundEditor.hpp + src/ui/editors/w_RoutesEditor.hpp + src/ui/messaging/QvMessageBus.hpp + src/ui/models/InboundNodeModel.hpp + src/ui/models/NodeModelsBase.hpp + src/ui/models/OutboundNodeModel.hpp + src/ui/models/RuleNodeModel.hpp + src/ui/widgets/ConnectionInfoWidget.hpp + src/ui/widgets/ConnectionItemWidget.hpp + src/ui/widgets/QvAutoCompleteTextEdit.hpp + src/ui/widgets/StreamSettingsWidget.hpp + src/ui/widgets/RouteSettingsMatrix.hpp + src/ui/w_ImportConfig.hpp + src/ui/w_MainWindow.hpp + src/ui/w_PreferencesWindow.hpp + src/ui/w_PluginManager.hpp + src/ui/w_ScreenShot_Core.hpp + src/ui/w_SubscriptionManager.hpp + assets/qv2ray.rc + ) if(EMBED_TRANSLATIONS) - add_definitions(-DEMBED_TRANSLATIONS) - configure_file(translations/translations.qrc ${CMAKE_BINARY_DIR} COPYONLY) - list(APPEND QV2RAY_SOURCES ${CMAKE_BINARY_DIR}/translations.qrc) + add_definitions(-DEMBED_TRANSLATIONS) + configure_file(translations/translations.qrc ${CMAKE_BINARY_DIR} COPYONLY) + list(APPEND QV2RAY_SOURCES ${CMAKE_BINARY_DIR}/translations.qrc) endif() add_custom_target(lupdate @@ -314,125 +314,125 @@ set(QT_LIBRARY Qt5::Gui Qt5::Widgets Qt5::Network -) + ) add_executable(${PROJECT_NAME} - ${GUI_TYPE} - ${QV2RAY_SOURCES} - ${QNODEEDITOR_SOURCES} - ${QNODEEDITOR_QRC_RESOURCES} - ${SINGLEAPPLICATION_SOURCES} - ${ZXING_SOURCES} - ${PROTO_SRCS} - ${PROTO_HDRS} - ${API_GRPC_SRCS} - ${API_PROTO_SRCS} - ${QRC_RESOURCES} - ${QM_FILES} -) + ${GUI_TYPE} + ${QV2RAY_SOURCES} + ${QNODEEDITOR_SOURCES} + ${QNODEEDITOR_QRC_RESOURCES} + ${SINGLEAPPLICATION_SOURCES} + ${ZXING_SOURCES} + ${PROTO_SRCS} + ${PROTO_HDRS} + ${API_GRPC_SRCS} + ${API_PROTO_SRCS} + ${QRC_RESOURCES} + ${QM_FILES} + ) target_link_libraries(${PROJECT_NAME} - ${QT_LIBRARY} - ${QV2RAY_PROTOBUF_LIBRARY} - ${QV2RAY_BACKEND_LIBRARIES} - ${QNODEEDITOR_LIBRARY} - ${ZXING_LIBRARY} -) + ${QT_LIBRARY} + ${QV2RAY_PROTOBUF_LIBRARY} + ${QV2RAY_BACKEND_LIBRARIES} + ${QNODEEDITOR_LIBRARY} + ${ZXING_LIBRARY} + ) target_include_directories(${PROJECT_NAME} PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_CURRENT_SOURCE_DIR}/src - ${CMAKE_BINARY_DIR} - ${QHTTPSERVER_DIR} - ${QNODEEDITOR_INCLUDE_PATH} - ${ZXING_INCLUDE_PATH} - ${SINGLEAPPLICATION_DIR} - ${Protobuf_INCLUDE_DIRS} - ${cpp-httplib_INCLUDE_DIRS} -) + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/src + ${CMAKE_BINARY_DIR} + ${QHTTPSERVER_DIR} + ${QNODEEDITOR_INCLUDE_PATH} + ${ZXING_INCLUDE_PATH} + ${SINGLEAPPLICATION_DIR} + ${Protobuf_INCLUDE_DIRS} + ${cpp-httplib_INCLUDE_DIRS} + ) if(APPLE) find_package(Iconv REQUIRED) target_link_libraries(${PROJECT_NAME} - Iconv::Iconv - "-framework Carbon" - "-framework Cocoa" - "-framework Security" - ) + Iconv::Iconv + "-framework Carbon" + "-framework Cocoa" + "-framework Security" + ) target_include_directories(${PROJECT_NAME} PRIVATE - ${Iconv_INCLUDE_DIR} - ) + ${Iconv_INCLUDE_DIR} + ) set(MACOSX_ICON "${CMAKE_SOURCE_DIR}/assets/icons/qv2ray.icns") set(MACOSX_PLIST "${CMAKE_SOURCE_DIR}/assets/MacOSXBundleInfo.plist.in") set_source_files_properties(${QM_FILES} - PROPERTIES - MACOSX_PACKAGE_LOCATION Resources/lang - ) + PROPERTIES + MACOSX_PACKAGE_LOCATION Resources/lang + ) target_sources(${PROJECT_NAME} PRIVATE - ${MACOSX_ICON} - ) + ${MACOSX_ICON} + ) set_target_properties(${PROJECT_NAME} - PROPERTIES - MACOSX_BUNDLE TRUE - MACOSX_BUNDLE_INFO_PLIST ${MACOSX_PLIST} - MACOSX_BUNDLE_BUNDLE_NAME "Qv2ray" - MACOSX_BUNDLE_BUNDLE_VERSION ${QV2RAY_VERSION_STRING} - MACOSX_BUNDLE_COPYRIGHT "Copyright (c) 2019-2020 Qv2ray Development Group" - MACOSX_BUNDLE_GUI_IDENTIFIER "com.github.qv2ray" - MACOSX_BUNDLE_ICON_FILE "qv2ray.icns" - MACOSX_BUNDLE_INFO_STRING "Created by Qv2ray development team" - MACOSX_BUNDLE_LONG_VERSION_STRING ${QV2RAY_VERSION_STRING} - MACOSX_BUNDLE_SHORT_VERSION_STRING ${QV2RAY_VERSION_STRING} - RESOURCE - ${MACOSX_ICON} - ) + PROPERTIES + MACOSX_BUNDLE TRUE + MACOSX_BUNDLE_INFO_PLIST ${MACOSX_PLIST} + MACOSX_BUNDLE_BUNDLE_NAME "Qv2ray" + MACOSX_BUNDLE_BUNDLE_VERSION ${QV2RAY_VERSION_STRING} + MACOSX_BUNDLE_COPYRIGHT "Copyright (c) 2019-2020 Qv2ray Development Group" + MACOSX_BUNDLE_GUI_IDENTIFIER "com.github.qv2ray" + MACOSX_BUNDLE_ICON_FILE "qv2ray.icns" + MACOSX_BUNDLE_INFO_STRING "Created by Qv2ray development team" + MACOSX_BUNDLE_LONG_VERSION_STRING ${QV2RAY_VERSION_STRING} + MACOSX_BUNDLE_SHORT_VERSION_STRING ${QV2RAY_VERSION_STRING} + RESOURCE + ${MACOSX_ICON} + ) # Destination paths below are relative to ${CMAKE_INSTALL_PREFIX} install(TARGETS ${PROJECT_NAME} - BUNDLE DESTINATION . COMPONENT Runtime - RUNTIME DESTINATION bin COMPONENT Runtime - ) + BUNDLE DESTINATION . COMPONENT Runtime + RUNTIME DESTINATION bin COMPONENT Runtime + ) add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD - COMMAND macdeployqt ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.app - ) + COMMAND macdeployqt ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.app + ) set(APPS "\${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}.app") include(cmake/deployment.cmake) endif() if(UNIX AND NOT APPLE AND NOT WIN32) - install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION bin) - install(FILES assets/qv2ray.metainfo.xml DESTINATION share/metainfo) - install(FILES assets/qv2ray.desktop DESTINATION share/applications) - install(FILES assets/icons/qv2ray.png DESTINATION share/icons/hicolor/256x256/apps) - if(NOT EMBED_TRANSLATIONS) - install(FILES ${QM_FILES} DESTINATION share/qv2ray/lang) - endif() + install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION bin) + install(FILES assets/qv2ray.metainfo.xml DESTINATION share/metainfo) + install(FILES assets/qv2ray.desktop DESTINATION share/applications) + install(FILES assets/icons/qv2ray.png DESTINATION share/icons/hicolor/256x256/apps) + if(NOT EMBED_TRANSLATIONS) + install(FILES ${QM_FILES} DESTINATION share/qv2ray/lang) + endif() endif() if(WIN32) - target_link_libraries(${PROJECT_NAME} wininet wsock32 ws2_32 user32) - install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION .) - if(NOT EMBED_TRANSLATIONS) - install(FILES ${QM_FILES} DESTINATION lang) - endif() - - if(CMAKE_CL_64) - install(FILES ${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/bin/libssl-1_1-x64.dll DESTINATION .) - install(FILES ${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/bin/libcrypto-1_1-x64.dll DESTINATION .) - else() - install(FILES ${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/bin/libssl-1_1.dll DESTINATION .) - install(FILES ${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/bin/libcrypto-1_1.dll DESTINATION .) - endif() + target_link_libraries(${PROJECT_NAME} wininet wsock32 ws2_32 user32) + install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION .) + if(NOT EMBED_TRANSLATIONS) + install(FILES ${QM_FILES} DESTINATION lang) + endif() - add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD - COMMAND windeployqt ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.exe --compiler-runtime --verbose 2 --dir ${CMAKE_BINARY_DIR}/winqt/ - ) - install(DIRECTORY ${CMAKE_BINARY_DIR}/winqt/ DESTINATION .) - set(APPS "\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}.exe") - include(cmake/deployment.cmake) + if(CMAKE_CL_64) + install(FILES ${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/bin/libssl-1_1-x64.dll DESTINATION .) + install(FILES ${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/bin/libcrypto-1_1-x64.dll DESTINATION .) + else() + install(FILES ${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/bin/libssl-1_1.dll DESTINATION .) + install(FILES ${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/bin/libcrypto-1_1.dll DESTINATION .) + endif() + + add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND windeployqt ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.exe --compiler-runtime --verbose 2 --dir ${CMAKE_BINARY_DIR}/winqt/ + ) + install(DIRECTORY ${CMAKE_BINARY_DIR}/winqt/ DESTINATION .) + set(APPS "\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}.exe") + include(cmake/deployment.cmake) endif() diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 87d9fa61..2308ec14 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5245 +5259 \ No newline at end of file From b82a5a0741cad02edd4a4d6d4c22af6e18fb346b Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Thu, 16 Apr 2020 23:33:07 +0800 Subject: [PATCH 83/92] add: adapt new Interface --- makespec/BUILDVERSION | 2 +- src/components/plugins/QvPluginHost.cpp | 8 ++++---- src/components/plugins/QvPluginHost.hpp | 8 ++++---- src/components/plugins/interface | 2 +- src/components/proxy/QvProxyConfigurator.cpp | 12 +++++++----- src/core/handler/ConfigHandler.cpp | 18 +++++++++--------- src/core/handler/KernelInstanceHandler.cpp | 14 +++++++------- src/ui/editors/w_OutboundEditor.hpp | 2 +- 8 files changed, 34 insertions(+), 32 deletions(-) diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 2308ec14..f01c8a0e 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5259 \ No newline at end of file +5261 \ No newline at end of file diff --git a/src/components/plugins/QvPluginHost.cpp b/src/components/plugins/QvPluginHost.cpp index c1861d7c..d06dc228 100644 --- a/src/components/plugins/QvPluginHost.cpp +++ b/src/components/plugins/QvPluginHost.cpp @@ -184,7 +184,7 @@ namespace Qv2ray::components::plugins } // ================== BEGIN SEND EVENTS ================== - void QvPluginHost::Send_ConnectionStatsEvent(const QvConnectionStatsEventObject &object) + void QvPluginHost::Send_ConnectionStatsEvent(const Events::ConnectionStats::EventObject &object) { for (auto &plugin : plugins) { @@ -194,7 +194,7 @@ namespace Qv2ray::components::plugins } } } - void QvPluginHost::Send_ConnectivityEvent(const QvConnectivityEventObject &object) + void QvPluginHost::Send_ConnectivityEvent(const Events::Connectivity::EventObject &object) { for (auto &plugin : plugins) { @@ -204,7 +204,7 @@ namespace Qv2ray::components::plugins } } } - void QvPluginHost::Send_ConnectionEvent(const QvConnectionEntryEventObject &object) + void QvPluginHost::Send_ConnectionEvent(const Events::ConnectionEntry::EventObject &object) { for (auto &plugin : plugins) { @@ -214,7 +214,7 @@ namespace Qv2ray::components::plugins } } } - void QvPluginHost::Send_SystemProxyEvent(const QvSystemProxyEventObject &object) + void QvPluginHost::Send_SystemProxyEvent(const Events::SystemProxy::EventObject &object) { for (auto &plugin : plugins) { diff --git a/src/components/plugins/QvPluginHost.hpp b/src/components/plugins/QvPluginHost.hpp index c57eb08c..c8967cf6 100644 --- a/src/components/plugins/QvPluginHost.hpp +++ b/src/components/plugins/QvPluginHost.hpp @@ -72,10 +72,10 @@ namespace Qv2ray::components::plugins bool *status) const; const QList GetOutboundEditorWidgets() const; // - void Send_ConnectionStatsEvent(const QvConnectionStatsEventObject &object); - void Send_ConnectivityEvent(const QvConnectivityEventObject &object); - void Send_ConnectionEvent(const QvConnectionEntryEventObject &object); - void Send_SystemProxyEvent(const QvSystemProxyEventObject &object); + void Send_ConnectionStatsEvent(const Events::ConnectionStats::EventObject &object); + void Send_ConnectivityEvent(const Events::Connectivity::EventObject &object); + void Send_ConnectionEvent(const Events::ConnectionEntry::EventObject &object); + void Send_SystemProxyEvent(const Events::SystemProxy::EventObject &object); // private slots: void QvPluginLog(const QString &log); diff --git a/src/components/plugins/interface b/src/components/plugins/interface index 1fede69e..d37c7ea9 160000 --- a/src/components/plugins/interface +++ b/src/components/plugins/interface @@ -1 +1 @@ -Subproject commit 1fede69e7ff553b31e59f6e13cc40d4aeeee21d9 +Subproject commit d37c7ea9459956dc459610e98b821d4a790cb6e8 diff --git a/src/components/proxy/QvProxyConfigurator.cpp b/src/components/proxy/QvProxyConfigurator.cpp index 7de750cd..59bfd8b5 100644 --- a/src/components/proxy/QvProxyConfigurator.cpp +++ b/src/components/proxy/QvProxyConfigurator.cpp @@ -313,12 +313,13 @@ namespace Qv2ray::components::proxy #endif // // Trigger plugin events - QMap portSettings; + QMap portSettings; if (hasHTTP) - portSettings.insert(QvSystemProxyType::SystemProxy_HTTP, httpPort); + portSettings.insert(Events::SystemProxy::SystemProxyType::SystemProxy_HTTP, httpPort); if (hasSOCKS) - portSettings.insert(QvSystemProxyType::SystemProxy_SOCKS, socksPort); - PluginHost->Send_SystemProxyEvent(QvSystemProxyEventObject{ portSettings, QvSystemProxyStateType::SystemProxyState_SetProxy }); + portSettings.insert(Events::SystemProxy::SystemProxyType::SystemProxy_SOCKS, socksPort); + PluginHost->Send_SystemProxyEvent( + Events::SystemProxy::EventObject{ portSettings, Events::SystemProxy::SystemProxyStateType::SystemProxyState_SetProxy }); } void ClearSystemProxy() @@ -374,6 +375,7 @@ namespace Qv2ray::components::proxy #endif // // Trigger plugin events - PluginHost->Send_SystemProxyEvent(QvSystemProxyEventObject{ {}, QvSystemProxyStateType::SystemProxyState_ClearProxy }); + PluginHost->Send_SystemProxyEvent( + Events::SystemProxy::EventObject{ {}, Events::SystemProxy::SystemProxyStateType::SystemProxyState_ClearProxy }); } } // namespace Qv2ray::components::proxy diff --git a/src/core/handler/ConfigHandler.cpp b/src/core/handler/ConfigHandler.cpp index 9771bba0..d322008b 100644 --- a/src/core/handler/ConfigHandler.cpp +++ b/src/core/handler/ConfigHandler.cpp @@ -224,7 +224,7 @@ namespace Qv2ray::core::handlers { CheckConnectionExistance(id); OnConnectionRenamed(id, connections[id].displayName, newName); - PluginHost->Send_ConnectionEvent({ newName, connections[id].displayName, ConnectionEvent_Renamed }); + PluginHost->Send_ConnectionEvent({ newName, connections[id].displayName, Events::ConnectionEntry::ConnectionEvent_Renamed }); connections[id].displayName = newName; CHSaveConfigData_p(); return {}; @@ -236,7 +236,7 @@ namespace Qv2ray::core::handlers QFile connectionFile((groups[groupId].isSubscription ? QV2RAY_SUBSCRIPTION_DIR : QV2RAY_CONNECTIONS_DIR) + groupId.toString() + "/" + id.toString() + QV2RAY_CONFIG_FILE_EXTENSION); // - PluginHost->Send_ConnectionEvent({ connections[id].displayName, "", ConnectionEvent_Deleted }); + PluginHost->Send_ConnectionEvent({ connections[id].displayName, "", Events::ConnectionEntry::ConnectionEvent_Deleted }); connections.remove(id); groups[groupId].connections.removeAll(id); // @@ -284,7 +284,7 @@ namespace Qv2ray::core::handlers groups[newGroupId].connections.append(id); connections[id].groupId = newGroupId; // - PluginHost->Send_ConnectionEvent({ connections[id].displayName, "", ConnectionEvent_Updated }); + PluginHost->Send_ConnectionEvent({ connections[id].displayName, "", Events::ConnectionEntry::ConnectionEvent_Updated }); // emit OnConnectionGroupChanged(id, oldgid, newGroupId); // @@ -315,7 +315,7 @@ namespace Qv2ray::core::handlers QDir(QV2RAY_CONNECTIONS_DIR + id.toString()).removeRecursively(); } // - PluginHost->Send_ConnectionEvent({ groups[id].displayName, "", ConnectionEvent_Deleted }); + PluginHost->Send_ConnectionEvent({ groups[id].displayName, "", Events::ConnectionEntry::ConnectionEvent_Deleted }); // groups.remove(id); CHSaveConfigData_p(); @@ -355,7 +355,7 @@ namespace Qv2ray::core::handlers { LOG(MODULE_CORE_HANDLER, "V2ray core crashed!") emit OnDisconnected(id); - PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), {}, QvConnecticity_Disconnected }); + PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), {}, Events::Connectivity::QvConnecticity_Disconnected }); emit OnKernelCrashed(id); } @@ -394,7 +394,7 @@ namespace Qv2ray::core::handlers connectionRootCache[id] = root; // emit OnConnectionModified(id); - PluginHost->Send_ConnectionEvent({ connections[id].displayName, "", ConnectionEvent_Updated }); + PluginHost->Send_ConnectionEvent({ connections[id].displayName, "", Events::ConnectionEntry::ConnectionEvent_Updated }); if (!skipRestart && kernelHandler->isConnected(id)) { emit RestartConnection(); @@ -408,7 +408,7 @@ namespace Qv2ray::core::handlers groups[id].displayName = displayName; groups[id].isSubscription = isSubscription; groups[id].importDate = system_clock::to_time_t(system_clock::now()); - PluginHost->Send_ConnectionEvent({ displayName, "", ConnectionEvent_Created }); + PluginHost->Send_ConnectionEvent({ displayName, "", Events::ConnectionEntry::ConnectionEvent_Created }); emit OnGroupCreated(id, displayName); CHSaveConfigData_p(); return id; @@ -422,7 +422,7 @@ namespace Qv2ray::core::handlers return tr("Group does not exist"); } OnGroupRenamed(id, groups[id].displayName, newName); - PluginHost->Send_ConnectionEvent({ newName, groups[id].displayName, ConnectionEvent_Renamed }); + PluginHost->Send_ConnectionEvent({ newName, groups[id].displayName, Events::ConnectionEntry::ConnectionEvent_Renamed }); groups[id].displayName = newName; return {}; } @@ -600,7 +600,7 @@ namespace Qv2ray::core::handlers connections[newId].importDate = system_clock::to_time_t(system_clock::now()); connections[newId].displayName = displayName; emit OnConnectionCreated(newId, displayName); - PluginHost->Send_ConnectionEvent({ displayName, "", ConnectionEvent_Created }); + PluginHost->Send_ConnectionEvent({ displayName, "", Events::ConnectionEntry::ConnectionEvent_Created }); UpdateConnection(newId, root); if (!skipSaveConfig) { diff --git a/src/core/handler/KernelInstanceHandler.cpp b/src/core/handler/KernelInstanceHandler.cpp index a27c8744..b181c90a 100644 --- a/src/core/handler/KernelInstanceHandler.cpp +++ b/src/core/handler/KernelInstanceHandler.cpp @@ -33,7 +33,7 @@ namespace Qv2ray::core::handlers this->root = root; auto fullConfig = GenerateRuntimeConfig(root); auto inboundPorts = GetInboundPorts(fullConfig); - PluginHost->Send_ConnectivityEvent(QvConnectivityEventObject{ GetDisplayName(id), inboundPorts, QvConnecticity_Connecting }); + PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), inboundPorts, Events::Connectivity::QvConnecticity_Connecting }); QList> inboundInfo; for (const auto &inbound_v : fullConfig["inbounds"].toArray()) { @@ -160,11 +160,11 @@ namespace Qv2ray::core::handlers if (!result.has_value()) { emit OnConnected(currentConnectionId); - PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), inboundPorts, QvConnecticity_Connected }); + PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), inboundPorts, Events::Connectivity::QvConnecticity_Connected }); } else { - PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), {}, QvConnecticity_Disconnected }); + PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), {}, Events::Connectivity::QvConnecticity_Disconnected }); } return result; } // namespace Qv2ray::core::handlers @@ -202,11 +202,11 @@ namespace Qv2ray::core::handlers if (!result.has_value()) { emit OnConnected(currentConnectionId); - PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), inboundPorts, QvConnecticity_Connected }); + PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), inboundPorts, Events::Connectivity::QvConnecticity_Connected }); } else { - PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), {}, QvConnecticity_Disconnected }); + PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), {}, Events::Connectivity::QvConnecticity_Disconnected }); } return result; } @@ -236,7 +236,7 @@ namespace Qv2ray::core::handlers { if (vCoreInstance->KernelStarted) { - PluginHost->Send_ConnectivityEvent({ GetDisplayName(currentConnectionId), {}, QvConnecticity_Disconnecting }); + PluginHost->Send_ConnectivityEvent({ GetDisplayName(currentConnectionId), {}, Events::Connectivity::QvConnecticity_Disconnecting }); vCoreInstance->StopConnection(); // for (auto &kernel : activeKernels.keys()) @@ -248,7 +248,7 @@ namespace Qv2ray::core::handlers ConnectionId id = currentConnectionId; currentConnectionId = NullConnectionId; emit OnDisconnected(id); - PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), {}, QvConnecticity_Disconnected }); + PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), {}, Events::Connectivity::QvConnecticity_Disconnected }); } else { diff --git a/src/ui/editors/w_OutboundEditor.hpp b/src/ui/editors/w_OutboundEditor.hpp index 102a4428..4038ee01 100644 --- a/src/ui/editors/w_OutboundEditor.hpp +++ b/src/ui/editors/w_OutboundEditor.hpp @@ -64,5 +64,5 @@ class OutboundEditor // StreamSettingsWidget *streamSettingsWidget; // - QMap> pluginWidgets; + QMap> pluginWidgets; }; From 856022234363e81384bfb770143b67882666fae0 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Fri, 17 Apr 2020 15:23:19 +0800 Subject: [PATCH 84/92] fix: some UI fixes, added KernelCrashed message --- makespec/BUILDVERSION | 2 +- src/components/plugins/QvPluginHost.cpp | 18 ++++++++++++++++++ src/components/plugins/QvPluginHost.hpp | 1 + src/core/CoreUtils.cpp | 6 +++++- src/core/connection/Generation.cpp | 4 +++- src/core/handler/ConfigHandler.cpp | 6 +++--- src/core/handler/ConfigHandler.hpp | 4 ++-- src/core/handler/KernelInstanceHandler.cpp | 21 +++++++++++++-------- src/core/handler/KernelInstanceHandler.hpp | 4 ++-- src/core/kernel/V2rayKernelInteractions.cpp | 2 +- src/core/kernel/V2rayKernelInteractions.hpp | 2 +- src/ui/editors/w_OutboundEditor.cpp | 18 ++++++++++++++---- src/ui/w_MainWindow.cpp | 8 ++++---- 13 files changed, 68 insertions(+), 28 deletions(-) diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index f01c8a0e..48543ca6 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5261 \ No newline at end of file +5262 \ No newline at end of file diff --git a/src/components/plugins/QvPluginHost.cpp b/src/components/plugins/QvPluginHost.cpp index d06dc228..194382ba 100644 --- a/src/components/plugins/QvPluginHost.cpp +++ b/src/components/plugins/QvPluginHost.cpp @@ -270,6 +270,24 @@ namespace Qv2ray::components::plugins } return data; } + const QvPluginOutboundInfoObject QvPluginHost::TryGetOutboundInfo(const QString &protocol, const QJsonObject &o, bool *status) const + { + *status = false; + for (const auto &plugin : plugins) + { + if (plugin.isLoaded && plugin.metadata.SpecialPluginType.contains(SPECIAL_TYPE_SERIALIZOR)) + { + auto serializer = plugin.pluginInterface->GetSerializer(); + if (serializer->OutboundProtocols().contains(protocol)) + { + auto info = serializer->GetOutboundInfo(protocol, o); + *status = true; + return info; + } + } + } + return {}; + } const QString QvPluginHost::TrySerializeShareLink(const QString &protocol, // const QJsonObject &outboundSettings, // const QString &alias, // diff --git a/src/components/plugins/QvPluginHost.hpp b/src/components/plugins/QvPluginHost.hpp index c8967cf6..a0c7a981 100644 --- a/src/components/plugins/QvPluginHost.hpp +++ b/src/components/plugins/QvPluginHost.hpp @@ -70,6 +70,7 @@ namespace Qv2ray::components::plugins const QString &alias, // const QString &groupName, // bool *status) const; + const QvPluginOutboundInfoObject TryGetOutboundInfo(const QString &protocol, const QJsonObject &o, bool *status) const; const QList GetOutboundEditorWidgets() const; // void Send_ConnectionStatsEvent(const Events::ConnectionStats::EventObject &object); diff --git a/src/core/CoreUtils.cpp b/src/core/CoreUtils.cpp index 1101bd45..7d1d5f4b 100644 --- a/src/core/CoreUtils.cpp +++ b/src/core/CoreUtils.cpp @@ -56,7 +56,11 @@ namespace Qv2ray::core } else { - return false; + bool status; + auto info = PluginHost->TryGetOutboundInfo(*protocol, out["settings"].toObject(), &status); + *host = info.hostName; + *port = info.port; + return status; } } diff --git a/src/core/connection/Generation.cpp b/src/core/connection/Generation.cpp index c03299cd..a37a8fae 100644 --- a/src/core/connection/Generation.cpp +++ b/src/core/connection/Generation.cpp @@ -308,7 +308,7 @@ namespace Qv2ray::core::connection if (!root.contains("inbounds") || root.value("inbounds").toArray().empty()) { INBOUNDS inboundsList; - + QJsonObject sniffingObject{ { "enabled", false } }; // HTTP Inbound if (GlobalConfig.inboundConfig.useHTTP) { @@ -317,6 +317,7 @@ namespace Qv2ray::core::connection httpInBoundObject.insert("port", GlobalConfig.inboundConfig.http_port); httpInBoundObject.insert("protocol", "http"); httpInBoundObject.insert("tag", "http_IN"); + httpInBoundObject.insert("sniffing", sniffingObject); if (GlobalConfig.inboundConfig.http_useAuth) { @@ -335,6 +336,7 @@ namespace Qv2ray::core::connection socksInBoundObject.insert("port", GlobalConfig.inboundConfig.socks_port); socksInBoundObject.insert("protocol", "socks"); socksInBoundObject.insert("tag", "socks_IN"); + socksInBoundObject.insert("sniffing", sniffingObject); auto socksInSettings = GenerateSocksIN(GlobalConfig.inboundConfig.socks_useAuth ? "password" : "noauth", QList() << GlobalConfig.inboundConfig.socksAccount, GlobalConfig.inboundConfig.socksUDP, GlobalConfig.inboundConfig.socksLocalIP); diff --git a/src/core/handler/ConfigHandler.cpp b/src/core/handler/ConfigHandler.cpp index d322008b..f8d9db41 100644 --- a/src/core/handler/ConfigHandler.cpp +++ b/src/core/handler/ConfigHandler.cpp @@ -351,12 +351,12 @@ namespace Qv2ray::core::handlers return kernelHandler->isConnected(id); } - void QvConfigHandler::OnKernelCrashed_p(const ConnectionId &id) + void QvConfigHandler::OnKernelCrashed_p(const ConnectionId &id, const QString &errMessage) { - LOG(MODULE_CORE_HANDLER, "V2ray core crashed!") + LOG(MODULE_CORE_HANDLER, "Kernel crashed: " + errMessage) emit OnDisconnected(id); PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), {}, Events::Connectivity::QvConnecticity_Disconnected }); - emit OnKernelCrashed(id); + emit OnKernelCrashed(id, errMessage); } QvConfigHandler::~QvConfigHandler() diff --git a/src/core/handler/ConfigHandler.hpp b/src/core/handler/ConfigHandler.hpp index c0e71d91..7d046019 100644 --- a/src/core/handler/ConfigHandler.hpp +++ b/src/core/handler/ConfigHandler.hpp @@ -126,10 +126,10 @@ namespace Qv2ray::core::handlers void OnSubscriptionUpdateFinished(const GroupId &id); void OnConnected(const ConnectionId &id); void OnDisconnected(const ConnectionId &id); - void OnKernelCrashed(const ConnectionId &id); + void OnKernelCrashed(const ConnectionId &id, const QString &errMessage); // private slots: - void OnKernelCrashed_p(const ConnectionId &id); + void OnKernelCrashed_p(const ConnectionId &id, const QString &errMessage); void OnLatencyDataArrived_p(const QvTCPingResultObject &data); void OnStatsDataArrived_p(const ConnectionId &id, const quint64 uploadSpeed, const quint64 downloadSpeed); diff --git a/src/core/handler/KernelInstanceHandler.cpp b/src/core/handler/KernelInstanceHandler.cpp index b181c90a..5a567d6b 100644 --- a/src/core/handler/KernelInstanceHandler.cpp +++ b/src/core/handler/KernelInstanceHandler.cpp @@ -25,7 +25,7 @@ namespace Qv2ray::core::handlers std::optional KernelInstanceHandler::StartConnection(const ConnectionId &id, const CONFIGROOT &root) { - if (vCoreInstance->KernelStarted) + if (vCoreInstance->KernelStarted || !activeKernels.isEmpty()) { StopConnection(); } @@ -171,9 +171,11 @@ namespace Qv2ray::core::handlers else { auto firstOutbound = fullConfig["outbounds"].toArray().first().toObject(); - if (kernels.contains(firstOutbound["protocol"].toString())) + const auto protocol = firstOutbound["protocol"].toString(); + if (kernels.contains(protocol)) { auto kernel = kernels[firstOutbound["protocol"].toString()].get(); + activeKernels[protocol] = kernel; QMap pluginInboundPort; for (const auto &[_protocol, _port, _tag] : inboundInfo) { @@ -219,9 +221,9 @@ namespace Qv2ray::core::handlers StartConnection(lastConnectionId, root); } - void KernelInstanceHandler::OnKernelCrashed_p() + void KernelInstanceHandler::OnKernelCrashed_p(const QString &msg) { - emit OnCrashed(currentConnectionId); + emit OnCrashed(currentConnectionId, msg); emit OnDisconnected(currentConnectionId); lastConnectionId = currentConnectionId; currentConnectionId = NullConnectionId; @@ -234,12 +236,15 @@ namespace Qv2ray::core::handlers void KernelInstanceHandler::StopConnection() { - if (vCoreInstance->KernelStarted) + if (vCoreInstance->KernelStarted || !activeKernels.isEmpty()) { PluginHost->Send_ConnectivityEvent({ GetDisplayName(currentConnectionId), {}, Events::Connectivity::QvConnecticity_Disconnecting }); - vCoreInstance->StopConnection(); + if (vCoreInstance->KernelStarted) + { + vCoreInstance->StopConnection(); + } // - for (auto &kernel : activeKernels.keys()) + for (const auto &kernel : activeKernels.keys()) { LOG(MODULE_CONNECTION, "Stopping plugin kernel: " + kernel) activeKernels[kernel]->StopKernel(); @@ -252,7 +257,7 @@ namespace Qv2ray::core::handlers } else { - LOG(MODULE_CORE_HANDLER, "VCore is not started, not disconnecting") + LOG(MODULE_CORE_HANDLER, "Cannot disconnect when there's nothing connected.") } } diff --git a/src/core/handler/KernelInstanceHandler.hpp b/src/core/handler/KernelInstanceHandler.hpp index edc4815d..498e3948 100644 --- a/src/core/handler/KernelInstanceHandler.hpp +++ b/src/core/handler/KernelInstanceHandler.hpp @@ -30,12 +30,12 @@ namespace Qv2ray::core::handlers signals: void OnConnected(const ConnectionId &id); void OnDisconnected(const ConnectionId &id); - void OnCrashed(const ConnectionId &id); + void OnCrashed(const ConnectionId &id, const QString &errMessage); void OnStatsDataAvailable(const ConnectionId &id, const quint64 uploadSpeed, const quint64 downloadSpeed); void OnKernelLogAvailable(const ConnectionId &id, const QString &log); private slots: - void OnKernelCrashed_p(); + void OnKernelCrashed_p(const QString &msg); void OnKernelLogAvailable_p(const QString &log); void OnStatsDataArrived_p(const quint64 uploadSpeed, const quint64 downloadSpeed); diff --git a/src/core/kernel/V2rayKernelInteractions.cpp b/src/core/kernel/V2rayKernelInteractions.cpp index 72242e7c..5c365bc0 100644 --- a/src/core/kernel/V2rayKernelInteractions.cpp +++ b/src/core/kernel/V2rayKernelInteractions.cpp @@ -185,7 +185,7 @@ namespace Qv2ray::core::kernel { LOG(MODULE_VCORE, "V2ray kernel crashed.") StopConnection(); - emit OnProcessErrored(); + emit OnProcessErrored("V2ray kernel crashed."); } }); apiWorker = new APIWorker(); diff --git a/src/core/kernel/V2rayKernelInteractions.hpp b/src/core/kernel/V2rayKernelInteractions.hpp index dc97fe17..2b3a1ff7 100644 --- a/src/core/kernel/V2rayKernelInteractions.hpp +++ b/src/core/kernel/V2rayKernelInteractions.hpp @@ -29,7 +29,7 @@ namespace Qv2ray::core::kernel static bool ValidateKernel(const QString &vCorePath, const QString &vAssetsPath, QString *message); signals: - void OnProcessErrored(); + void OnProcessErrored(const QString &errMessage); void OnProcessOutputReadyRead(const QString &output); void OnNewStatsDataArrived(const quint64 speedUp, const quint64 speedDown); diff --git a/src/ui/editors/w_OutboundEditor.cpp b/src/ui/editors/w_OutboundEditor.cpp index 69cc5892..21724205 100644 --- a/src/ui/editors/w_OutboundEditor.cpp +++ b/src/ui/editors/w_OutboundEditor.cpp @@ -14,6 +14,8 @@ OutboundEditor::OutboundEditor(QWidget *parent) : QDialog(parent), tag(OUTBOUND_ QvMessageBusConnect(OutboundEditor); setupUi(this); // + outboundType = "vmess"; + // streamSettingsWidget = new StreamSettingsWidget(this); streamSettingsWidget->SetStreamObject({}); transportFrame->addWidget(streamSettingsWidget); @@ -143,7 +145,7 @@ void OutboundEditor::ReloadGUI() { tag = originalConfig["tag"].toString(); tagTxt->setText(tag); - outboundType = originalConfig["protocol"].toString(); + outboundType = originalConfig["protocol"].toString("vmess"); muxConfig = originalConfig["mux"].toObject(); useForwardProxy = originalConfig[QV2RAY_USE_FPROXY_KEY].toBool(false); streamSettingsWidget->SetStreamObject(StructFromJsonString(JsonToString(originalConfig["streamSettings"].toObject()))); @@ -158,6 +160,10 @@ void OutboundEditor::ReloadGUI() { outBoundTypeCombo->setCurrentIndex(0); vmess = StructFromJsonString(JsonToString(settings["vnext"].toArray().first().toObject())); + if (vmess.users.empty()) + { + vmess.users.push_back({}); + } address = vmess.address; port = vmess.port; idLineEdit->setText(vmess.users.front().id); @@ -184,8 +190,9 @@ void OutboundEditor::ReloadGUI() address = socks.address; port = socks.port; if (socks.users.empty()) - socks.users.push_back(SocksServerObject::UserObject()); - + { + socks.users.push_back({}); + } socks_PasswordTxt->setText(socks.users.front().pass); socks_UserNameTxt->setText(socks.users.front().user); } @@ -208,8 +215,11 @@ void OutboundEditor::ReloadGUI() } if (!processed) { + LOG(MODULE_UI, "Outbound type: " + outboundType + " is not supported.") QvMessageBoxWarn(this, tr("Unknown outbound."), - tr("The specified outbound type is invalid, this may be caused by a plugin failure.")); + tr("The specified outbound type is invalid, this may be caused by a plugin failure.") + NEWLINE + + tr("Please use the JsonEditor or reload the plugin.")); + reject(); } } // diff --git a/src/ui/w_MainWindow.cpp b/src/ui/w_MainWindow.cpp index 601bcf6a..3a8d9022 100644 --- a/src/ui/w_MainWindow.cpp +++ b/src/ui/w_MainWindow.cpp @@ -132,11 +132,11 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) UpdateColorScheme(); // // - connect(ConnectionManager, &QvConfigHandler::OnKernelCrashed, [&] { + connect(ConnectionManager, &QvConfigHandler::OnKernelCrashed, [&](const ConnectionId &, const QString &reason) { 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.")); + QvMessageBoxWarn(this, tr("Kernel terminated."), + tr("The kernel terminated unexpectedly:") + NEWLINE + reason + NEWLINE + NEWLINE + + tr("To solve the problem, read the kernel log in the log text browser.")); }); // connect(ConnectionManager, &QvConfigHandler::OnConnected, this, &MainWindow::OnConnected); From 6e5d0d34df5bf3d5191fc4d6460333e8a19c7278 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Fri, 17 Apr 2020 17:28:26 +0800 Subject: [PATCH 85/92] fix: call StopConnection after crash --- src/core/handler/KernelInstanceHandler.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/handler/KernelInstanceHandler.cpp b/src/core/handler/KernelInstanceHandler.cpp index 5a567d6b..f73dc972 100644 --- a/src/core/handler/KernelInstanceHandler.cpp +++ b/src/core/handler/KernelInstanceHandler.cpp @@ -223,6 +223,7 @@ namespace Qv2ray::core::handlers void KernelInstanceHandler::OnKernelCrashed_p(const QString &msg) { + StopConnection(); emit OnCrashed(currentConnectionId, msg); emit OnDisconnected(currentConnectionId); lastConnectionId = currentConnectionId; From 153f5db99d5c8e15c7a0ace1a0b6db1c21435c5b Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Fri, 17 Apr 2020 17:31:37 +0800 Subject: [PATCH 86/92] Fix DMG background issue. (#517) * damn it apple Co-authored-by: ymshenyu --- .github/workflows/build-qv2ray-cmake.yml | 23 ++++++++++++++--------- assets/package_dmg.json.in | 17 +++++++++++++++++ cmake/deployment.cmake | 1 + 3 files changed, 32 insertions(+), 9 deletions(-) create mode 100644 assets/package_dmg.json.in diff --git a/.github/workflows/build-qv2ray-cmake.yml b/.github/workflows/build-qv2ray-cmake.yml index 9b51bf09..020deb6d 100644 --- a/.github/workflows/build-qv2ray-cmake.yml +++ b/.github/workflows/build-qv2ray-cmake.yml @@ -96,18 +96,23 @@ jobs: pathSource: ./libs/Qv2ray-deps-grpc-${{ matrix.arch }}-windows.7z pathTarget: ./libs # ========================================================================================================= Generate MakeFile and Build + + - uses: actions/setup-node@v1 + if: matrix.platform == 'macos-latest' + with: + node-version: '10.x' + - run: npm install -g appdmg + if: matrix.platform == 'macos-latest' - name: macOS - ${{ matrix.qt_version }} - Generate Dependencies and Build shell: bash if: matrix.platform == 'macos-latest' run: | mkdir build cd build - cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_DEPLOYMENT_TARGET=10.13 - sudo cmake --build . --target package --parallel $(sysctl -n hw.logicalcpu) - cp qv2ray-*.dmg ../ - - name: macOS - Get package name - id: get_package - run: echo ::set-output name=NAME::$(basename qv2ray-*.dmg) + cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_DEPLOYMENT_TARGET=10.13 -DDS_STORE_SCRIPT=ON + sudo cmake --build . --parallel $(sysctl -n hw.logicalcpu) + sudo cmake --install . + sudo appdmg ../assets/package_dmg.json ../Qv2ray.dmg # -------------------------------------------------------- - name: Windows - ${{ matrix.qt_version }} - Generate Dependencies and Build shell: bash @@ -175,14 +180,14 @@ jobs: uses: actions/upload-artifact@master with: name: Qv2ray-${{ github.sha }}.macOS-${{ matrix.arch }}.qt${{ matrix.qt_version }}.dmg - path: ${{ steps.get_package.outputs.NAME }} + path: Qv2ray.dmg - name: macOS - ${{ matrix.qt_version }} - Upload binaries to release uses: svenstaro/upload-release-action@v1-release if: github.event_name == 'release' && matrix.platform == 'macos-latest' && matrix.qt_version == '5.14.2' with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: ${{ steps.get_package.outputs.NAME }} - asset_name: ${{ steps.get_package.outputs.NAME }} + file: Qv2ray.dmg + asset_name: Qv2ray-${{ steps.get_version.outputs.VERSION }}.macOS-${{ matrix.arch }}.dmg tag: ${{ github.ref }} overwrite: true # -------------------------------------------------------- diff --git a/assets/package_dmg.json.in b/assets/package_dmg.json.in new file mode 100644 index 00000000..fb1b9dc8 --- /dev/null +++ b/assets/package_dmg.json.in @@ -0,0 +1,17 @@ + +{ + "title": "Qv2ray @QV2RAY_VERSION_STRING@", + "icon": "@CMAKE_SOURCE_DIR@/assets/icons/qv2ray.icns", + "background": "@CMAKE_SOURCE_DIR@/assets/CMakeDMGBackground.png", + "icon-size": 128, + "window": { + "size": { + "width": 500, + "height": 365 + } + }, + "contents": [ + { "x": 130, "y": 205, "type": "file", "path": "@CMAKE_BINARY_DIR@/qv2ray.app" }, + { "x": 375, "y": 205, "type": "link", "path": "/Applications" } + ] +} diff --git a/cmake/deployment.cmake b/cmake/deployment.cmake index 9c76eb02..16f011f4 100644 --- a/cmake/deployment.cmake +++ b/cmake/deployment.cmake @@ -68,6 +68,7 @@ if(APPLE) endif() set(CPACK_DMG_BACKGROUND_IMAGE "${CMAKE_SOURCE_DIR}/assets/CMakeDMGBackground.png") + configure_file("${CMAKE_SOURCE_DIR}/assets/package_dmg.json.in" "${CMAKE_SOURCE_DIR}/assets/package_dmg.json" @ONLY) endif() include(CPack) From 52136212d7dc716652b2063472b60c11a6fdc0c1 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Fri, 17 Apr 2020 17:41:56 +0800 Subject: [PATCH 87/92] add: Ctrl+Q to quit Qv2ray with a confirm, fixed #520 --- src/ui/w_MainWindow.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/ui/w_MainWindow.cpp b/src/ui/w_MainWindow.cpp index 3a8d9022..de633d07 100644 --- a/src/ui/w_MainWindow.cpp +++ b/src/ui/w_MainWindow.cpp @@ -358,6 +358,13 @@ void MainWindow::keyPressEvent(QKeyEvent *e) this->close(); } } + else if (e->modifiers() & Qt::ControlModifier && e->key() == Qt::Key_Q) + { + if (QvMessageBoxAsk(this, tr("Quit Qv2ray"), tr("Are you sure to exit Qv2ray?"), QMessageBox::No) == QMessageBox::Yes) + { + ExitQv2ray(); + } + } } void MainWindow::keyReleaseEvent(QKeyEvent *e) From 1039e537d0e7c72271fd98ac52d9d4e5f72f6433 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Fri, 17 Apr 2020 18:29:50 +0800 Subject: [PATCH 88/92] fix: this resolved #495 --- src/core/handler/ConfigHandler.cpp | 14 +++++++--- src/core/handler/ConfigHandler.hpp | 3 ++- src/ui/w_MainWindow.cpp | 34 +++++++++++++++++++------ src/ui/widgets/ConnectionItemWidget.cpp | 13 ++++++---- 4 files changed, 46 insertions(+), 18 deletions(-) diff --git a/src/core/handler/ConfigHandler.cpp b/src/core/handler/ConfigHandler.cpp index f8d9db41..35e0cc82 100644 --- a/src/core/handler/ConfigHandler.cpp +++ b/src/core/handler/ConfigHandler.cpp @@ -209,15 +209,21 @@ namespace Qv2ray::core::handlers return NullGroupId; } - - const optional QvConfigHandler::ClearConnectionUsage(const ConnectionId &id) + void QvConfigHandler::ClearGroupUsage(const GroupId &id) { - CheckConnectionExistance(id); + for (const auto &conn : groups[id].connections) + { + ClearConnectionUsage(conn); + } + } + void QvConfigHandler::ClearConnectionUsage(const ConnectionId &id) + { + CheckConnectionExistanceEx(id, nothing); connections[id].upLinkData = 0; connections[id].downLinkData = 0; emit OnStatsAvailable(id, 0, 0, 0, 0); PluginHost->Send_ConnectionStatsEvent({ GetDisplayName(id), 0, 0, 0, 0 }); - return {}; + return; } const optional QvConfigHandler::RenameConnection(const ConnectionId &id, const QString &newName) diff --git a/src/core/handler/ConfigHandler.hpp b/src/core/handler/ConfigHandler.hpp index 7d046019..8dc430a9 100644 --- a/src/core/handler/ConfigHandler.hpp +++ b/src/core/handler/ConfigHandler.hpp @@ -79,7 +79,8 @@ namespace Qv2ray::core::handlers // // Connection Operations. bool UpdateConnection(const ConnectionId &id, const CONFIGROOT &root, bool skipRestart = false); - const optional ClearConnectionUsage(const ConnectionId &id); + void ClearGroupUsage(const GroupId &id); + void ClearConnectionUsage(const ConnectionId &id); const optional DeleteConnection(const ConnectionId &id); const optional RenameConnection(const ConnectionId &id, const QString &newName); const optional MoveConnectionGroup(const ConnectionId &id, const GroupId &newGroupId); diff --git a/src/ui/w_MainWindow.cpp b/src/ui/w_MainWindow.cpp index de633d07..790b4b3c 100644 --- a/src/ui/w_MainWindow.cpp +++ b/src/ui/w_MainWindow.cpp @@ -230,7 +230,6 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) connectionListRCM_Menu->addAction(action_RCM_Delete); connect(action_RCM_Start, &QAction::triggered, this, &MainWindow::on_action_StartThis_triggered); connect(action_RCM_SetAutoConnection, &QAction::triggered, this, &MainWindow::on_action_RCM_SetAutoConnection_triggered); - connect(action_RCM_Edit, &QAction::triggered, this, &MainWindow::on_action_RCM_EditThis_triggered); connect(action_RCM_EditJson, &QAction::triggered, this, &MainWindow::on_action_RCM_EditAsJson_triggered); connect(action_RCM_EditComplex, &QAction::triggered, this, &MainWindow::on_action_RCM_EditAsComplex_triggered); @@ -481,10 +480,16 @@ void MainWindow::on_connectionListWidget_customContextMenuRequested(const QPoint auto item = connectionListWidget->itemAt(connectionListWidget->mapFromGlobal(_pos)); if (item != nullptr) { - if (GetItemWidget(item)->IsConnection()) - { - connectionListRCM_Menu->popup(_pos); - } + bool isConnection = GetItemWidget(item)->IsConnection(); + // Disable connection-specific settings. + action_RCM_Start->setEnabled(isConnection); + action_RCM_SetAutoConnection->setEnabled(isConnection); + action_RCM_Edit->setEnabled(isConnection); + action_RCM_EditJson->setEnabled(isConnection); + action_RCM_EditComplex->setEnabled(isConnection); + action_RCM_Rename->setEnabled(isConnection); + action_RCM_Duplicate->setEnabled(isConnection); + connectionListRCM_Menu->popup(_pos); } } @@ -495,9 +500,16 @@ void MainWindow::on_action_RCM_DeleteThese_triggered() for (auto item : connectionListWidget->selectedItems()) { auto widget = GetItemWidget(item); - if (widget->IsConnection()) + if (widget) { - connlist.append(get<1>(widget->Identifier())); + if (widget->IsConnection()) + { + connlist.append(get<1>(widget->Identifier())); + } + else + { + connlist.append(ConnectionManager->GetGroupMetaObject(get<0>(widget->Identifier())).connections); + } } } @@ -914,7 +926,13 @@ void MainWindow::on_action_RCM_ClearUsage_triggered() if (current != nullptr) { auto widget = GetItemWidget(current); - ConnectionManager->ClearConnectionUsage(get<1>(widget->Identifier())); + if (widget) + { + if (widget->IsConnection()) + ConnectionManager->ClearConnectionUsage(get<1>(widget->Identifier())); + else + ConnectionManager->ClearGroupUsage(get<0>(widget->Identifier())); + } } } diff --git a/src/ui/widgets/ConnectionItemWidget.cpp b/src/ui/widgets/ConnectionItemWidget.cpp index cadaeab7..ebfa22e4 100644 --- a/src/ui/widgets/ConnectionItemWidget.cpp +++ b/src/ui/widgets/ConnectionItemWidget.cpp @@ -145,11 +145,14 @@ void ConnectionItemWidget::CancelRename() void ConnectionItemWidget::BeginRename() { - stackedWidget->setCurrentIndex(1); - renameTxt->setStyle(QStyleFactory::create("Fusion")); - renameTxt->setStyleSheet("background-color: " + this->palette().color(this->backgroundRole()).name(QColor::HexRgb)); - renameTxt->setText(originalItemName); - renameTxt->setFocus(); + if (IsConnection()) + { + stackedWidget->setCurrentIndex(1); + renameTxt->setStyle(QStyleFactory::create("Fusion")); + renameTxt->setStyleSheet("background-color: " + this->palette().color(this->backgroundRole()).name(QColor::HexRgb)); + renameTxt->setText(originalItemName); + renameTxt->setFocus(); + } } ConnectionItemWidget::~ConnectionItemWidget() From 4bed1ac7244bd025419460cc175a75fab5234a58 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Fri, 17 Apr 2020 18:46:51 +0800 Subject: [PATCH 89/92] fix: fixed #501 and added SystemProxy support for complex connection config - KNOWNISSUE: 1 --- src/base/models/QvSettingsObject.hpp | 3 +- src/core/handler/ConfigHandler.hpp | 2 +- src/core/handler/KernelInstanceHandler.cpp | 6 +-- src/core/handler/KernelInstanceHandler.hpp | 2 +- src/ui/w_MainWindow.cpp | 21 +++++--- src/ui/w_MainWindow.hpp | 4 +- src/ui/w_MainWindow_extra.cpp | 58 +++++++--------------- src/ui/w_PreferencesWindow.cpp | 8 +++ src/ui/w_PreferencesWindow.hpp | 2 + src/ui/w_PreferencesWindow.ui | 36 ++++++++++---- 10 files changed, 78 insertions(+), 64 deletions(-) diff --git a/src/base/models/QvSettingsObject.hpp b/src/base/models/QvSettingsObject.hpp index 6c216d86..3e1561ad 100644 --- a/src/base/models/QvSettingsObject.hpp +++ b/src/base/models/QvSettingsObject.hpp @@ -89,13 +89,14 @@ namespace Qv2ray::base::config { QString theme; QString language; + bool quietMode; bool useDarkTheme; bool useDarkTrayIcon; int maximumLogLines; Qv2rayUIConfig() : theme("Fusion"), language("en_US"), useDarkTheme(false), useDarkTrayIcon(true), maximumLogLines(500) { } - XTOSTRUCT(O(theme, language, useDarkTheme, useDarkTrayIcon, maximumLogLines)) + XTOSTRUCT(O(theme, language, quietMode, useDarkTheme, useDarkTrayIcon, maximumLogLines)) }; struct Qv2rayRouteConfig_Impl diff --git a/src/core/handler/ConfigHandler.hpp b/src/core/handler/ConfigHandler.hpp index 8dc430a9..82e3f699 100644 --- a/src/core/handler/ConfigHandler.hpp +++ b/src/core/handler/ConfigHandler.hpp @@ -125,7 +125,7 @@ namespace Qv2ray::core::handlers void OnGroupDeleted(const GroupId &id, const QList &connections); // void OnSubscriptionUpdateFinished(const GroupId &id); - void OnConnected(const ConnectionId &id); + void OnConnected(const ConnectionId &id, const QMap &inboundPorts); void OnDisconnected(const ConnectionId &id); void OnKernelCrashed(const ConnectionId &id, const QString &errMessage); // diff --git a/src/core/handler/KernelInstanceHandler.cpp b/src/core/handler/KernelInstanceHandler.cpp index f73dc972..6bbe64e2 100644 --- a/src/core/handler/KernelInstanceHandler.cpp +++ b/src/core/handler/KernelInstanceHandler.cpp @@ -159,7 +159,7 @@ namespace Qv2ray::core::handlers if (!result.has_value()) { - emit OnConnected(currentConnectionId); + emit OnConnected(currentConnectionId, inboundPorts); PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), inboundPorts, Events::Connectivity::QvConnecticity_Connected }); } else @@ -188,7 +188,7 @@ namespace Qv2ray::core::handlers bool result = kernel->StartKernel(); if (result) { - emit OnConnected(currentConnectionId); + emit OnConnected(currentConnectionId, inboundPorts); return {}; } else @@ -203,7 +203,7 @@ namespace Qv2ray::core::handlers auto result = vCoreInstance->StartConnection(fullConfig); if (!result.has_value()) { - emit OnConnected(currentConnectionId); + emit OnConnected(currentConnectionId, inboundPorts); PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), inboundPorts, Events::Connectivity::QvConnecticity_Connected }); } else diff --git a/src/core/handler/KernelInstanceHandler.hpp b/src/core/handler/KernelInstanceHandler.hpp index 498e3948..b742e6e9 100644 --- a/src/core/handler/KernelInstanceHandler.hpp +++ b/src/core/handler/KernelInstanceHandler.hpp @@ -28,7 +28,7 @@ namespace Qv2ray::core::handlers } signals: - void OnConnected(const ConnectionId &id); + void OnConnected(const ConnectionId &id, const QMap &inboundPorts); void OnDisconnected(const ConnectionId &id); void OnCrashed(const ConnectionId &id, const QString &errMessage); void OnStatsDataAvailable(const ConnectionId &id, const quint64 uploadSpeed, const quint64 downloadSpeed); diff --git a/src/ui/w_MainWindow.cpp b/src/ui/w_MainWindow.cpp index 790b4b3c..f1a62da9 100644 --- a/src/ui/w_MainWindow.cpp +++ b/src/ui/w_MainWindow.cpp @@ -195,7 +195,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) connect(tray_action_Stop, &QAction::triggered, ConnectionManager, &QvConfigHandler::StopConnection); connect(tray_action_Restart, &QAction::triggered, ConnectionManager, &QvConfigHandler::RestartConnection); connect(tray_action_Quit, &QAction::triggered, this, &MainWindow::on_actionExit_triggered); - connect(tray_action_SetSystemProxy, &QAction::triggered, this, &MainWindow::MWSetSystemProxy); + // connect(tray_action_SetSystemProxy, &QAction::triggered, this, &MainWindow::MWSetSystemProxy); connect(tray_action_ClearSystemProxy, &QAction::triggered, this, &MainWindow::MWClearSystemProxy); connect(&hTray, &QSystemTrayIcon::activated, this, &MainWindow::on_activatedTray); // @@ -589,7 +589,10 @@ void MainWindow::OnDisconnected(const ConnectionId &id) tray_SystemProxyMenu->setEnabled(false); lastConnectedId = id; locateBtn->setEnabled(false); - this->hTray.showMessage("Qv2ray", tr("Disconnected from: ") + GetDisplayName(id), this->windowIcon()); + if (!GlobalConfig.uiConfig.quietMode) + { + this->hTray.showMessage("Qv2ray", tr("Disconnected from: ") + GetDisplayName(id), this->windowIcon()); + } hTray.setToolTip(TRAY_TOOLTIP_PREFIX); netspeedLabel->setText("0.00 B/s" NEWLINE "0.00 B/s"); dataamountLabel->setText("0.00 B" NEWLINE "0.00 B"); @@ -600,7 +603,7 @@ void MainWindow::OnDisconnected(const ConnectionId &id) } } -void MainWindow::OnConnected(const ConnectionId &id) +void MainWindow::OnConnected(const ConnectionId &id, const QMap &inboundPorts) { Q_UNUSED(id) tray_action_Start->setEnabled(false); @@ -611,14 +614,17 @@ void MainWindow::OnConnected(const ConnectionId &id) locateBtn->setEnabled(true); on_clearlogButton_clicked(); auto name = GetDisplayName(id); - this->hTray.showMessage("Qv2ray", tr("Connected: ") + name, this->windowIcon()); + if (!GlobalConfig.uiConfig.quietMode) + { + this->hTray.showMessage("Qv2ray", tr("Connected: ") + name, this->windowIcon()); + } hTray.setToolTip(TRAY_TOOLTIP_PREFIX NEWLINE + tr("Connected: ") + name); connetionStatusLabel->setText(tr("Connected: ") + name); // ConnectionManager->StartLatencyTest(id); if (GlobalConfig.inboundConfig.setSystemProxy) { - MWSetSystemProxy(); + MWSetSystemProxy(inboundPorts["http"], inboundPorts["socks"]); } } @@ -915,7 +921,10 @@ void MainWindow::on_action_RCM_SetAutoConnection_triggered() auto widget = GetItemWidget(current); auto &conn = get<1>(widget->Identifier()); GlobalConfig.autoStartId = conn.toString(); - hTray.showMessage(tr("Set auto connection"), tr("Set %1 as auto connect.").arg(GetDisplayName(conn))); + if (!GlobalConfig.uiConfig.quietMode) + { + hTray.showMessage(tr("Set auto connection"), tr("Set %1 as auto connect.").arg(GetDisplayName(conn))); + } SaveGlobalSettings(); } } diff --git a/src/ui/w_MainWindow.hpp b/src/ui/w_MainWindow.hpp index fbcba6e6..97636333 100644 --- a/src/ui/w_MainWindow.hpp +++ b/src/ui/w_MainWindow.hpp @@ -82,7 +82,7 @@ class MainWindow void ToggleVisibility(); void OnEditRequested(const ConnectionId &id); void OnEditJsonRequested(const ConnectionId &id); - void OnConnected(const ConnectionId &id); + void OnConnected(const ConnectionId &id, const QMap &inboundPorts); void OnDisconnected(const ConnectionId &id); // void OnStatsAvailable(const ConnectionId &id, const quint64 upS, const quint64 downS, const quint64 upD, const quint64 downD); @@ -153,7 +153,7 @@ class MainWindow QAction *action_RCM_toQvLog = new QAction(tr("Switch to Qv2ray log"), this); // ConnectionId lastConnectedId; - void MWSetSystemProxy(); + void MWSetSystemProxy(int httpPort, int socksPort); void MWClearSystemProxy(); void CheckSubscriptionsUpdate(); // diff --git a/src/ui/w_MainWindow_extra.cpp b/src/ui/w_MainWindow_extra.cpp index 22107b1c..ba77db32 100644 --- a/src/ui/w_MainWindow_extra.cpp +++ b/src/ui/w_MainWindow_extra.cpp @@ -2,56 +2,36 @@ #include "components/proxy/QvProxyConfigurator.hpp" #include "w_MainWindow.hpp" -void MainWindow::MWSetSystemProxy() +void MainWindow::MWSetSystemProxy(int httpPort, int socksPort) { - bool httpEnabled = GlobalConfig.inboundConfig.useHTTP; - bool socksEnabled = GlobalConfig.inboundConfig.useSocks; - // - bool isComplex = IsComplexConfig(ConnectionManager->CurrentConnection()); + bool httpEnabled = httpPort != 0; + bool socksEnabled = socksPort != 0; - if (!isComplex) + QString proxyAddress; + + if (httpEnabled || socksEnabled) { - // Is simple config and we will try to set system proxy. - LOG(MODULE_UI, "Preparing to set system proxy") - // - QString proxyAddress; - bool canSetSystemProxy = true; - - // Not using PAC - if (httpEnabled || socksEnabled) + proxyAddress = "127.0.0.1"; + SetSystemProxy(proxyAddress, httpPort, socksPort); + if (!GlobalConfig.uiConfig.quietMode) { - // Not use PAC, System proxy should use HTTP or SOCKS - LOG(MODULE_PROXY, "Setting up system proxy.") - // A 'proxy host' should be a host WITHOUT `http://` uri scheme - proxyAddress = "127.0.0.1"; - } - else - { - LOG(MODULE_PROXY, "Neither of HTTP nor SOCKS is enabled, cannot set system proxy.") - QvMessageBoxWarn(this, tr("Cannot set system proxy"), tr("Both HTTP and SOCKS inbounds are not enabled")); - canSetSystemProxy = false; - } - - if (canSetSystemProxy) - { - LOG(MODULE_UI, "Setting system proxy for simple config.") - auto httpPort = GlobalConfig.inboundConfig.useHTTP ? GlobalConfig.inboundConfig.http_port : 0; - auto socksPort = GlobalConfig.inboundConfig.useSocks ? GlobalConfig.inboundConfig.socks_port : 0; - // - SetSystemProxy(proxyAddress, httpPort, socksPort); hTray.showMessage("Qv2ray", tr("System proxy configured.")); } } else { - hTray.showMessage("Qv2ray", tr("Didn't set proxy for complex config."), windowIcon()); + LOG(MODULE_PROXY, "Neither of HTTP nor SOCKS is enabled, cannot set system proxy.") + QvMessageBoxWarn(this, tr("Cannot set system proxy"), tr("Both HTTP and SOCKS inbounds are not enabled")); } } void MainWindow::MWClearSystemProxy() { ClearSystemProxy(); - hTray.showMessage("Qv2ray", tr("System proxy removed.")); + if (!GlobalConfig.uiConfig.quietMode) + { + hTray.showMessage("Qv2ray", tr("System proxy removed.")); + } } void MainWindow::CheckSubscriptionsUpdate() @@ -59,7 +39,7 @@ void MainWindow::CheckSubscriptionsUpdate() QStringList updateList; auto subscriptions = ConnectionManager->Subscriptions(); - for (auto entry : subscriptions) + for (const auto &entry : subscriptions) { const auto info = ConnectionManager->GetGroupMetaObject(entry); // @@ -67,8 +47,8 @@ void MainWindow::CheckSubscriptionsUpdate() if (info.updateInterval == 0) continue; // - auto lastRenewDate = QDateTime::fromTime_t(info.lastUpdated); - auto renewTime = lastRenewDate.addSecs(info.updateInterval * 86400); + const auto &lastRenewDate = QDateTime::fromTime_t(info.lastUpdated); + const auto &renewTime = lastRenewDate.addSecs(info.updateInterval * 86400); LOG(MODULE_SUBSCRIPTION, // "Subscription \"" + info.displayName + "\": " + // NEWLINE + " --> Last renewal time: " + lastRenewDate.toString() + // @@ -78,7 +58,7 @@ void MainWindow::CheckSubscriptionsUpdate() if (renewTime <= QDateTime::currentDateTime()) { LOG(MODULE_SUBSCRIPTION, "Subscription: " + info.displayName + " needs to be updated.") - updateList.append(entry.toString()); + updateList.append(info.displayName); } } diff --git a/src/ui/w_PreferencesWindow.cpp b/src/ui/w_PreferencesWindow.cpp index f28f0867..e653cd45 100644 --- a/src/ui/w_PreferencesWindow.cpp +++ b/src/ui/w_PreferencesWindow.cpp @@ -137,6 +137,8 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent), Current qvNetworkUATxt->setText(CurrentConfig.networkConfig.userAgent); qvUseProxyCB->setChecked(CurrentConfig.networkConfig.useCustomProxy); // + quietModeCB->setChecked(CurrentConfig.uiConfig.quietMode); + // // Advanced config. setAllowInsecureCB->setChecked(CurrentConfig.advancedConfig.setAllowInsecure); setAllowInsecureCiphersCB->setChecked(CurrentConfig.advancedConfig.setAllowInsecureCiphers); @@ -1183,3 +1185,9 @@ void PreferencesWindow::on_setAllowInsecureCiphersCB_stateChanged(int arg1) } CurrentConfig.advancedConfig.setAllowInsecureCiphers = arg1 == Qt::Checked; } + +void PreferencesWindow::on_quietModeCB_stateChanged(int arg1) +{ + LOADINGCHECK + CurrentConfig.uiConfig.quietMode = arg1 == Qt::Checked; +} diff --git a/src/ui/w_PreferencesWindow.hpp b/src/ui/w_PreferencesWindow.hpp index fc5dec20..051a4587 100644 --- a/src/ui/w_PreferencesWindow.hpp +++ b/src/ui/w_PreferencesWindow.hpp @@ -176,6 +176,8 @@ class PreferencesWindow void on_setAllowInsecureCiphersCB_stateChanged(int arg1); + void on_quietModeCB_stateChanged(int arg1); + private: // RouteSettingsMatrixWidget *routeSettingsWidget; diff --git a/src/ui/w_PreferencesWindow.ui b/src/ui/w_PreferencesWindow.ui index 98720403..feb043bd 100644 --- a/src/ui/w_PreferencesWindow.ui +++ b/src/ui/w_PreferencesWindow.ui @@ -185,19 +185,29 @@ - + + + + Enabled + + + + Auto Connect - + - - + + + + + - Group + Config @@ -216,18 +226,22 @@ - - + + - Connection + Group - - - + + + + Quiet Mode + + + From 5783c9eb61e386253ac860f0e861adac3487408b Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Fri, 17 Apr 2020 21:01:16 +0800 Subject: [PATCH 90/92] fix: fixed bug introduced in the previous commit --- src/core/CoreUtils.cpp | 6 +++--- src/core/CoreUtils.hpp | 4 ++-- src/core/handler/ConfigHandler.hpp | 2 +- src/core/handler/KernelInstanceHandler.cpp | 23 ++++++++++++---------- src/core/handler/KernelInstanceHandler.hpp | 8 +++++++- src/ui/w_MainWindow.cpp | 6 +++--- src/ui/w_MainWindow.hpp | 4 ++-- src/ui/w_MainWindow_extra.cpp | 9 ++++++--- 8 files changed, 37 insertions(+), 25 deletions(-) diff --git a/src/core/CoreUtils.cpp b/src/core/CoreUtils.cpp index 7d1d5f4b..282a0b7c 100644 --- a/src/core/CoreUtils.cpp +++ b/src/core/CoreUtils.cpp @@ -163,7 +163,7 @@ namespace Qv2ray::core return ConnectionManager->GetConnectionMetaObject(id).groupId; } - const QMap GetInboundPorts(const CONFIGROOT &root) + const QMap GetConfigInboundPorts(const CONFIGROOT &root) { if (!root.contains("inbounds")) { @@ -178,8 +178,8 @@ namespace Qv2ray::core return inboundPorts; } - const QMap GetInboundPorts(const ConnectionId &id) + const QMap GetConfigInboundPorts(const ConnectionId &id) { - return GetInboundPorts(ConnectionManager->GetConnectionRoot(id)); + return GetConfigInboundPorts(ConnectionManager->GetConnectionRoot(id)); } } // namespace Qv2ray::core diff --git a/src/core/CoreUtils.hpp b/src/core/CoreUtils.hpp index 50182ca8..3a1f24ce 100644 --- a/src/core/CoreUtils.hpp +++ b/src/core/CoreUtils.hpp @@ -45,8 +45,8 @@ namespace Qv2ray::core // const GroupId GetConnectionGroupId(const ConnectionId &id); // - const QMap GetInboundPorts(const CONFIGROOT &root); - const QMap GetInboundPorts(const ConnectionId &id); + const QMap GetConfigInboundPorts(const CONFIGROOT &root); + const QMap GetConfigInboundPorts(const ConnectionId &id); // } // namespace Qv2ray::core diff --git a/src/core/handler/ConfigHandler.hpp b/src/core/handler/ConfigHandler.hpp index 82e3f699..8dc430a9 100644 --- a/src/core/handler/ConfigHandler.hpp +++ b/src/core/handler/ConfigHandler.hpp @@ -125,7 +125,7 @@ namespace Qv2ray::core::handlers void OnGroupDeleted(const GroupId &id, const QList &connections); // void OnSubscriptionUpdateFinished(const GroupId &id); - void OnConnected(const ConnectionId &id, const QMap &inboundPorts); + void OnConnected(const ConnectionId &id); void OnDisconnected(const ConnectionId &id); void OnKernelCrashed(const ConnectionId &id, const QString &errMessage); // diff --git a/src/core/handler/KernelInstanceHandler.cpp b/src/core/handler/KernelInstanceHandler.cpp index 6bbe64e2..5a26b38c 100644 --- a/src/core/handler/KernelInstanceHandler.cpp +++ b/src/core/handler/KernelInstanceHandler.cpp @@ -31,8 +31,9 @@ namespace Qv2ray::core::handlers } activeKernels.clear(); this->root = root; + bool isComplex = IsComplexConfig(root); auto fullConfig = GenerateRuntimeConfig(root); - auto inboundPorts = GetInboundPorts(fullConfig); + inboundPorts = GetConfigInboundPorts(fullConfig); PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), inboundPorts, Events::Connectivity::QvConnecticity_Connecting }); QList> inboundInfo; for (const auto &inbound_v : fullConfig["inbounds"].toArray()) @@ -41,7 +42,6 @@ namespace Qv2ray::core::handlers inboundInfo.push_back({ inbound["protocol"].toString(), inbound["port"].toInt(), inbound["tag"].toString() }); } // - bool isComplex = IsComplexConfig(root); if (GlobalConfig.pluginConfig.v2rayIntegration) { if (isComplex) @@ -136,6 +136,7 @@ namespace Qv2ray::core::handlers routing["rules"] = newRules; fullConfig["routing"] = routing; } + // ================================================================================================ // currentConnectionId = id; lastConnectionId = id; @@ -147,19 +148,21 @@ namespace Qv2ray::core::handlers if (!status) { LOG(MODULE_CONNECTION, "Plugin Kernel: " + kernel + " failed to start.") + break; } } if (!success) { + StopConnection(); return tr("A plugin kernel failed to start. Please check the outbound settings."); } // auto result = vCoreInstance->StartConnection(fullConfig); - + // if (!result.has_value()) { - emit OnConnected(currentConnectionId, inboundPorts); + emit OnConnected(currentConnectionId); PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), inboundPorts, Events::Connectivity::QvConnecticity_Connected }); } else @@ -167,7 +170,7 @@ namespace Qv2ray::core::handlers PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), {}, Events::Connectivity::QvConnecticity_Disconnected }); } return result; - } // namespace Qv2ray::core::handlers + } else { auto firstOutbound = fullConfig["outbounds"].toArray().first().toObject(); @@ -188,7 +191,7 @@ namespace Qv2ray::core::handlers bool result = kernel->StartKernel(); if (result) { - emit OnConnected(currentConnectionId, inboundPorts); + emit OnConnected(currentConnectionId); return {}; } else @@ -201,14 +204,14 @@ namespace Qv2ray::core::handlers currentConnectionId = id; lastConnectionId = id; auto result = vCoreInstance->StartConnection(fullConfig); - if (!result.has_value()) + if (result.has_value()) { - emit OnConnected(currentConnectionId, inboundPorts); - PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), inboundPorts, Events::Connectivity::QvConnecticity_Connected }); + PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), {}, Events::Connectivity::QvConnecticity_Disconnected }); } else { - PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), {}, Events::Connectivity::QvConnecticity_Disconnected }); + emit OnConnected(currentConnectionId); + PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), inboundPorts, Events::Connectivity::QvConnecticity_Connected }); } return result; } diff --git a/src/core/handler/KernelInstanceHandler.hpp b/src/core/handler/KernelInstanceHandler.hpp index b742e6e9..d766a97c 100644 --- a/src/core/handler/KernelInstanceHandler.hpp +++ b/src/core/handler/KernelInstanceHandler.hpp @@ -26,9 +26,13 @@ namespace Qv2ray::core::handlers { return id == currentConnectionId; } + const QMap InboundPorts() const + { + return inboundPorts; + } signals: - void OnConnected(const ConnectionId &id, const QMap &inboundPorts); + void OnConnected(const ConnectionId &id); void OnDisconnected(const ConnectionId &id); void OnCrashed(const ConnectionId &id, const QString &errMessage); void OnStatsDataAvailable(const ConnectionId &id, const quint64 uploadSpeed, const quint64 downloadSpeed); @@ -42,9 +46,11 @@ namespace Qv2ray::core::handlers private: QMap> kernels; QMap activeKernels; + QMap inboundPorts; CONFIGROOT root; V2rayKernelInstance *vCoreInstance = nullptr; ConnectionId currentConnectionId = NullConnectionId; ConnectionId lastConnectionId = NullConnectionId; }; + inline const KernelInstanceHandler *KernelInstance; } // namespace Qv2ray::core::handlers diff --git a/src/ui/w_MainWindow.cpp b/src/ui/w_MainWindow.cpp index f1a62da9..f8fb0fdc 100644 --- a/src/ui/w_MainWindow.cpp +++ b/src/ui/w_MainWindow.cpp @@ -195,7 +195,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) connect(tray_action_Stop, &QAction::triggered, ConnectionManager, &QvConfigHandler::StopConnection); connect(tray_action_Restart, &QAction::triggered, ConnectionManager, &QvConfigHandler::RestartConnection); connect(tray_action_Quit, &QAction::triggered, this, &MainWindow::on_actionExit_triggered); - // connect(tray_action_SetSystemProxy, &QAction::triggered, this, &MainWindow::MWSetSystemProxy); + connect(tray_action_SetSystemProxy, &QAction::triggered, this, &MainWindow::MWSetSystemProxy); connect(tray_action_ClearSystemProxy, &QAction::triggered, this, &MainWindow::MWClearSystemProxy); connect(&hTray, &QSystemTrayIcon::activated, this, &MainWindow::on_activatedTray); // @@ -603,7 +603,7 @@ void MainWindow::OnDisconnected(const ConnectionId &id) } } -void MainWindow::OnConnected(const ConnectionId &id, const QMap &inboundPorts) +void MainWindow::OnConnected(const ConnectionId &id) { Q_UNUSED(id) tray_action_Start->setEnabled(false); @@ -624,7 +624,7 @@ void MainWindow::OnConnected(const ConnectionId &id, const QMap &i ConnectionManager->StartLatencyTest(id); if (GlobalConfig.inboundConfig.setSystemProxy) { - MWSetSystemProxy(inboundPorts["http"], inboundPorts["socks"]); + MWSetSystemProxy(); } } diff --git a/src/ui/w_MainWindow.hpp b/src/ui/w_MainWindow.hpp index 97636333..fbcba6e6 100644 --- a/src/ui/w_MainWindow.hpp +++ b/src/ui/w_MainWindow.hpp @@ -82,7 +82,7 @@ class MainWindow void ToggleVisibility(); void OnEditRequested(const ConnectionId &id); void OnEditJsonRequested(const ConnectionId &id); - void OnConnected(const ConnectionId &id, const QMap &inboundPorts); + void OnConnected(const ConnectionId &id); void OnDisconnected(const ConnectionId &id); // void OnStatsAvailable(const ConnectionId &id, const quint64 upS, const quint64 downS, const quint64 upD, const quint64 downD); @@ -153,7 +153,7 @@ class MainWindow QAction *action_RCM_toQvLog = new QAction(tr("Switch to Qv2ray log"), this); // ConnectionId lastConnectedId; - void MWSetSystemProxy(int httpPort, int socksPort); + void MWSetSystemProxy(); void MWClearSystemProxy(); void CheckSubscriptionsUpdate(); // diff --git a/src/ui/w_MainWindow_extra.cpp b/src/ui/w_MainWindow_extra.cpp index ba77db32..eb059a48 100644 --- a/src/ui/w_MainWindow_extra.cpp +++ b/src/ui/w_MainWindow_extra.cpp @@ -2,10 +2,13 @@ #include "components/proxy/QvProxyConfigurator.hpp" #include "w_MainWindow.hpp" -void MainWindow::MWSetSystemProxy(int httpPort, int socksPort) +void MainWindow::MWSetSystemProxy() { - bool httpEnabled = httpPort != 0; - bool socksEnabled = socksPort != 0; + auto inboundPorts = KernelInstance->InboundPorts(); + bool httpEnabled = inboundPorts.contains("http"); + bool socksEnabled = inboundPorts.contains("socks"); + auto httpPort = inboundPorts["http"]; + auto socksPort = inboundPorts["socks"]; QString proxyAddress; From 51e80b841c2c20ddedc8781cbb00a891bb31ecfe Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Fri, 17 Apr 2020 22:06:56 +0800 Subject: [PATCH 91/92] add: added colorful system tray icon, fixed #214 --- .../icons/ui_dark/design/tray-connected.svg | 310 +++++++++++++++ .../icons/ui_dark/design/tray-systemproxy.svg | 309 +++++++++++++++ assets/icons/ui_dark/tray-connected.png | Bin 0 -> 3106 bytes assets/icons/ui_dark/tray-systemproxy.png | Bin 0 -> 2927 bytes .../icons/ui_light/design/tray-connected.svg | 356 +++++++++++++++++ .../ui_light/design/tray-systemproxy.svg | 359 ++++++++++++++++++ assets/icons/ui_light/tray-connected.png | Bin 0 -> 3729 bytes assets/icons/ui_light/tray-systemproxy.png | Bin 0 -> 3770 bytes makespec/BUILDVERSION | 2 +- resources.qrc | 4 + src/base/Qv2rayBase.hpp | 6 +- src/components/plugins/toolbar/QvToolbar.cpp | 12 +- src/core/handler/ConfigHandler.hpp | 5 - src/core/handler/KernelInstanceHandler.cpp | 11 +- src/ui/w_MainWindow.cpp | 10 +- src/ui/w_MainWindow_extra.cpp | 2 + 16 files changed, 1365 insertions(+), 21 deletions(-) create mode 100644 assets/icons/ui_dark/design/tray-connected.svg create mode 100644 assets/icons/ui_dark/design/tray-systemproxy.svg create mode 100644 assets/icons/ui_dark/tray-connected.png create mode 100644 assets/icons/ui_dark/tray-systemproxy.png create mode 100644 assets/icons/ui_light/design/tray-connected.svg create mode 100644 assets/icons/ui_light/design/tray-systemproxy.svg create mode 100644 assets/icons/ui_light/tray-connected.png create mode 100644 assets/icons/ui_light/tray-systemproxy.png diff --git a/assets/icons/ui_dark/design/tray-connected.svg b/assets/icons/ui_dark/design/tray-connected.svg new file mode 100644 index 00000000..ca95e7bd --- /dev/null +++ b/assets/icons/ui_dark/design/tray-connected.svg @@ -0,0 +1,310 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/assets/icons/ui_dark/design/tray-systemproxy.svg b/assets/icons/ui_dark/design/tray-systemproxy.svg new file mode 100644 index 00000000..fd542395 --- /dev/null +++ b/assets/icons/ui_dark/design/tray-systemproxy.svg @@ -0,0 +1,309 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/assets/icons/ui_dark/tray-connected.png b/assets/icons/ui_dark/tray-connected.png new file mode 100644 index 0000000000000000000000000000000000000000..10dc8cd0d46e760d31e402e6fa4a818451bf1565 GIT binary patch literal 3106 zcmZuzcU05M681 zL&t{{0i%GxrCwTS!pnVsy+7`rGy9z_XJ=<-XXi_?wlw8n6=DSdfWzDjX-h}qKLTZ< z_r3bqemX!xjm+(#^cD^EB+%2k=+veT=wDo(-g7t4vI|CgYR4mL_SU_O1#HXw=rI-&9 z6LZFAp2NuiFCA`AAw@GVFeDpSjBHyRJyuwF0sMr-fMAhX1;Z(G5~hq<@BIKkP8nxiE0jF5i#F zY33>{v;zWNc9u0eLDExl4O9MBMV@kUpS(c%q9<^xC0nJeTCf>rD^_HcF$4{)u-<=E zyuuh_2@IUr7RH|&*aHtgd6SNkT!L7m(P7x^!bn;E$k<|@E?5pvC`c0UkUxAwbU6?`}1IB5YBQRV*6DBlC|`Ip?P=}oF%m~Z*iQJzK{+8 zCwvG(b;&1KqF7qQI1$zT56Q~A%s-WX@#s#MdUp3mQ0FU z<3(IK04fcYt~f((!SgS!LbzlOY%KU=1y~y`06jguM2FfvQ5W$Ocp?)2tgVeuG()syrsf8ezb>}Y7Cgn_c&#!5LT-!NpYkB+0%_gHvT#7nA7OrySuyVCeZ+3p3|e4 zx4L_@x7vwqnzEnBKO8SCEL=)&%?<=T>FH525Q{}n`EJu7)%4}&`!DaXB=_D92uPIg zXX(FrIUf-|B~=}uRzKk6o;hjFr+tpZNeSQCG_nY5miT<&HYMWP8%u7}dNO8d=2|R=5RY=<>emnM~ z3O)yaut3Sk$c)a+q@AALe{u7zLz`RVCuz`(@_`MXF(i_4WOOuE&3lOG_%0m!ID`5h ziiS%FW7nf_Y&V3RFY{J?IB(hPSZrgk%3qxwJbou_hCgfBT1rdB($dqh^B>b+-!(JK zP1|G`sM0yG(G|10N_+_@KJAsnB~`*%`kUL@s#;pm7fyVxL&y`&KdIH{M(#A7C3R<# zZ;xu~cdTBSn>hh3gzVvQ;o$-@R;5Ty5yflM!6J=V&Mf?vhxt-5k#r@-r=+ChwvUg^ zV&XNEV9(z&0*XRv!_3nDYp>b%@*LuC^`LLum}GVzugcWd4(z+%o=l|2g6(B$j#OCt z>_)ta&{nwaY@>XT9q(d|$y#50LH9tXBn;z!N~KCTIXk1h7#0mh>dv5cUwuOFNqi*N z>XXg7HCtQ92O#HP`qwCAg_HH=y?#_u~rrJ%% z^*~?W5T9}L1TG?{i{eDe%F4?A#zrk6wd!G$#Rx{9WSQqnZZOMvVjt+v0$N_2c@tXp z;q*wy<$fCP&CmG;H5eC~b|2Zc;Uc8`VO?DvWfC9t2d3Hu1nkc8_EZ!Y zu0uh~?wfP(98fkwl`DeRA$l377VQvV8UJS{2iGXW=V^LnFboR|6D5w_cB+FKR`h-A zOEnByB+3pBa0sE)&x1$k$zAL5rF$&f;!PzCpDsSo;Z*fZ+6XwZF5#psEGRwRYBV2b z;ypiG_<}iiD1)5ukN>a^HQS3y5xe{%Q5qr<-qC@-!E7s;EozULz9 z_cXAqzKDtFJ2yxMdln23M z#sN<75_lZSCUL9M!BHUrz$@$MeBAMwPtcDa(9oVo8AKsTHES*ENb;Y|&J&TFa` z>xc70;a{Nr%;RO3-GA}?&+g%dWdjmR8_j#)d5rt|jj7F)L0fsg{$%rSDuY!b$pvU{ z{?$l~C*=arMd>f~lOdAtu1|+)IqDbz+LDzQ0Y1x^*Cw*oI{9OQ?DOxHgRN?gK8mZ= z;1#W*2pQK`grtHqp(5Jg{#!%JPlNOCrR%{%4Mn$_B4y>|zFT~7aLP z;-}sGDHKY5;H>tz8c0}aj`58y36WUAoD+t{vV7P2LX2DSYM4^U&CNYC8?P1s=R9d` zi6D=46hjpMID96rPyPj7yt zIc=aEUr%P;vxTmr}#G?F1`;^{aCa!(a(eJs={k7;Xd z)z?*1t9tO-h;?WJKkpwvZn4^y@^tJ=(pgx1w&^5_L5`1)m-$aOuNb3jGU!#M*W|Zb z$Sd`J6MAWI$&nHT>h&*HwGNh adj^8kUAYO%N0`$2JYbHpMAjL($Nd*BF`%yi literal 0 HcmV?d00001 diff --git a/assets/icons/ui_dark/tray-systemproxy.png b/assets/icons/ui_dark/tray-systemproxy.png new file mode 100644 index 0000000000000000000000000000000000000000..035ddabdac6f753a3fcbd758569d657e7c9fc8c1 GIT binary patch literal 2927 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/assets/icons/ui_light/design/tray-systemproxy.svg b/assets/icons/ui_light/design/tray-systemproxy.svg new file mode 100644 index 00000000..239b4758 --- /dev/null +++ b/assets/icons/ui_light/design/tray-systemproxy.svg @@ -0,0 +1,359 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/assets/icons/ui_light/tray-connected.png b/assets/icons/ui_light/tray-connected.png new file mode 100644 index 0000000000000000000000000000000000000000..5760b02122ce49f8e221f90a9e217a7dcc775d93 GIT binary patch literal 3729 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/icons/ui_light/locate.png assets/icons/ui_dark/sort.png assets/icons/ui_light/sort.png + assets/icons/ui_dark/tray-connected.png + assets/icons/ui_dark/tray-systemproxy.png + assets/icons/ui_light/tray-connected.png + assets/icons/ui_light/tray-systemproxy.png diff --git a/src/base/Qv2rayBase.hpp b/src/base/Qv2rayBase.hpp index 6b03ee7c..55d89ea8 100644 --- a/src/base/Qv2rayBase.hpp +++ b/src/base/Qv2rayBase.hpp @@ -97,9 +97,11 @@ using namespace Qv2ray::base::objects::transfer; #define ACCESS_OPTIONAL_VALUE(obj) (obj.value()) #endif -#define QV2RAY_UI_COLORSCHEME_ROOT \ +#define Q_TRAYICON(name) (QIcon(GlobalConfig.uiConfig.useDarkTrayIcon ? ":/assets/icons/ui_dark/" name : ":/assets/icons/ui_light/" name)) + +#define QV2RAY_COLORSCHEME_ROOT \ ((GlobalConfig.uiConfig.useDarkTheme) ? QStringLiteral(":/assets/icons/ui_dark/") : QStringLiteral(":/assets/icons/ui_light/")) -#define QICON_R(file) QIcon(QV2RAY_UI_COLORSCHEME_ROOT + file) +#define QICON_R(file) QIcon(QV2RAY_COLORSCHEME_ROOT + file) #define QSTRN(num) QString::number(num) diff --git a/src/components/plugins/toolbar/QvToolbar.cpp b/src/components/plugins/toolbar/QvToolbar.cpp index 6c6cde88..967bfd22 100644 --- a/src/components/plugins/toolbar/QvToolbar.cpp +++ b/src/components/plugins/toolbar/QvToolbar.cpp @@ -89,15 +89,15 @@ namespace Qv2ray::components::plugins case 104: { // Current Connection Name - CL.Message = GetDisplayName(ConnectionManager->CurrentConnection()); + CL.Message = GetDisplayName(KernelInstance->CurrentConnection()); break; } case 105: { // Current Connection Status - CL.Message = ConnectionManager->CurrentConnection() == NullConnectionId ? QObject::tr("Not connected") : - QObject::tr("Connected"); + CL.Message = KernelInstance->CurrentConnection() == NullConnectionId ? QObject::tr("Not connected") : + QObject::tr("Connected"); break; } @@ -132,14 +132,14 @@ namespace Qv2ray::components::plugins case 301: { // Total Upload - CL.Message = FormatBytes(get<0>(GetConnectionUsageAmount(ConnectionManager->CurrentConnection()))); + CL.Message = FormatBytes(get<0>(GetConnectionUsageAmount(KernelInstance->CurrentConnection()))); break; } case 302: { // Total download - CL.Message = FormatBytes(get<1>(GetConnectionUsageAmount(ConnectionManager->CurrentConnection()))); + CL.Message = FormatBytes(get<1>(GetConnectionUsageAmount(KernelInstance->CurrentConnection()))); break; } @@ -160,7 +160,7 @@ namespace Qv2ray::components::plugins case 305: { // Connection latency - CL.Message = QSTRN(GetConnectionLatency(ConnectionManager->CurrentConnection())) + " ms"; + CL.Message = QSTRN(GetConnectionLatency(KernelInstance->CurrentConnection())) + " ms"; break; } default: diff --git a/src/core/handler/ConfigHandler.hpp b/src/core/handler/ConfigHandler.hpp index 8dc430a9..d4220c28 100644 --- a/src/core/handler/ConfigHandler.hpp +++ b/src/core/handler/ConfigHandler.hpp @@ -29,11 +29,6 @@ namespace Qv2ray::core::handlers ~QvConfigHandler(); public slots: - // - inline const ConnectionId CurrentConnection() const - { - return kernelHandler->CurrentConnection(); - } inline const QList Connections() const { return connections.keys(); diff --git a/src/core/handler/KernelInstanceHandler.cpp b/src/core/handler/KernelInstanceHandler.cpp index 5a26b38c..80408904 100644 --- a/src/core/handler/KernelInstanceHandler.cpp +++ b/src/core/handler/KernelInstanceHandler.cpp @@ -4,8 +4,10 @@ #include "core/connection/Generation.hpp" namespace Qv2ray::core::handlers { +#define isConnected (vCoreInstance->KernelStarted || !activeKernels.isEmpty()) KernelInstanceHandler::KernelInstanceHandler(QObject *parent) : QObject(parent) { + KernelInstance = this; vCoreInstance = new V2rayKernelInstance(this); connect(vCoreInstance, &V2rayKernelInstance::OnNewStatsDataArrived, this, &KernelInstanceHandler::OnStatsDataArrived_p); connect(vCoreInstance, &V2rayKernelInstance::OnProcessOutputReadyRead, this, &KernelInstanceHandler::OnKernelLogAvailable_p); @@ -25,7 +27,7 @@ namespace Qv2ray::core::handlers std::optional KernelInstanceHandler::StartConnection(const ConnectionId &id, const CONFIGROOT &root) { - if (vCoreInstance->KernelStarted || !activeKernels.isEmpty()) + if (isConnected) { StopConnection(); } @@ -240,7 +242,7 @@ namespace Qv2ray::core::handlers void KernelInstanceHandler::StopConnection() { - if (vCoreInstance->KernelStarted || !activeKernels.isEmpty()) + if (isConnected) { PluginHost->Send_ConnectivityEvent({ GetDisplayName(currentConnectionId), {}, Events::Connectivity::QvConnecticity_Disconnecting }); if (vCoreInstance->KernelStarted) @@ -267,6 +269,9 @@ namespace Qv2ray::core::handlers void KernelInstanceHandler::OnStatsDataArrived_p(const quint64 uploadSpeed, const quint64 downloadSpeed) { - emit OnStatsDataAvailable(currentConnectionId, uploadSpeed, downloadSpeed); + if (isConnected) + { + emit OnStatsDataAvailable(currentConnectionId, uploadSpeed, downloadSpeed); + } } } // namespace Qv2ray::core::handlers diff --git a/src/ui/w_MainWindow.cpp b/src/ui/w_MainWindow.cpp index f8fb0fdc..3ecc22ef 100644 --- a/src/ui/w_MainWindow.cpp +++ b/src/ui/w_MainWindow.cpp @@ -51,11 +51,11 @@ QvMessageBusSlotImpl(MainWindow) void MainWindow::UpdateColorScheme() { - hTray.setIcon(QIcon(GlobalConfig.uiConfig.useDarkTrayIcon ? ":/assets/icons/ui_dark/tray.png" : ":/assets/icons/ui_light/tray.png")); + hTray.setIcon(KernelInstance->CurrentConnection() == NullConnectionId ? Q_TRAYICON("tray.png") : Q_TRAYICON("tray-connected.png")); // importConfigButton->setIcon(QICON_R("import.png")); - updownImageBox->setStyleSheet("image: url(" + QV2RAY_UI_COLORSCHEME_ROOT + "netspeed_arrow.png)"); - updownImageBox_2->setStyleSheet("image: url(" + QV2RAY_UI_COLORSCHEME_ROOT + "netspeed_arrow.png)"); + updownImageBox->setStyleSheet("image: url(" + QV2RAY_COLORSCHEME_ROOT + "netspeed_arrow.png)"); + updownImageBox_2->setStyleSheet("image: url(" + QV2RAY_COLORSCHEME_ROOT + "netspeed_arrow.png)"); // tray_action_ShowHide->setIcon(this->windowIcon()); action_RCM_Start->setIcon(QICON_R("connect.png")); @@ -583,6 +583,7 @@ void MainWindow::on_connectionListWidget_itemDoubleClicked(QTreeWidgetItem *item void MainWindow::OnDisconnected(const ConnectionId &id) { Q_UNUSED(id) + hTray.setIcon(Q_TRAYICON("tray.png")); tray_action_Start->setEnabled(true); tray_action_Stop->setEnabled(false); tray_action_Restart->setEnabled(false); @@ -606,6 +607,7 @@ void MainWindow::OnDisconnected(const ConnectionId &id) void MainWindow::OnConnected(const ConnectionId &id) { Q_UNUSED(id) + hTray.setIcon(Q_TRAYICON("tray-connected.png")); tray_action_Start->setEnabled(false); tray_action_Stop->setEnabled(true); tray_action_Restart->setEnabled(true); @@ -817,7 +819,7 @@ void MainWindow::OnGroupDeleted(const GroupId &id, const QList &co void MainWindow::on_locateBtn_clicked() { - auto id = ConnectionManager->CurrentConnection(); + auto id = KernelInstance->CurrentConnection(); if (id != NullConnectionId) { connectionListWidget->setCurrentItem(connectionNodes.value(id).get()); diff --git a/src/ui/w_MainWindow_extra.cpp b/src/ui/w_MainWindow_extra.cpp index eb059a48..62c87969 100644 --- a/src/ui/w_MainWindow_extra.cpp +++ b/src/ui/w_MainWindow_extra.cpp @@ -16,6 +16,7 @@ void MainWindow::MWSetSystemProxy() { proxyAddress = "127.0.0.1"; SetSystemProxy(proxyAddress, httpPort, socksPort); + hTray.setIcon(Q_TRAYICON("tray-systemproxy.png")); if (!GlobalConfig.uiConfig.quietMode) { hTray.showMessage("Qv2ray", tr("System proxy configured.")); @@ -31,6 +32,7 @@ void MainWindow::MWSetSystemProxy() void MainWindow::MWClearSystemProxy() { ClearSystemProxy(); + hTray.setIcon(KernelInstance->CurrentConnection() == NullConnectionId ? Q_TRAYICON("tray.png") : Q_TRAYICON("tray-connected.png")); if (!GlobalConfig.uiConfig.quietMode) { hTray.showMessage("Qv2ray", tr("System proxy removed.")); From 04d098779a102762fe499f7a8f99e8d2b08c2721 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Fri, 17 Apr 2020 23:16:36 +0800 Subject: [PATCH 92/92] add: this could resolve #514 --- makespec/BUILDVERSION | 2 +- src/ui/w_PluginManager.cpp | 17 +++++++++++++++++ src/ui/w_PluginManager.hpp | 4 ++++ src/ui/w_PluginManager.ui | 39 +++++++++++++++++++++++++++++--------- 4 files changed, 52 insertions(+), 10 deletions(-) diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index c03b6622..2dc33761 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5263 \ No newline at end of file +5264 \ No newline at end of file diff --git a/src/ui/w_PluginManager.cpp b/src/ui/w_PluginManager.cpp index 1feb196c..69ea3c77 100644 --- a/src/ui/w_PluginManager.cpp +++ b/src/ui/w_PluginManager.cpp @@ -70,6 +70,7 @@ void PluginManageWindow::on_pluginListWidget_currentItemChanged(QListWidgetItem void PluginManageWindow::on_pluginListWidget_itemClicked(QListWidgetItem *item) { + Q_UNUSED(item) // on_pluginListWidget_currentItemChanged(item, nullptr); } @@ -113,3 +114,19 @@ void PluginManageWindow::on_pluginListWidget_itemSelectionChanged() auto needEnable = !pluginListWidget->selectedItems().isEmpty(); pluginEditSettingsJsonBtn->setEnabled(needEnable); } + +void PluginManageWindow::on_openPluginFolder_clicked() +{ + QDir pluginPath(QV2RAY_CONFIG_DIR + "plugins/"); + if (!pluginPath.exists()) + { + pluginPath.mkpath(QV2RAY_CONFIG_DIR + "plugins/"); + } + QDesktopServices::openUrl(QUrl(pluginPath.absolutePath())); +} + +void PluginManageWindow::on_toolButton_clicked() +{ + auto address = GlobalConfig.uiConfig.language.contains("zh") ? "https://qv2ray.github.io/plugins/" : "https://qv2ray.github.io/en/plugins/"; + QDesktopServices::openUrl(QUrl(address)); +} diff --git a/src/ui/w_PluginManager.hpp b/src/ui/w_PluginManager.hpp index 065f0bdb..fa40d450 100644 --- a/src/ui/w_PluginManager.hpp +++ b/src/ui/w_PluginManager.hpp @@ -23,6 +23,10 @@ class PluginManageWindow void on_pluginListWidget_itemSelectionChanged(); + void on_openPluginFolder_clicked(); + + void on_toolButton_clicked(); + private: std::unique_ptr settingsWidget; bool isLoading = true; diff --git a/src/ui/w_PluginManager.ui b/src/ui/w_PluginManager.ui index 64ba2988..a9cb9baa 100644 --- a/src/ui/w_PluginManager.ui +++ b/src/ui/w_PluginManager.ui @@ -13,10 +13,38 @@ Plugin Manager - - + + + + + Plugins + + + + + + + + + + Open Local Plugin Folder + + + + + + + Online help about plugins + + + ? + + + + + @@ -27,13 +55,6 @@ - - - - Plugins - - -