From 664a181b8fac49f47b517ae280cf29091176bb66 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Mon, 27 Apr 2020 21:48:28 +0800 Subject: [PATCH 001/385] revert: this reverts a46a466d24620cf6c0e44be1389e5d0c6eed77db --- 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 62c87969..da957fab 100644 --- a/src/ui/w_MainWindow_extra.cpp +++ b/src/ui/w_MainWindow_extra.cpp @@ -14,7 +14,7 @@ void MainWindow::MWSetSystemProxy() if (httpEnabled || socksEnabled) { - proxyAddress = "127.0.0.1"; + proxyAddress = "localhost"; SetSystemProxy(proxyAddress, httpPort, socksPort); hTray.setIcon(Q_TRAYICON("tray-systemproxy.png")); if (!GlobalConfig.uiConfig.quietMode) From 83ade3638aee6588e32f43baf4c8c7820fd5e1f8 Mon Sep 17 00:00:00 2001 From: ymshenyu Date: Mon, 27 Apr 2020 23:25:36 +0800 Subject: [PATCH 002/385] add windows missing system library for nsis --- cmake/deployment.cmake | 2 -- 1 file changed, 2 deletions(-) diff --git a/cmake/deployment.cmake b/cmake/deployment.cmake index 3d3ab105..40f960df 100644 --- a/cmake/deployment.cmake +++ b/cmake/deployment.cmake @@ -14,9 +14,7 @@ list(APPEND DIRS "/usr/local/lib") list(APPEND DIRS "/usr/lib") if(MSVC) - if(NOT BUILD_NSIS) set(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION .) - endif() endif() include(InstallRequiredSystemLibraries) From cdb920e9f7be2c5f3f48f978e93cd255794fcb1c Mon Sep 17 00:00:00 2001 From: DuckSoft Date: Tue, 28 Apr 2020 08:03:00 +0800 Subject: [PATCH 003/385] [cleanup] slow catchs --- src/ui/widgets/RouteSettingsMatrix.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ui/widgets/RouteSettingsMatrix.cpp b/src/ui/widgets/RouteSettingsMatrix.cpp index 1a6ff3d7..6742bea3 100644 --- a/src/ui/widgets/RouteSettingsMatrix.cpp +++ b/src/ui/widgets/RouteSettingsMatrix.cpp @@ -121,7 +121,7 @@ void RouteSettingsMatrixWidget::on_importSchemeBtn_clicked() // done LOG(MODULE_SETTINGS, "Imported route config: " + scheme.name + " by: " + scheme.author) } - catch (exception e) + catch (exception &e) { LOG(MODULE_UI, "Exception: " + QString(e.what())) // TODO: Give some error as Notification @@ -181,7 +181,7 @@ void RouteSettingsMatrixWidget::on_exportSchemeBtn_clicked() // TODO: Give some success as Notification QvMessageBoxInfo(this, dialogTitle, tr("Your route scheme has been successfully exported!")); } - catch (exception) + catch (exception &) { // TODO: Give some error as Notification From 5db7961f316d6ca0ea2a8465ef8843cfb6a032d1 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Tue, 28 Apr 2020 13:26:32 +0800 Subject: [PATCH 004/385] add: added BitTorrent detection --- makespec/BUILDVERSION | 2 +- src/base/models/QvSettingsObject.hpp | 19 ++++---- src/core/connection/Generation.cpp | 27 ++++++++++- src/core/connection/Generation.hpp | 2 + src/ui/w_PreferencesWindow.cpp | 25 ++++++++++ src/ui/w_PreferencesWindow.hpp | 6 +++ src/ui/w_PreferencesWindow.ui | 71 +++++++++++++++++++++------- 7 files changed, 125 insertions(+), 27 deletions(-) diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 040c32b6..371665f9 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5335 \ No newline at end of file +5336 \ No newline at end of file diff --git a/src/base/models/QvSettingsObject.hpp b/src/base/models/QvSettingsObject.hpp index 03dc86a5..c8ed20f8 100644 --- a/src/base/models/QvSettingsObject.hpp +++ b/src/base/models/QvSettingsObject.hpp @@ -69,11 +69,13 @@ namespace Qv2ray::base::config bool socksUDP; QString socksLocalIP; objects::AccountObject socksAccount; + bool socksSniffing; // HTTP bool useHTTP; int http_port; bool http_useAuth; objects::AccountObject httpAccount; + bool httpSniffing; // dokodemo-door transparent proxy bool useTPROXY; @@ -88,15 +90,15 @@ namespace Qv2ray::base::config Qv2rayInboundsConfig() : 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(), useTPROXY(false), - tproxy_ip("127.0.0.1"), tproxy_port(12345), tproxy_use_tcp(true), tproxy_use_udp(false), tproxy_followRedirect(true), - tproxy_mode("tproxy"), dnsIntercept(true) + socksLocalIP("127.0.0.1"), socksAccount(), socksSniffing(false), useHTTP(true), http_port(8888), http_useAuth(false), + httpAccount(), httpSniffing(false), useTPROXY(false), tproxy_ip("127.0.0.1"), tproxy_port(12345), tproxy_use_tcp(true), + tproxy_use_udp(false), tproxy_followRedirect(true), tproxy_mode("tproxy"), dnsIntercept(true) { } - XTOSTRUCT(O(setSystemProxy, listenip, useSocks, useHTTP, socks_port, socks_useAuth, socksAccount, socksUDP, socksLocalIP, http_port, - http_useAuth, httpAccount, useTPROXY, tproxy_ip, tproxy_port, tproxy_use_tcp, tproxy_use_udp, tproxy_followRedirect, - tproxy_mode, dnsIntercept)) + XTOSTRUCT(O(setSystemProxy, listenip, useSocks, useHTTP, socks_port, socks_useAuth, socksAccount, socksSniffing, socksUDP, socksLocalIP, + http_port, http_useAuth, httpAccount, httpSniffing, useTPROXY, tproxy_ip, tproxy_port, tproxy_use_tcp, tproxy_use_udp, + tproxy_followRedirect, tproxy_mode, dnsIntercept)) }; struct Qv2rayOutboundsConfig @@ -167,6 +169,7 @@ namespace Qv2ray::base::config struct Qv2rayConnectionConfig { bool bypassCN; + bool bypassBT; bool enableProxy; bool v2rayFreedomDNS; bool withLocalDNS; @@ -174,11 +177,11 @@ namespace Qv2ray::base::config QList dnsList; Qv2rayForwardProxyConfig forwardProxyConfig; Qv2rayConnectionConfig() - : bypassCN(true), enableProxy(true), v2rayFreedomDNS(false), withLocalDNS(false), routeConfig(), + : bypassCN(true), bypassBT(false), enableProxy(true), v2rayFreedomDNS(false), withLocalDNS(false), routeConfig(), dnsList(QStringList{ "8.8.4.4", "1.1.1.1" }) { } - XTOSTRUCT(O(bypassCN, enableProxy, v2rayFreedomDNS, withLocalDNS, dnsList, forwardProxyConfig, routeConfig)) + XTOSTRUCT(O(bypassCN, bypassBT, enableProxy, v2rayFreedomDNS, withLocalDNS, dnsList, forwardProxyConfig, routeConfig)) }; struct Qv2rayAPIConfig diff --git a/src/core/connection/Generation.cpp b/src/core/connection/Generation.cpp index beafd3bb..daf96f67 100644 --- a/src/core/connection/Generation.cpp +++ b/src/core/connection/Generation.cpp @@ -319,7 +319,10 @@ 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.httpSniffing) + { + httpInBoundObject.insert("sniffing", sniffingObject); + } if (GlobalConfig.inboundConfig.http_useAuth) { @@ -338,7 +341,11 @@ namespace Qv2ray::core::connection socksInBoundObject.insert("port", GlobalConfig.inboundConfig.socks_port); socksInBoundObject.insert("protocol", "socks"); socksInBoundObject.insert("tag", "socks_IN"); - socksInBoundObject.insert("sniffing", sniffingObject); + if (!GlobalConfig.inboundConfig.socksSniffing) + { + socksInBoundObject.insert("sniffing", sniffingObject); + } + auto socksInSettings = GenerateSocksIN(GlobalConfig.inboundConfig.socks_useAuth ? "password" : "noauth", QList() << GlobalConfig.inboundConfig.socksAccount, GlobalConfig.inboundConfig.socksUDP, GlobalConfig.inboundConfig.socksLocalIP); @@ -527,6 +534,12 @@ namespace Qv2ray::core::connection { OutboundMarkSettingFilter(GlobalConfig.outboundConfig.mark, root); } + + if (GlobalConfig.connectionConfig.bypassBT) + { + bypassBTFilter(root); + } + } // Let's process some api features. @@ -628,5 +641,15 @@ namespace Qv2ray::core::connection root["routing"] = routing; } + void bypassBTFilter(CONFIGROOT &root) + { + QJsonObject bypassBTRuleObj{ { "protocol", QJsonArray::fromStringList(QStringList{ "bittorrent" }) },{ "outboundTag", OUTBOUND_TAG_DIRECT }, { "type", "field" } }; + ROUTING routing(root["routing"].toObject()); + QJsonArray _rules(routing["rules"].toArray()); + _rules.insert(0, bypassBTRuleObj); + routing["rules"] = _rules; + root["routing"] = routing; + } + } // namespace Generation } // namespace Qv2ray::core::connection diff --git a/src/core/connection/Generation.hpp b/src/core/connection/Generation.hpp index 78a480b1..84445d06 100644 --- a/src/core/connection/Generation.hpp +++ b/src/core/connection/Generation.hpp @@ -42,6 +42,8 @@ namespace Qv2ray::core::connection void DNSInterceptFilter(CONFIGROOT &root); + void bypassBTFilter(CONFIGROOT &root); + } // namespace Generation } // namespace Qv2ray::core::connection diff --git a/src/ui/w_PreferencesWindow.cpp b/src/ui/w_PreferencesWindow.cpp index 0a88d2b1..1fd9b6b2 100644 --- a/src/ui/w_PreferencesWindow.cpp +++ b/src/ui/w_PreferencesWindow.cpp @@ -102,6 +102,7 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent), Current httpAuthPasswordTxt->setEnabled(CurrentConfig.inboundConfig.http_useAuth); httpAuthUsernameTxt->setText(CurrentConfig.inboundConfig.httpAccount.user); httpAuthPasswordTxt->setText(CurrentConfig.inboundConfig.httpAccount.pass); + httpSniffingCB->setChecked(CurrentConfig.inboundConfig.httpSniffing); // // bool have_socks = CurrentConfig.inboundConfig.useSocks; @@ -117,6 +118,7 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent), Current socksUDPCB->setChecked(CurrentConfig.inboundConfig.socksUDP); socksUDPIP->setEnabled(CurrentConfig.inboundConfig.socksUDP); socksUDPIP->setText(CurrentConfig.inboundConfig.socksLocalIP); + socksSniffingCB->setChecked(CurrentConfig.inboundConfig.socksSniffing); // // bool have_tproxy = CurrentConfig.inboundConfig.useTPROXY; @@ -139,6 +141,7 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent), Current // // bypassCNCb->setChecked(CurrentConfig.connectionConfig.bypassCN); + bypassBTCb->setChecked(CurrentConfig.connectionConfig.bypassBT); proxyDefaultCb->setChecked(CurrentConfig.connectionConfig.enableProxy); // localDNSCb->setChecked(CurrentConfig.connectionConfig.withLocalDNS); @@ -635,6 +638,16 @@ void PreferencesWindow::on_bypassCNCb_stateChanged(int arg1) CurrentConfig.connectionConfig.bypassCN = arg1 == Qt::Checked; } +void PreferencesWindow::on_bypassBTCb_stateChanged(int arg1) +{ + NEEDRESTART + if (arg1 == Qt::Checked) + { + QvMessageBoxInfo(this, tr("Note"), tr("To recognize the protocol of a connection, one must enable sniffing option in inbound proxy.") + NEWLINE+tr("tproxy inbound's sniffing is enabled by default.")); + } + CurrentConfig.connectionConfig.bypassBT = arg1 == Qt::Checked; +} + void PreferencesWindow::on_statsPortBox_valueChanged(int arg1) { NEEDRESTART @@ -1303,3 +1316,15 @@ void PreferencesWindow::on_DnsFreedomCb_stateChanged(int arg1) NEEDRESTART CurrentConfig.connectionConfig.v2rayFreedomDNS = arg1 == Qt::Checked; } + +void PreferencesWindow::on_httpSniffingCB_stateChanged(int arg1) +{ + NEEDRESTART + CurrentConfig.inboundConfig.httpSniffing = arg1 == Qt::Checked; +} + +void PreferencesWindow::on_socksSniffingCB_stateChanged(int arg1) +{ + NEEDRESTART + CurrentConfig.inboundConfig.socksSniffing = arg1 == Qt::Checked; +} \ No newline at end of file diff --git a/src/ui/w_PreferencesWindow.hpp b/src/ui/w_PreferencesWindow.hpp index 7f57a2bc..962d6520 100644 --- a/src/ui/w_PreferencesWindow.hpp +++ b/src/ui/w_PreferencesWindow.hpp @@ -64,6 +64,8 @@ class PreferencesWindow void on_bypassCNCb_stateChanged(int arg1); + void on_bypassBTCb_stateChanged(int arg1); + void on_statsPortBox_valueChanged(int arg1); void on_socksUDPCB_stateChanged(int arg1); @@ -204,6 +206,10 @@ class PreferencesWindow void on_DnsFreedomCb_stateChanged(int arg1); + void on_httpSniffingCB_stateChanged(int arg1); + + void on_socksSniffingCB_stateChanged(int arg1); + private: // RouteSettingsMatrixWidget *routeSettingsWidget; diff --git a/src/ui/w_PreferencesWindow.ui b/src/ui/w_PreferencesWindow.ui index 0e202e08..8e50a231 100644 --- a/src/ui/w_PreferencesWindow.ui +++ b/src/ui/w_PreferencesWindow.ui @@ -365,9 +365,6 @@ - - QAbstractSpinBox::NoButtons - 0 @@ -729,7 +726,7 @@ Custom DNS Settings 0 0 818 - 520 + 558 @@ -841,34 +838,48 @@ Custom DNS Settings - + Username - + user - + Password - + pass + + + + Enabled + + + + + + + Sniffing + + + @@ -946,6 +957,20 @@ Custom DNS Settings + + + + Enabled + + + + + + + Sniffing + + + @@ -1170,44 +1195,58 @@ Custom DNS Settings - + - Use V2ray Dns for Freedom Outbound + Use V2ray DNS for Freedom Outbound - + Enabled - + Use Local DNS - + + + + Enabled + + + + Custom DNS List - + - - + + Enabled + + + + Bypass Bittorrent Protocol + + + From 1f5e934f5ace339851e411ce68afb4a715cd3bfb Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Tue, 28 Apr 2020 13:37:36 +0800 Subject: [PATCH 005/385] change: Subscription Editor --> Group Editor --- CMakeLists.txt | 6 ++-- makespec/BUILDVERSION | 2 +- ...criptionManager.cpp => w_GroupManager.cpp} | 36 +++++++++---------- ...criptionManager.hpp => w_GroupManager.hpp} | 10 +++--- ...bscriptionManager.ui => w_GroupManager.ui} | 28 +++++++-------- src/ui/w_ImportConfig.cpp | 2 +- src/ui/w_MainWindow.cpp | 2 +- src/ui/w_MainWindow.ui | 2 +- 8 files changed, 44 insertions(+), 44 deletions(-) rename src/ui/{w_SubscriptionManager.cpp => w_GroupManager.cpp} (72%) rename src/ui/{w_SubscriptionManager.hpp => w_GroupManager.hpp} (76%) rename src/ui/{w_SubscriptionManager.ui => w_GroupManager.ui} (93%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c32e6b2..6a89bcee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -209,9 +209,9 @@ set(QV2RAY_SOURCES src/ui/w_PreferencesWindow.cpp src/ui/w_PluginManager.cpp src/ui/w_ScreenShot_Core.cpp - src/ui/w_SubscriptionManager.cpp + src/ui/w_GroupManager.cpp # ui files - src/ui/w_SubscriptionManager.ui + src/ui/w_GroupManager.ui src/ui/editors/w_OutboundEditor.ui src/ui/editors/w_InboundEditor.ui src/ui/editors/w_JsonEditor.ui @@ -289,7 +289,7 @@ set(QV2RAY_SOURCES src/ui/w_PreferencesWindow.hpp src/ui/w_PluginManager.hpp src/ui/w_ScreenShot_Core.hpp - src/ui/w_SubscriptionManager.hpp + src/ui/w_GroupManager.hpp assets/qv2ray.rc ) diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 371665f9..e3aa5020 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5336 \ No newline at end of file +5338 diff --git a/src/ui/w_SubscriptionManager.cpp b/src/ui/w_GroupManager.cpp similarity index 72% rename from src/ui/w_SubscriptionManager.cpp rename to src/ui/w_GroupManager.cpp index b776da28..21864a9d 100644 --- a/src/ui/w_SubscriptionManager.cpp +++ b/src/ui/w_GroupManager.cpp @@ -1,4 +1,4 @@ -#include "w_SubscriptionManager.hpp" +#include "w_GroupManager.hpp" #include "common/QvHelpers.hpp" #include "core/handler/ConfigHandler.hpp" @@ -11,11 +11,11 @@ SubscriptionEditor::SubscriptionEditor(QWidget *parent) : QDialog(parent) UpdateColorScheme(); for (auto subs : ConnectionManager->Subscriptions()) { - subscriptionList->addTopLevelItem(new QTreeWidgetItem(QStringList{ GetDisplayName(subs), subs.toString() })); + groupList->addTopLevelItem(new QTreeWidgetItem(QStringList{ GetDisplayName(subs), subs.toString() })); } - if (subscriptionList->topLevelItemCount() > 0) + if (groupList->topLevelItemCount() > 0) { - subscriptionList->setCurrentItem(subscriptionList->topLevelItem(0)); + groupList->setCurrentItem(groupList->topLevelItem(0)); } } @@ -47,7 +47,7 @@ void SubscriptionEditor::on_addSubsButton_clicked() auto const key = QSTRN(QTime::currentTime().msecsSinceStartOfDay()); auto id = ConnectionManager->CreateGroup(key, true); // - subscriptionList->addTopLevelItem(new QTreeWidgetItem(QStringList{ key, id.toString() })); + groupList->addTopLevelItem(new QTreeWidgetItem(QStringList{ key, id.toString() })); } void SubscriptionEditor::on_updateButton_clicked() @@ -57,7 +57,7 @@ void SubscriptionEditor::on_updateButton_clicked() this->setEnabled(false); ConnectionManager->UpdateSubscription(currentSubId); // this->setEnabled(true); - on_subscriptionList_itemClicked(subscriptionList->currentItem(), 0); + on_groupList_itemClicked(groupList->currentItem(), 0); } } @@ -67,13 +67,13 @@ void SubscriptionEditor::on_removeSubsButton_clicked() QMessageBox::Yes) { ConnectionManager->DeleteGroup(currentSubId); // - auto item = subscriptionList->currentItem(); - subscriptionList->removeItemWidget(item, 0); + auto item = groupList->currentItem(); + groupList->removeItemWidget(item, 0); delete item; - if (subscriptionList->topLevelItemCount() > 0) + if (groupList->topLevelItemCount() > 0) { - subscriptionList->setCurrentItem(subscriptionList->topLevelItem(0)); - on_subscriptionList_itemClicked(subscriptionList->topLevelItem(0), 0); + groupList->setCurrentItem(groupList->topLevelItem(0)); + on_groupList_itemClicked(groupList->topLevelItem(0), 0); } else { @@ -87,12 +87,12 @@ void SubscriptionEditor::on_buttonBox_accepted() // Nothing? } -void SubscriptionEditor::on_subscriptionList_itemSelectionChanged() +void SubscriptionEditor::on_groupList_itemSelectionChanged() { - groupBox_2->setEnabled(subscriptionList->selectedItems().count() > 0); + groupBox_2->setEnabled(groupList->selectedItems().count() > 0); } -void SubscriptionEditor::on_subscriptionList_itemClicked(QTreeWidgetItem *item, int column) +void SubscriptionEditor::on_groupList_itemClicked(QTreeWidgetItem *item, int column) { Q_UNUSED(column) @@ -105,7 +105,7 @@ void SubscriptionEditor::on_subscriptionList_itemClicked(QTreeWidgetItem *item, groupBox_2->setEnabled(true); currentSubId = GroupId(item->text(1)); // - subNameTxt->setText(GetDisplayName(currentSubId)); + groupNameTxt->setText(GetDisplayName(currentSubId)); auto const [addr, lastUpdated, updateInterval] = ConnectionManager->GetSubscriptionData(currentSubId); subAddrTxt->setText(addr); lastUpdatedLabel->setText(timeToString(lastUpdated)); @@ -119,15 +119,15 @@ void SubscriptionEditor::on_subscriptionList_itemClicked(QTreeWidgetItem *item, } } -void SubscriptionEditor::on_subscriptionList_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous) +void SubscriptionEditor::on_groupList_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous) { Q_UNUSED(previous) - on_subscriptionList_itemClicked(current, 0); + on_groupList_itemClicked(current, 0); } void SubscriptionEditor::on_subNameTxt_textEdited(const QString &arg1) { - subscriptionList->selectedItems().first()->setText(0, arg1); + groupList->selectedItems().first()->setText(0, arg1); ConnectionManager->RenameGroup(currentSubId, arg1.trimmed()); } diff --git a/src/ui/w_SubscriptionManager.hpp b/src/ui/w_GroupManager.hpp similarity index 76% rename from src/ui/w_SubscriptionManager.hpp rename to src/ui/w_GroupManager.hpp index 01134023..a29cdfc7 100644 --- a/src/ui/w_SubscriptionManager.hpp +++ b/src/ui/w_GroupManager.hpp @@ -3,13 +3,13 @@ #include "base/Qv2rayBase.hpp" #include "core/CoreSafeTypes.hpp" #include "ui/messaging/QvMessageBus.hpp" -#include "ui_w_SubscriptionManager.h" +#include "ui_w_GroupManager.h" #include class SubscriptionEditor : public QDialog - , private Ui::w_SubscribeEditor + , private Ui::w_GroupManager { Q_OBJECT @@ -30,11 +30,11 @@ class SubscriptionEditor void on_buttonBox_accepted(); - void on_subscriptionList_itemSelectionChanged(); + void on_groupList_itemSelectionChanged(); - void on_subscriptionList_itemClicked(QTreeWidgetItem *item, int column); + void on_groupList_itemClicked(QTreeWidgetItem *item, int column); - void on_subscriptionList_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous); + void on_groupList_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous); void on_subNameTxt_textEdited(const QString &arg1); diff --git a/src/ui/w_SubscriptionManager.ui b/src/ui/w_GroupManager.ui similarity index 93% rename from src/ui/w_SubscriptionManager.ui rename to src/ui/w_GroupManager.ui index e665ec05..afefa712 100644 --- a/src/ui/w_SubscriptionManager.ui +++ b/src/ui/w_GroupManager.ui @@ -1,13 +1,13 @@ - w_SubscribeEditor - + w_GroupManager + 0 0 - 655 - 440 + 911 + 629 @@ -17,7 +17,7 @@ - SubscribeEditor + Group Editor true @@ -39,11 +39,11 @@ - Subscription List + Group List - + QAbstractItemView::SingleSelection @@ -145,7 +145,7 @@ false - Subscription Details + Group Details @@ -153,12 +153,12 @@ - Subscription Name + Group Name - + @@ -264,11 +264,11 @@ - subscriptionList + groupList addSubsButton removeSubsButton subAddrTxt - subNameTxt + groupNameTxt updateIntervalSB connectionsList updateButton @@ -280,7 +280,7 @@ buttonBox accepted() - w_SubscribeEditor + w_GroupManager accept() @@ -296,7 +296,7 @@ buttonBox rejected() - w_SubscribeEditor + w_GroupManager reject() diff --git a/src/ui/w_ImportConfig.cpp b/src/ui/w_ImportConfig.cpp index 95f6d991..19e13618 100644 --- a/src/ui/w_ImportConfig.cpp +++ b/src/ui/w_ImportConfig.cpp @@ -9,7 +9,7 @@ #include "ui/editors/w_JsonEditor.hpp" #include "ui/editors/w_OutboundEditor.hpp" #include "ui/editors/w_RoutesEditor.hpp" -#include "ui/w_SubscriptionManager.hpp" +#include "ui/w_GroupManager.hpp" #include "w_ScreenShot_Core.hpp" #include diff --git a/src/ui/w_MainWindow.cpp b/src/ui/w_MainWindow.cpp index 16a6a2d9..e1f604f0 100644 --- a/src/ui/w_MainWindow.cpp +++ b/src/ui/w_MainWindow.cpp @@ -8,10 +8,10 @@ #include "ui/editors/w_JsonEditor.hpp" #include "ui/editors/w_OutboundEditor.hpp" #include "ui/editors/w_RoutesEditor.hpp" +#include "ui/w_GroupManager.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" #include diff --git a/src/ui/w_MainWindow.ui b/src/ui/w_MainWindow.ui index b308458b..f99dc46a 100644 --- a/src/ui/w_MainWindow.ui +++ b/src/ui/w_MainWindow.ui @@ -29,7 +29,7 @@ - Subscriptions + Groups From c424301924116eb992d463c35b83b9500d6c277b Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Tue, 28 Apr 2020 14:59:32 +0800 Subject: [PATCH 006/385] format: code format --- makespec/BUILDVERSION | 2 +- src/components/latency/QvTCPing.cpp | 2 +- src/components/plugins/toolbar/QvToolbar_linux.cpp | 3 ++- src/components/route/presets/RouteScheme_V2rayN.hpp | 6 +++--- src/core/connection/Generation.cpp | 7 ++++--- src/core/connection/Serialization_vmess.cpp | 3 ++- src/ui/w_PreferencesWindow.cpp | 6 ++++-- 7 files changed, 17 insertions(+), 12 deletions(-) diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index e3aa5020..00d32773 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5338 +5339 \ No newline at end of file diff --git a/src/components/latency/QvTCPing.cpp b/src/components/latency/QvTCPing.cpp index 32970468..0091a4ed 100644 --- a/src/components/latency/QvTCPing.cpp +++ b/src/components/latency/QvTCPing.cpp @@ -222,4 +222,4 @@ namespace Qv2ray::components::tcping rv = rv ? rv : -errno; return rv; } -} // namespace Qv2ray::core::tcping +} // namespace Qv2ray::components::tcping diff --git a/src/components/plugins/toolbar/QvToolbar_linux.cpp b/src/components/plugins/toolbar/QvToolbar_linux.cpp index 9fdd2a4b..314f995a 100644 --- a/src/components/plugins/toolbar/QvToolbar_linux.cpp +++ b/src/components/plugins/toolbar/QvToolbar_linux.cpp @@ -67,7 +67,8 @@ namespace Qv2ray::components::plugins::Toolbar { bool result = server->waitForNewConnection(5000, &timeOut); DEBUG(MODULE_PLUGINHOST, "Plugin thread listening failed: " + server->errorString()) - DEBUG(MODULE_PLUGINHOST, "waitForNewConnection: " + QString(result ? "true" : "false") + ", " + QString(timeOut ? "true" : "false")) + DEBUG(MODULE_PLUGINHOST, + "waitForNewConnection: " + QString(result ? "true" : "false") + ", " + QString(timeOut ? "true" : "false")) } server->close(); diff --git a/src/components/route/presets/RouteScheme_V2rayN.hpp b/src/components/route/presets/RouteScheme_V2rayN.hpp index 447c4074..b3ae4ed5 100644 --- a/src/components/route/presets/RouteScheme_V2rayN.hpp +++ b/src/components/route/presets/RouteScheme_V2rayN.hpp @@ -141,9 +141,9 @@ namespace Qv2ray::components::route::presets::v2rayN "domain:samsungdm.com" }; const inline QList DomainsBlock{ "geosite:category-ads-all" }; - const inline QList DomainsProxy{ "geosite:google", "geosite:github", "geosite:netflix", "geosite:steam", - "geosite:telegram", "geosite:tumblr", "domain:naver.com", "geosite:bbc", - "domain:gvt1.com", "domain:textnow.com", "domain:twitch.tv", "domain:wikileaks.org" }; + const inline QList DomainsProxy{ "geosite:google", "geosite:github", "geosite:netflix", "geosite:steam", + "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", diff --git a/src/core/connection/Generation.cpp b/src/core/connection/Generation.cpp index daf96f67..4e234e2d 100644 --- a/src/core/connection/Generation.cpp +++ b/src/core/connection/Generation.cpp @@ -539,7 +539,6 @@ namespace Qv2ray::core::connection { bypassBTFilter(root); } - } // Let's process some api features. @@ -643,12 +642,14 @@ namespace Qv2ray::core::connection void bypassBTFilter(CONFIGROOT &root) { - QJsonObject bypassBTRuleObj{ { "protocol", QJsonArray::fromStringList(QStringList{ "bittorrent" }) },{ "outboundTag", OUTBOUND_TAG_DIRECT }, { "type", "field" } }; + QJsonObject bypassBTRuleObj{ { "protocol", QJsonArray::fromStringList(QStringList{ "bittorrent" }) }, + { "outboundTag", OUTBOUND_TAG_DIRECT }, + { "type", "field" } }; ROUTING routing(root["routing"].toObject()); QJsonArray _rules(routing["rules"].toArray()); _rules.insert(0, bypassBTRuleObj); routing["rules"] = _rules; - root["routing"] = routing; + root["routing"] = routing; } } // namespace Generation diff --git a/src/core/connection/Serialization_vmess.cpp b/src/core/connection/Serialization_vmess.cpp index b7bbf44f..8add3fa5 100644 --- a/src/core/connection/Serialization_vmess.cpp +++ b/src/core/connection/Serialization_vmess.cpp @@ -106,7 +106,8 @@ namespace Qv2ray::core::connection } // Explicitly don't support v1 vmess links. - if (!vmessConf.contains("v")) { + if (!vmessConf.contains("v")) + { *errMessage = QObject::tr("seems like a v1 vmess, we don't support it"); return default; } diff --git a/src/ui/w_PreferencesWindow.cpp b/src/ui/w_PreferencesWindow.cpp index 1fd9b6b2..a83ae8a9 100644 --- a/src/ui/w_PreferencesWindow.cpp +++ b/src/ui/w_PreferencesWindow.cpp @@ -643,7 +643,9 @@ void PreferencesWindow::on_bypassBTCb_stateChanged(int arg1) NEEDRESTART if (arg1 == Qt::Checked) { - QvMessageBoxInfo(this, tr("Note"), tr("To recognize the protocol of a connection, one must enable sniffing option in inbound proxy.") + NEWLINE+tr("tproxy inbound's sniffing is enabled by default.")); + QvMessageBoxInfo(this, tr("Note"), + tr("To recognize the protocol of a connection, one must enable sniffing option in inbound proxy.") + NEWLINE + + tr("tproxy inbound's sniffing is enabled by default.")); } CurrentConfig.connectionConfig.bypassBT = arg1 == Qt::Checked; } @@ -1327,4 +1329,4 @@ void PreferencesWindow::on_socksSniffingCB_stateChanged(int arg1) { NEEDRESTART CurrentConfig.inboundConfig.socksSniffing = arg1 == Qt::Checked; -} \ No newline at end of file +} From 8961c6ed0fab04faacf9ca4efec7dd3594eb0c58 Mon Sep 17 00:00:00 2001 From: choohoo Date: Tue, 28 Apr 2020 17:28:53 +0800 Subject: [PATCH 007/385] Update Japanese translations Update Japanese translations by my habits Just for reference. --- translations/ja_JP.ts | 104 +++++++++++++++++++++--------------------- 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/translations/ja_JP.ts b/translations/ja_JP.ts index 3cf8486c..d266e7fa 100644 --- a/translations/ja_JP.ts +++ b/translations/ja_JP.ts @@ -311,7 +311,7 @@ Place your vmess:// here, one line for each. - ここに vmess:// を1行ずつ配置します。 + ここに vmess:// を1行ずつ貼り付けます。 Error List @@ -431,7 +431,7 @@ Paste share link here, one line for each. - ここに vmess:// を1行ずつ配置します。 + ここに vmess:// を1行ずつ貼り付けます。 @@ -1072,11 +1072,11 @@ The kernel terminated unexpectedly: - カーネルが異常終了しました: + カーネルがエラー終了しました: To solve the problem, read the kernel log in the log text browser. - 問題を解決するには、ログテキストブラウザでカーネルログを読み込んでください。 + 問題を解決するには、ログテキストブラウザでカーネルログを確認しでください。 Quit Qv2ray @@ -1084,7 +1084,7 @@ Are you sure to exit Qv2ray? - Qv2rayは確実に終了していますか? + Qv2rayを終了してよろしいでしょうか? Duplicating Connection(s) @@ -1419,7 +1419,7 @@ The specified outbound type is not supported, this may happen due to a plugin failure. - 指定されたアウトバウンドがサポートされていない場合、プラグインの障害により発生する可能性があります。 + 指定されたアウトバウンドがサポートされていないです、プラグインの問題の可能性があります。 Forward proxy has been disabled when using plugin outbound @@ -1431,7 +1431,7 @@ The specified outbound type is invalid, this may be caused by a plugin failure. - 指定された送信アウトバウンドタイプが無効な場合、プラグインの障害が原因である可能性があります。 + 指定された送信アウトバウンドタイプが無効です、プラグインの問題の可能性があります。 Please use the JsonEditor or reload the plugin. @@ -1466,11 +1466,11 @@ Plugin not loaded - プラグインが読み込まれていない + プラグインがロードされていない This plugin is not loaded, please enable or reload the plugin to continue. - このプラグインが読み込まれていません, プラグインを有効にするか、プラグインをリロードして続行してください. + このプラグインがロードされていません, プラグインを有効にするか、プラグインをリロードしてください. @@ -1701,7 +1701,7 @@ Only simple config is supported. - 単純接続のみがサポートされています。 + 簡易構成のみがサポートされています。 Build Info @@ -1757,7 +1757,7 @@ V2ray Assets Directory - V2rayアセットディレクトリ + V2rayアセットフォルダー Check V2ray Core Settings @@ -1773,7 +1773,7 @@ All settings below will only be applied onto simple connection. - 以下のすべての設定は、単純接続にのみ適用されます。 + 以下のすべての設定は、簡易構成にのみ適用されます。 Add Docodemo-door inbound @@ -1841,7 +1841,7 @@ This feature is not stable and no documentation is provided, please use it at your own risk! - この機能は安定しておらず、ドキュメントも提供されていません。ご自身の責任で使用してください! + この機能は安定しておらず、ドキュメントも提供されていません。自己責任で使用してください! Text Style @@ -2033,15 +2033,15 @@ You will lose the advantage of TLS and make your connection under MITM attack. - TLSの優位性を失い、MITMの攻撃を受けて接続を行うことになります。 + TLSの保護を失い、MITMの攻撃を受ける可能性が高くなります。 This will (probably) make it easy to fingerprint your connection. - これはおそらくあなたの接続を指紋化するのが簡単になります。 + そのため、(おそらく)あなたの接続が特定しやすくなります。 This will (probably) makes it easy to fingerprint your connection. - これは(おそらく)あなたの接続を指紋化するのが簡単になります。 + そのため、(おそらく)あなたの接続が特定しやすくなります。 Dark Mode @@ -2061,7 +2061,7 @@ Operation is cancelled. - 操作はキャンセルされます。 + 操作はキャンセルされました。 Successfully downloaded GFWList. @@ -2129,7 +2129,7 @@ p, li { white-space: pre-wrap; } You can configure route rules for all simple connection config here. - ここで、すべての簡易接続設定のルーティングルールを設定できます。 + ここで、すべての簡易構成のルーティングルールを設定できます。 Connection @@ -2161,11 +2161,11 @@ p, li { white-space: pre-wrap; } Stable Release - 安定版リリース + 安定版 Testing - テストリリース + テスト版 Appearance @@ -2173,7 +2173,7 @@ p, li { white-space: pre-wrap; } Behavior - 行為 + 動作 Transparent Proxy @@ -2243,7 +2243,7 @@ Custom DNS Settings Qv2ray will allocate ports, for HTTP and SOCKS respectively, if enabled, for each kernel plugin. - Qv2ray は、有効になっている場合、各カーネルプラグインにそれぞれ HTTP と SOCKS 用のポートを割り当てます。 + 有効な場合、Qv2ray は各カーネルプラグインにそれぞれ HTTP と SOCKS 用のポートを割り当てます。 Port Allocation Start @@ -2251,7 +2251,7 @@ Custom DNS Settings All settings below will only be applied on simple connection. - 以下の設定はすべて簡易接続時のみ適用されます。 + 以下の設定はすべて簡易構成時のみ適用されます。 Page Y Axis Offset @@ -2271,7 +2271,7 @@ Custom DNS Settings Advanced Behavior - 高度的な行為 + 高度的な動作 AllowInsecure By Default @@ -2280,24 +2280,24 @@ Custom DNS Settings Enable "AllowInsecure" settings for all connections when importing. This could resolve the certificate issues, but also could let one performing TLS MITM attack. - インポート時にすべての接続に対して「AllowInsecure」の設定を有効にします。 -これは証明書の問題を解決することができますが、TLS MITM 攻撃を実行させることもできます。 + インポート時にすべての項目に対して「AllowInsecure」の設定を有効にします。 +これは証明書の問題を緩和することができますが、TLS MITM 攻撃を受けるリスクが高くなります。 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. サーバーに接続した後、定期的にTCPingまたはICMPingを実行します。 -Qv2ray を有効にすると、より正確なレイテンシ値が得られますが、接続のフィンガープリントを簡単に行うことができます。 +有効にすると、Qv2ray はより正確なレイテンシ値が得られますが、サーバーは制限される可能性が高くなります。 These settings may be useful. But could damage your server if improperly used. - これらの設定は役に立つかもしれません。 -しかし、不適切な使い方をするとサーバーにダメージを与える可能性があります。 + 以下の設定は特定の効果があります。 +しかし、不適切な使用の場合、不都合が生じる可能性があります。 AllowInsecureCiphers By Default @@ -2305,7 +2305,7 @@ But could damage your server if improperly used. Quiet Mode - 静音モード + サイレントモード Recent Jumplist @@ -2321,7 +2321,7 @@ But could damage your server if improperly used. No Proxy - 使わない + 使用しない System Proxy @@ -2341,7 +2341,7 @@ But could damage your server if improperly used. DNS Intercept - DNS傍受 + DNSインターセプト Mode @@ -2392,11 +2392,11 @@ But could damage your server if improperly used. JSON should not be empty - JSONは空にしないでください + JSONを空にしないでください seems like a v1 vmess, we don't support it - バージョン1のリンクのように見えますで、それをサポートしていません + V1のVMessリンクの可能性があります,現在非対応です。 Warning @@ -2444,7 +2444,7 @@ But could damage your server if improperly used. --> USE IT AT YOUR OWN RISK! - --> 自分の責任で使用してください! + --> 自己責任で使用してください! Cannot load languages @@ -2716,7 +2716,7 @@ But could damage your server if improperly used. %1 - %2 (rate %3) - %1 - %2 (%3 倍数) + %1 - %2 (%3 倍) (Complex config) @@ -2900,7 +2900,7 @@ But could damage your server if improperly used. Please contact the plugin provider or report the issue to Qv2ray Workgroup. - プラグインの提供者に連絡するか、Qv2ray ワークグループに問題を報告してください。 + プラグインの提供者に連絡するか、Qv2ray 制作チームに問題を報告してください。 Enabling a plugin @@ -2908,7 +2908,7 @@ But could damage your server if improperly used. The plugin will become fully functional after restarting Qv2ray. - Qv2rayを再起動すると、プラグインが完全に機能するようになります。 + プラグインを正しく動作するには、Qv2rayを再起動する必要があります。 @@ -2956,8 +2956,8 @@ But could damage your server if improperly used. Expected core ABI is %1, but got actual %2. Maybe you have downloaded the wrong core? V2Rayコアはプラットフォームと互換性がありません。 -予想されるコアABIは%1ですが、実際の%2を取得しました。 -間違ったコアをダウンロードした可能性がありますか? +予想されるコアABIは%1ですが、実際は%2でした。 +間違ったコアをダウンロードした可能性がありませんか? V2ray assets path is not valid. @@ -3128,7 +3128,7 @@ Maybe you have downloaded the wrong core? Both - どちらも + 両方 Protocol @@ -3176,7 +3176,7 @@ Maybe you have downloaded the wrong core? Domain Strategy - ドメイン名ルール + ドメインポリシー Default Outbound @@ -3184,11 +3184,11 @@ Maybe you have downloaded the wrong core? Rule Order Settings - ルールの並び替え設定 + ルールの順番設定 Drag and drop to re-order the rules. - ルールを並べ替えるには、ドラッグアンドドロップします。 + ルール順番を変更するには、ドラッグアンドドロップします。 Rule Settings @@ -3256,7 +3256,7 @@ Maybe you have downloaded the wrong core? Balancer is empty, not processing. - バランサーは空であり、処理されていません。 + バランサーがないため、処理をスキップする。 Default rule @@ -3276,11 +3276,11 @@ Maybe you have downloaded the wrong core? The new tag has been used, we appended a postfix. - 新しいタグが使用されているため、サフィックスを追加します。 + 新しいタグ名は既に使用されているため、サフィックスを追加します。 The new tag has been used, we appended a random string to the tag. - 新しいタグが使用されたため、ランダムな文字列をタグに追加します。 + 新しいタグ名は既に使用されているため、ランダムな文字列をタグ名に追加します。 To make this rule ready to use, you need to connect it to an outbound node. @@ -3332,15 +3332,15 @@ Maybe you have downloaded the wrong core? New tag is empty, please try another. - 新しいタグが空です。別のタグを試してください。 + 新しいタグ名が空です。別のタグ名を試してください。 New tag is the same as the original one. - 新しいタグと元のタグと同じです。 + 新しいタグ名と元のタグ名と同じです。 Duplicate rule tag detected, please try another. - 重複したルールタグが検出されました。別のタグを試してください。 + 重複したルールタグ名が検出されました。別のタグ名を試してください。 Added DIRECT outbound @@ -3455,7 +3455,7 @@ Maybe you have downloaded the wrong core? Domain Strategy - ドメイン戦略 + ドメインポリシー Route Settings From eb9448bce9acb04f6a2b9fd17ec19d5deec8b248 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Tue, 28 Apr 2020 15:17:06 +0800 Subject: [PATCH 008/385] add: added JsonStruct submodule --- .gitmodules | 3 +++ libs/QJsonStruct | 1 + 2 files changed, 4 insertions(+) create mode 160000 libs/QJsonStruct diff --git a/.gitmodules b/.gitmodules index acd86d16..0f14b595 100644 --- a/.gitmodules +++ b/.gitmodules @@ -19,3 +19,6 @@ [submodule "src/components/plugins/interface"] path = src/components/plugins/interface url = https://github.com/Qv2ray/QvPlugin-Interface/ +[submodule "libs/QJsonStruct"] + path = libs/QJsonStruct + url = https://github.com/Qv2ray/QJsonStruct diff --git a/libs/QJsonStruct b/libs/QJsonStruct new file mode 160000 index 00000000..1bd58ce0 --- /dev/null +++ b/libs/QJsonStruct @@ -0,0 +1 @@ +Subproject commit 1bd58ce00212cb0e6d313c927108119567ede261 From 58b8b68a61dc76b5aedd77c7f1f12edf170e4ed4 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Tue, 28 Apr 2020 15:19:23 +0800 Subject: [PATCH 009/385] make it broken! --- .gitmodules | 3 --- 3rdparty/x2struct | 1 - 2 files changed, 4 deletions(-) delete mode 160000 3rdparty/x2struct diff --git a/.gitmodules b/.gitmodules index 0f14b595..9d280afb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "3rdparty/x2struct"] - path = 3rdparty/x2struct - url = https://github.com/xyz347/x2struct [submodule "3rdparty/SingleApplication"] path = 3rdparty/SingleApplication url = https://github.com/itay-grudev/SingleApplication diff --git a/3rdparty/x2struct b/3rdparty/x2struct deleted file mode 160000 index 45397646..00000000 --- a/3rdparty/x2struct +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4539764671509655370b2ff4fae90bfd8a12df40 From db63389a2f84b45956393fb4f99e4f9bbdca4b9e Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Tue, 28 Apr 2020 17:53:33 +0800 Subject: [PATCH 010/385] refactor: Migrate JSON backend from X2struct to QJsonStruct --- libs/QJsonStruct | 2 +- src/base/JsonHelpers.hpp | 37 +------- src/base/models/CoreObjectModels.hpp | 71 ++++++++-------- src/base/models/QvConfigIdentifier.hpp | 22 ++--- src/base/models/QvSettingsObject.hpp | 89 +++++++++++--------- src/common/QvHelpers.hpp | 21 ----- src/components/plugins/toolbar/QvToolbar.cpp | 2 +- src/components/route/RouteSchemeIO.hpp | 2 +- src/core/CoreUtils.cpp | 11 +-- src/core/connection/Generation.cpp | 4 +- src/core/connection/Serialization.cpp | 6 +- src/core/connection/Serialization_vmess.cpp | 4 +- src/core/settings/SettingsBackend.cpp | 2 +- src/core/settings/SettingsUpgrade.cpp | 3 +- src/main.cpp | 2 +- src/ui/editors/w_OutboundEditor.cpp | 16 ++-- src/ui/editors/w_RoutesEditor.cpp | 4 +- src/ui/widgets/RouteSettingsMatrix.cpp | 4 +- src/ui/widgets/StreamSettingsWidget.cpp | 16 ++-- 19 files changed, 131 insertions(+), 187 deletions(-) diff --git a/libs/QJsonStruct b/libs/QJsonStruct index 1bd58ce0..7f71fcde 160000 --- a/libs/QJsonStruct +++ b/libs/QJsonStruct @@ -1 +1 @@ -Subproject commit 1bd58ce00212cb0e6d313c927108119567ede261 +Subproject commit 7f71fcded7054888cd74fb205d2d718bbba0e5e7 diff --git a/src/base/JsonHelpers.hpp b/src/base/JsonHelpers.hpp index 38114f90..b238696b 100644 --- a/src/base/JsonHelpers.hpp +++ b/src/base/JsonHelpers.hpp @@ -7,42 +7,7 @@ #define CONCATENATE1(arg1, arg2) CONCATENATE2(arg1, arg2) #define CONCATENATE2(arg1, arg2) arg1##arg2 -#define EXPAND(x) x -#define FOR_EACH_1(what, x, ...) what(x) - -#define FOR_EACH_2(what, x, ...) \ - what(x); \ - EXPAND(FOR_EACH_1(what, __VA_ARGS__)) -#define FOR_EACH_3(what, x, ...) \ - what(x); \ - EXPAND(FOR_EACH_2(what, __VA_ARGS__)) -#define FOR_EACH_4(what, x, ...) \ - what(x); \ - EXPAND(FOR_EACH_3(what, __VA_ARGS__)) -#define FOR_EACH_5(what, x, ...) \ - what(x); \ - EXPAND(FOR_EACH_4(what, __VA_ARGS__)) -#define FOR_EACH_6(what, x, ...) \ - what(x); \ - EXPAND(FOR_EACH_5(what, __VA_ARGS__)) -#define FOR_EACH_7(what, x, ...) \ - what(x); \ - EXPAND(FOR_EACH_6(what, __VA_ARGS__)) -#define FOR_EACH_8(what, x, ...) \ - what(x); \ - EXPAND(FOR_EACH_7(what, __VA_ARGS__)) - -#define FOR_EACH_NARG(...) FOR_EACH_NARG_(__VA_ARGS__, FOR_EACH_RSEQ_N()) -#define FOR_EACH_NARG_(...) EXPAND(FOR_EACH_ARG_N(__VA_ARGS__)) -#define FOR_EACH_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N -#define FOR_EACH_RSEQ_N() 8, 7, 6, 5, 4, 3, 2, 1, 0 -#define CONCATENATE(x, y) x##y -#define FOR_EACH_(N, what, ...) EXPAND(CONCATENATE(FOR_EACH_, N)(what, __VA_ARGS__)) -#define FOR_EACH(what, ...) FOR_EACH_(FOR_EACH_NARG(__VA_ARGS__), what, __VA_ARGS__) -#define JADDEx_(jsonObj, field) jsonObj.insert(#field, field); -#define JADDEx(field) JADDEx_(root, field) - // Add key value pair into JSON named 'root' +#define JADDEx(field) root.insert(#field, field); #define JADD(...) FOR_EACH(JADDEx, __VA_ARGS__) - #define RROOT return root; diff --git a/src/base/models/CoreObjectModels.hpp b/src/base/models/CoreObjectModels.hpp index 3c4e5a68..990b117f 100644 --- a/src/base/models/CoreObjectModels.hpp +++ b/src/base/models/CoreObjectModels.hpp @@ -1,5 +1,5 @@ #pragma once -#include "3rdparty/x2struct/x2struct.hpp" +#include "libs/QJsonStruct/QJsonStruct.hpp" #include #include @@ -13,7 +13,7 @@ namespace Qv2ray::base::objects { QString user; QString pass; - XTOSTRUCT(O(user, pass)) + JSONSTRUCT_REGISTER(AccountObject, F(user, pass)) }; // // @@ -24,7 +24,7 @@ namespace Qv2ray::base::objects ApiObject() : tag("api"), services() { } - XTOSTRUCT(O(tag, services)) + JSONSTRUCT_REGISTER(ApiObject, F(tag, services)) }; // // @@ -35,7 +35,7 @@ namespace Qv2ray::base::objects SystemPolicyObject() : statsInboundUplink(), statsInboundDownlink() { } - XTOSTRUCT(O(statsInboundUplink, statsInboundDownlink)) + JSONSTRUCT_REGISTER(SystemPolicyObject, F(statsInboundUplink, statsInboundDownlink)) }; // // @@ -51,7 +51,7 @@ namespace Qv2ray::base::objects LevelPolicyObject() : handshake(), connIdle(), uplinkOnly(), downlinkOnly(), statsUserUplink(), statsUserDownlink(), bufferSize() { } - XTOSTRUCT(O(handshake, connIdle, uplinkOnly, downlinkOnly, statsUserUplink, statsUserDownlink, bufferSize)) + JSONSTRUCT_REGISTER(LevelPolicyObject, F(handshake, connIdle, uplinkOnly, downlinkOnly, statsUserUplink, statsUserDownlink, bufferSize)) }; // // @@ -62,7 +62,7 @@ namespace Qv2ray::base::objects PolicyObject() : level(), system() { } - XTOSTRUCT(O(level, system)) + JSONSTRUCT_REGISTER(PolicyObject, F(level, system)) }; // // @@ -90,8 +90,8 @@ namespace Qv2ray::base::objects port("1-65535"), network(""), source(), user(), inboundTag(), protocol(), attrs(), outboundTag(""), balancerTag("") { } - XTOSTRUCT(O(QV2RAY_RULE_ENABLED, QV2RAY_RULE_USE_BALANCER, QV2RAY_RULE_TAG, type, domain, ip, port, network, source, user, inboundTag, - protocol, attrs, outboundTag, balancerTag)) + JSONSTRUCT_REGISTER(RuleObject, F(QV2RAY_RULE_ENABLED, QV2RAY_RULE_USE_BALANCER, QV2RAY_RULE_TAG, type, domain, ip, port, network, + source, user, inboundTag, protocol, attrs, outboundTag, balancerTag)) }; // // @@ -102,7 +102,7 @@ namespace Qv2ray::base::objects BalancerObject() : tag(), selector() { } - XTOSTRUCT(O(tag, selector)) + JSONSTRUCT_REGISTER(BalancerObject, F(tag, selector)) }; // // @@ -117,7 +117,7 @@ namespace Qv2ray::base::objects HTTPRequestObject() : version("1.1"), method("GET"), path(), headers() { } - XTOSTRUCT(O(version, method, path, headers)) + JSONSTRUCT_REGISTER(HTTPRequestObject, F(version, method, path, headers)) }; // // @@ -130,7 +130,7 @@ namespace Qv2ray::base::objects HTTPResponseObject() : version("1.1"), status("200"), reason("OK"), headers() { } - XTOSTRUCT(O(version, status, reason, headers)) + JSONSTRUCT_REGISTER(HTTPResponseObject, F(version, status, reason, headers)) }; // // @@ -142,7 +142,7 @@ namespace Qv2ray::base::objects TCPHeader_M_Object() : type("none"), request(), response() { } - XTOSTRUCT(O(type, request, response)) + JSONSTRUCT_REGISTER(TCPHeader_M_Object, F(type, request, response)) }; // // @@ -152,7 +152,7 @@ namespace Qv2ray::base::objects HeaderObject() : type("none") { } - XTOSTRUCT(O(type)) + JSONSTRUCT_REGISTER(HeaderObject, F(type)) }; // // @@ -162,7 +162,7 @@ namespace Qv2ray::base::objects TCPObject() : header() { } - XTOSTRUCT(O(header)) + JSONSTRUCT_REGISTER(TCPObject, F(header)) }; // // @@ -179,7 +179,7 @@ namespace Qv2ray::base::objects KCPObject() : header() { } - XTOSTRUCT(O(mtu, tti, uplinkCapacity, downlinkCapacity, congestion, readBufferSize, writeBufferSize, header)) + JSONSTRUCT_REGISTER(KCPObject, F(mtu, tti, uplinkCapacity, downlinkCapacity, congestion, readBufferSize, writeBufferSize, header)) }; // // @@ -190,7 +190,7 @@ namespace Qv2ray::base::objects WebSocketObject() : path("/"), headers() { } - XTOSTRUCT(O(path, headers)) + JSONSTRUCT_REGISTER(WebSocketObject, F(path, headers)) }; // // @@ -201,7 +201,7 @@ namespace Qv2ray::base::objects HttpObject() : host(), path("/") { } - XTOSTRUCT(O(host, path)) + JSONSTRUCT_REGISTER(HttpObject, F(host, path)) }; // // @@ -211,7 +211,7 @@ namespace Qv2ray::base::objects DomainSocketObject() : path("/") { } - XTOSTRUCT(O(path)) + JSONSTRUCT_REGISTER(DomainSocketObject, F(path)) }; // // @@ -223,7 +223,7 @@ namespace Qv2ray::base::objects QuicObject() : security(""), key(""), header() { } - XTOSTRUCT(O(security, key, header)) + JSONSTRUCT_REGISTER(QuicObject, F(security, key, header)) }; // // @@ -235,7 +235,7 @@ namespace Qv2ray::base::objects SockoptObject() : mark(0), tcpFastOpen(false), tproxy("off") { } - XTOSTRUCT(O(mark, tcpFastOpen, tproxy)) + JSONSTRUCT_REGISTER(SockoptObject, F(mark, tcpFastOpen, tproxy)) }; // // @@ -249,7 +249,7 @@ namespace Qv2ray::base::objects CertificateObject() : usage(), certificateFile(), keyFile(), certificate(), key() { } - XTOSTRUCT(O(usage, certificateFile, keyFile, certificate, key)) + JSONSTRUCT_REGISTER(CertificateObject, F(usage, certificateFile, keyFile, certificate, key)) }; // // @@ -264,7 +264,7 @@ namespace Qv2ray::base::objects TLSObject() : serverName(), allowInsecure(), allowInsecureCiphers(), certificates(), disableSystemRoot() { } - XTOSTRUCT(O(serverName, allowInsecure, allowInsecureCiphers, alpn, certificates, disableSystemRoot)) + JSONSTRUCT_REGISTER(TLSObject, F(serverName, allowInsecure, allowInsecureCiphers, alpn, certificates, disableSystemRoot)) }; } // namespace transfer // @@ -276,7 +276,7 @@ namespace Qv2ray::base::objects SniffingObject() : enabled(), destOverride() { } - XTOSTRUCT(O(enabled, destOverride)) + JSONSTRUCT_REGISTER(SniffingObject, F(enabled, destOverride)) }; // // @@ -297,7 +297,8 @@ namespace Qv2ray::base::objects dsSettings(), quicSettings() { } - XTOSTRUCT(O(network, security, sockopt, tcpSettings, tlsSettings, kcpSettings, wsSettings, httpSettings, dsSettings, quicSettings)) + JSONSTRUCT_REGISTER(StreamSettingsObject, F(network, security, sockopt, tcpSettings, tlsSettings, kcpSettings, wsSettings, httpSettings, + dsSettings, quicSettings)) }; // // @@ -308,7 +309,7 @@ namespace Qv2ray::base::objects MuxObject() : enabled(), concurrency() { } - XTOSTRUCT(O(enabled, concurrency)) + JSONSTRUCT_REGISTER(MuxObject, F(enabled, concurrency)) }; // // Some protocols from: https://v2ray.com/chapter_02/02_protocols.html @@ -323,7 +324,7 @@ namespace Qv2ray::base::objects DNSOut() : network(""), address("0.0.0.0"), port(0) { } - XTOSTRUCT(O(network, address, port)) + JSONSTRUCT_REGISTER(DNSOut, F(network, address, port)) }; // // MTProto, InBound || OutBound @@ -337,10 +338,10 @@ namespace Qv2ray::base::objects UserObject() : email("user@domain.com"), level(0), secret("") { } - XTOSTRUCT(O(email, level, secret)) + JSONSTRUCT_REGISTER(UserObject, F(email, level, secret)) }; QList users; - XTOSTRUCT(O(users)) + JSONSTRUCT_REGISTER(MTProtoIn, F(users)) }; // // Socks, OutBound @@ -354,16 +355,14 @@ namespace Qv2ray::base::objects UserObject() : user(), pass(), level(0) { } - XTOSTRUCT(O(user, pass, level)) + JSONSTRUCT_REGISTER(UserObject, F(user, pass, level)) }; QString address; int port; QList users; - SocksServerObject() : address("0.0.0.0"), port(0), users() - { - } - XTOSTRUCT(O(address, port, users)) + SocksServerObject() : address("0.0.0.0"), port(0), users(){}; + JSONSTRUCT_REGISTER(SocksServerObject, F(address, port, users)) }; // // VMess Server @@ -378,7 +377,7 @@ namespace Qv2ray::base::objects UserObject() : id(""), alterId(64), security("auto"), level(0) { } - XTOSTRUCT(O(id, alterId, security, level)) + JSONSTRUCT_REGISTER(UserObject, F(id, alterId, security, level)) }; QString address; @@ -387,7 +386,7 @@ namespace Qv2ray::base::objects VMessServerObject() : address(""), port(0), users() { } - XTOSTRUCT(O(address, port, users)) + JSONSTRUCT_REGISTER(VMessServerObject, F(address, port, users)) }; // // ShadowSocks Server @@ -404,7 +403,7 @@ namespace Qv2ray::base::objects : email("user@domain.com"), address("0.0.0.0"), method("aes-256-cfb"), password(""), ota(false), level(0), port(0) { } - XTOSTRUCT(O(email, address, port, method, password, ota, level)) + JSONSTRUCT_REGISTER(ShadowSocksServerObject, F(email, address, port, method, password, ota, level)) }; } // namespace protocol } // namespace Qv2ray::base::objects diff --git a/src/base/models/QvConfigIdentifier.hpp b/src/base/models/QvConfigIdentifier.hpp index 4137c9d9..0708951f 100644 --- a/src/base/models/QvConfigIdentifier.hpp +++ b/src/base/models/QvConfigIdentifier.hpp @@ -1,5 +1,5 @@ #pragma once -#include "3rdparty/x2struct/x2struct.hpp" +#include "libs/QJsonStruct/QJsonStruct.hpp" #include #include @@ -13,39 +13,39 @@ namespace Qv2ray::base { QString displayName; QList connections; - int64_t importDate; + qint64 importDate; GroupObject_Config() : displayName(), connections(), importDate() { } - XTOSTRUCT(O(displayName, connections, importDate)) + JSONSTRUCT_REGISTER(GroupObject_Config, F(displayName, connections, importDate)) }; struct SubscriptionObject_Config : GroupObject_Config { // QString address; - int64_t lastUpdated; + qint64 lastUpdated; float updateInterval; SubscriptionObject_Config() : address(""), lastUpdated(system_clock::to_time_t(system_clock::now())), updateInterval(10) { } - XTOSTRUCT(O(lastUpdated, updateInterval, address, connections, displayName, importDate)) + JSONSTRUCT_REGISTER(SubscriptionObject_Config, F(lastUpdated, updateInterval, address), B(GroupObject_Config)) }; struct ConnectionObject_Config { QString displayName; - int64_t importDate; - int64_t lastConnected; - int64_t latency; - int64_t upLinkData; - int64_t downLinkData; + qint64 importDate; + qint64 lastConnected; + qint64 latency; + qint64 upLinkData; + qint64 downLinkData; ConnectionObject_Config() : displayName(), importDate(system_clock::to_time_t(system_clock::now())), lastConnected(), latency(QVTCPING_VALUE_NODATA), upLinkData(0), downLinkData(0) { } - XTOSTRUCT(O(displayName, importDate, lastConnected, latency, upLinkData, downLinkData)) + JSONSTRUCT_REGISTER(ConnectionObject_Config, F(displayName, importDate, lastConnected, latency, upLinkData, downLinkData)) }; } // namespace Qv2ray::base diff --git a/src/base/models/QvSettingsObject.hpp b/src/base/models/QvSettingsObject.hpp index c8ed20f8..c80227ea 100644 --- a/src/base/models/QvSettingsObject.hpp +++ b/src/base/models/QvSettingsObject.hpp @@ -1,7 +1,7 @@ #pragma once -#include "3rdparty/x2struct/x2struct.hpp" #include "base/models/CoreObjectModels.hpp" #include "base/models/QvConfigIdentifier.hpp" +#include "libs/QJsonStruct/QJsonStruct.hpp" #include @@ -22,7 +22,7 @@ namespace Qv2ray::base::config Message("") { } - XTOSTRUCT(O(Bold, Italic, ColorA, ColorR, ColorG, ColorB, Size, Family, Message, ContentType)) + JSONSTRUCT_REGISTER(QvBarLine, F(Bold, Italic, ColorA, ColorR, ColorG, ColorB, Size, Family, Message, ContentType)) }; struct QvBarPage @@ -32,13 +32,13 @@ namespace Qv2ray::base::config QvBarPage() : OffsetYpx(5) { } - XTOSTRUCT(O(OffsetYpx, Lines)) + JSONSTRUCT_REGISTER(QvBarPage, F(OffsetYpx, Lines)) }; struct Qv2rayToolBarConfig { QList Pages; - XTOSTRUCT(O(Pages)) + JSONSTRUCT_REGISTER(Qv2rayToolBarConfig, F(Pages)) }; struct Qv2rayForwardProxyConfig @@ -54,7 +54,7 @@ namespace Qv2ray::base::config : enableForwardProxy(false), type("http"), serverAddress("127.0.0.1"), port(8008), useAuth(false), username(), password() { } - XTOSTRUCT(O(enableForwardProxy, type, serverAddress, port, useAuth, username, password)) + JSONSTRUCT_REGISTER(Qv2rayForwardProxyConfig, F(enableForwardProxy, type, serverAddress, port, useAuth, username, password)) }; struct Qv2rayInboundsConfig @@ -96,9 +96,10 @@ namespace Qv2ray::base::config { } - XTOSTRUCT(O(setSystemProxy, listenip, useSocks, useHTTP, socks_port, socks_useAuth, socksAccount, socksSniffing, socksUDP, socksLocalIP, - http_port, http_useAuth, httpAccount, httpSniffing, useTPROXY, tproxy_ip, tproxy_port, tproxy_use_tcp, tproxy_use_udp, - tproxy_followRedirect, tproxy_mode, dnsIntercept)) + JSONSTRUCT_REGISTER(Qv2rayInboundsConfig, + F(setSystemProxy, listenip, useSocks, useHTTP, socks_port, socks_useAuth, socksAccount, socksSniffing, socksUDP, + socksLocalIP, http_port, http_useAuth, httpAccount, httpSniffing, useTPROXY), + F(tproxy_ip, tproxy_port, tproxy_use_tcp, tproxy_use_udp, tproxy_followRedirect, tproxy_mode, dnsIntercept)) }; struct Qv2rayOutboundsConfig @@ -107,7 +108,7 @@ namespace Qv2ray::base::config Qv2rayOutboundsConfig() : mark(255) { } - XTOSTRUCT(O(mark)) + JSONSTRUCT_REGISTER(Qv2rayOutboundsConfig, F(mark)) }; struct Qv2rayUIConfig @@ -124,7 +125,8 @@ namespace Qv2ray::base::config : theme("Fusion"), language("en_US"), useDarkTheme(false), useDarkTrayIcon(true), maximumLogLines(500), maxJumpListCount(20) { } - XTOSTRUCT(O(theme, language, quietMode, useDarkTheme, useDarkTrayIcon, maximumLogLines, maxJumpListCount, recentConnections)) + JSONSTRUCT_REGISTER(Qv2rayUIConfig, + F(theme, language, quietMode, useDarkTheme, useDarkTrayIcon, maximumLogLines, maxJumpListCount, recentConnections)) }; struct Qv2rayRouteConfig_Impl @@ -139,7 +141,7 @@ namespace Qv2ray::base::config } Qv2rayRouteConfig_Impl(const QList &_direct, const QList &_block, const QList &_proxy) : direct(_direct), block(_block), proxy(_proxy){}; - XTOSTRUCT(O(proxy, block, direct)) + JSONSTRUCT_REGISTER(Qv2rayRouteConfig_Impl, F(proxy, block, direct)) }; struct Qv2rayRouteConfig @@ -154,7 +156,7 @@ namespace Qv2ray::base::config Qv2rayRouteConfig(){}; Qv2rayRouteConfig(const Qv2rayRouteConfig_Impl &_domains, const Qv2rayRouteConfig_Impl &_ips, const QString &ds) : domainStrategy(ds), domains(_domains), ips(_ips){}; - XTOSTRUCT(O(domainStrategy, domains, ips)) + JSONSTRUCT_REGISTER(Qv2rayRouteConfig, F(domainStrategy, domains, ips)) }; struct Qv2rayPluginConfig @@ -163,7 +165,7 @@ namespace Qv2ray::base::config bool v2rayIntegration; int portAllocationStart; Qv2rayPluginConfig() : pluginStates(), v2rayIntegration(true), portAllocationStart(15000){}; - XTOSTRUCT(O(pluginStates, v2rayIntegration)) + JSONSTRUCT_REGISTER(Qv2rayPluginConfig, F(pluginStates, v2rayIntegration, portAllocationStart)) }; struct Qv2rayConnectionConfig @@ -181,7 +183,8 @@ namespace Qv2ray::base::config dnsList(QStringList{ "8.8.4.4", "1.1.1.1" }) { } - XTOSTRUCT(O(bypassCN, bypassBT, enableProxy, v2rayFreedomDNS, withLocalDNS, dnsList, forwardProxyConfig, routeConfig)) + JSONSTRUCT_REGISTER(Qv2rayConnectionConfig, + F(bypassCN, bypassBT, enableProxy, v2rayFreedomDNS, withLocalDNS, dnsList, forwardProxyConfig, routeConfig)) }; struct Qv2rayAPIConfig @@ -191,7 +194,7 @@ namespace Qv2ray::base::config Qv2rayAPIConfig() : enableAPI(true), statsPort(15490) { } - XTOSTRUCT(O(enableAPI, statsPort)) + JSONSTRUCT_REGISTER(Qv2rayAPIConfig, F(enableAPI, statsPort)) }; struct Qv2rayKernelConfig @@ -232,7 +235,8 @@ namespace Qv2ray::base::config #undef _VARNAME_VCOREPATH_ #undef _VARNAME_VASSETSPATH_ - XTOSTRUCT(O(v2CorePath_linux, v2AssetsPath_linux, v2CorePath_macx, v2AssetsPath_macx, v2CorePath_win, v2AssetsPath_win)) + JSONSTRUCT_REGISTER(Qv2rayKernelConfig, + F(v2CorePath_linux, v2AssetsPath_linux, v2CorePath_macx, v2AssetsPath_macx, v2CorePath_win, v2AssetsPath_win)) }; struct Qv2rayUpdateConfig @@ -243,7 +247,7 @@ namespace Qv2ray::base::config /// 0: Stable /// 1: Testing int updateChannel; - XTOSTRUCT(O(ignoredVersion, updateChannel)) + JSONSTRUCT_REGISTER(Qv2rayUpdateConfig, F(ignoredVersion, updateChannel)) }; struct Qv2rayAdvancedConfig @@ -251,16 +255,16 @@ namespace Qv2ray::base::config bool setAllowInsecure; bool setAllowInsecureCiphers; bool testLatencyPeriodcally; - XTOSTRUCT(O(setAllowInsecure, setAllowInsecureCiphers, testLatencyPeriodcally)) + JSONSTRUCT_REGISTER(Qv2rayAdvancedConfig, F(setAllowInsecure, setAllowInsecureCiphers, testLatencyPeriodcally)) }; struct Qv2rayNetworkConfig { - enum Qv2rayProxyType + enum Qv2rayProxyType : int { - QVPROXY_NONE, - QVPROXY_SYSTEM, - QVPROXY_CUSTOM + QVPROXY_NONE = 0, + QVPROXY_SYSTEM = 1, + QVPROXY_CUSTOM = 2 } proxyType; QString address; @@ -273,7 +277,7 @@ namespace Qv2ray::base::config type("http"), // port(8000), // userAgent("Qv2ray/$VERSION WebRequestHelper"){}; - XTOSTRUCT(O(proxyType, type, address, port, userAgent)) + JSONSTRUCT_REGISTER(Qv2rayNetworkConfig, F(proxyType, type, address, port, userAgent)) }; struct Qv2rayConfig @@ -324,24 +328,25 @@ namespace Qv2ray::base::config { } - XTOSTRUCT(O(config_version, // - tProxySupport, // - logLevel, // - uiConfig, // - pluginConfig, // - updateConfig, // - kernelConfig, // - networkConfig, // - groups, // - connections, // - subscriptions, // - autoStartId, // - inboundConfig, // - outboundConfig, // - connectionConfig, // - toolBarConfig, // - advancedConfig, // - apiConfig // - )) + JSONSTRUCT_REGISTER(Qv2rayConfig, + F(config_version, // + tProxySupport, // + logLevel, // + uiConfig, // + pluginConfig, // + updateConfig, // + kernelConfig, // + networkConfig, // + groups, // + connections, // + subscriptions, // + autoStartId, // + inboundConfig, // + outboundConfig, // + connectionConfig), + F(toolBarConfig, // + advancedConfig, // + apiConfig // + )) }; } // namespace Qv2ray::base::config diff --git a/src/common/QvHelpers.hpp b/src/common/QvHelpers.hpp index 14c61e30..584beb19 100644 --- a/src/common/QvHelpers.hpp +++ b/src/common/QvHelpers.hpp @@ -49,27 +49,6 @@ namespace Qv2ray::common return GenerateRandomString().toLower(); // return QUuid::createUuid().toString(QUuid::WithoutBraces); } - // - template - QString StructToJsonString(const TYPE &t) - { - return QString::fromStdString(x2struct::X::tojson(t, "", 4, ' ')); - } - // - template - TYPE StructFromJsonString(const QString &str) - { - TYPE v; - x2struct::X::loadjson(str.toStdString(), v, false); - return v; - } - // Misc - template - QJsonObject GetRootObject(const T &t) - { - auto json = StructToJsonString(t); - return JsonFromString(json); - } inline QString TruncateString(const QString &str, int limit = -1, const QString &suffix = "...") { diff --git a/src/components/plugins/toolbar/QvToolbar.cpp b/src/components/plugins/toolbar/QvToolbar.cpp index 967bfd22..bcee2f4e 100644 --- a/src/components/plugins/toolbar/QvToolbar.cpp +++ b/src/components/plugins/toolbar/QvToolbar.cpp @@ -172,7 +172,7 @@ namespace Qv2ray::components::plugins } } #undef CL - reply = StructToJsonString(BarConfig); + reply = JsonToString(BarConfig.toJson()); return reply; } } // namespace Toolbar diff --git a/src/components/route/RouteSchemeIO.hpp b/src/components/route/RouteSchemeIO.hpp index 3454061a..53267313 100644 --- a/src/components/route/RouteSchemeIO.hpp +++ b/src/components/route/RouteSchemeIO.hpp @@ -26,7 +26,7 @@ namespace Qv2ray::components::route QString description; // M: all these fields are mandatory - XTOSTRUCT(M(name, author, description, domainStrategy, domains, ips)); + JSONSTRUCT_REGISTER(Qv2rayRouteScheme, F(name, author, description, domainStrategy, domains, ips)); }; } // namespace Qv2ray::components::route diff --git a/src/core/CoreUtils.cpp b/src/core/CoreUtils.cpp index 282a0b7c..791b5af5 100644 --- a/src/core/CoreUtils.cpp +++ b/src/core/CoreUtils.cpp @@ -32,24 +32,21 @@ namespace Qv2ray::core if (*protocol == "vmess") { - auto Server = - StructFromJsonString(JsonToString(out["settings"].toObject()["vnext"].toArray().first().toObject())); + auto Server = VMessServerObject::fromJson(out["settings"].toObject()["vnext"].toArray().first().toObject()); *host = Server.address; *port = Server.port; return true; } else if (*protocol == "shadowsocks") { - auto x = JsonToString(out["settings"].toObject()["servers"].toArray().first().toObject()); - auto Server = StructFromJsonString(x); + auto Server = ShadowSocksServerObject::fromJson(out["settings"].toObject()["servers"].toArray().first().toObject()); *host = Server.address; *port = Server.port; return true; } else if (*protocol == "socks") { - auto x = JsonToString(out["settings"].toObject()["servers"].toArray().first().toObject()); - auto Server = StructFromJsonString(x); + auto Server = SocksServerObject::fromJson(out["settings"].toObject()["servers"].toArray().first().toObject()); *host = Server.address; *port = Server.port; return true; @@ -120,7 +117,7 @@ namespace Qv2ray::core int64_t GetConnectionLatency(const ConnectionId &id) { auto connection = ConnectionManager->GetConnectionMetaObject(id); - return max(connection.latency, (int64_t) 0); + return max(connection.latency, {}); } const QString GetConnectionProtocolString(const ConnectionId &id) diff --git a/src/core/connection/Generation.cpp b/src/core/connection/Generation.cpp index 4e234e2d..3406439c 100644 --- a/src/core/connection/Generation.cpp +++ b/src/core/connection/Generation.cpp @@ -163,7 +163,7 @@ namespace Qv2ray::core::connection continue; } - accounts.append(GetRootObject(account)); + accounts.append(account.toJson()); } if (!accounts.isEmpty()) @@ -212,7 +212,7 @@ namespace Qv2ray::core::connection continue; } - accounts.append(GetRootObject(acc)); + accounts.append(acc.toJson()); } if (!accounts.isEmpty()) diff --git a/src/core/connection/Serialization.cpp b/src/core/connection/Serialization.cpp index b9aa8908..56d2bc2c 100644 --- a/src/core/connection/Serialization.cpp +++ b/src/core/connection/Serialization.cpp @@ -89,13 +89,13 @@ namespace Qv2ray::core::connection QString sharelink = ""; if (type == "vmess") { - auto vmessServer = StructFromJsonString(JsonToString(settings["vnext"].toArray().first().toObject())); - auto transport = StructFromJsonString(JsonToString(outbound["streamSettings"].toObject())); + auto vmessServer = VMessServerObject::fromJson(settings["vnext"].toArray().first().toObject()); + auto transport = StreamSettingsObject::fromJson(outbound["streamSettings"].toObject()); sharelink = vmess::ConvertConfigToVMessString(transport, vmessServer, alias); } else if (type == "shadowsocks") { - auto ssServer = StructFromJsonString(JsonToString(settings["servers"].toArray().first().toObject())); + auto ssServer = ShadowSocksServerObject::fromJson(settings["servers"].toArray().first().toObject()); sharelink = ss::ConvertConfigToSSString(ssServer, alias, isSip002); } else diff --git a/src/core/connection/Serialization_vmess.cpp b/src/core/connection/Serialization_vmess.cpp index 8add3fa5..034f7247 100644 --- a/src/core/connection/Serialization_vmess.cpp +++ b/src/core/connection/Serialization_vmess.cpp @@ -225,7 +225,7 @@ namespace Qv2ray::core::connection // VMess root config OUTBOUNDSETTING vConf; QJsonArray vnextArray; - vnextArray.append(JsonFromString(StructToJsonString(serv))); + vnextArray.append(serv.toJson()); vConf["vnext"] = vnextArray; // // Stream Settings @@ -285,7 +285,7 @@ namespace Qv2ray::core::connection streaming.network = net; // // WARN Mux is missing here. - auto outbound = GenerateOutboundEntry("vmess", vConf, GetRootObject(streaming), QJsonObject(), "0.0.0.0", OUTBOUND_TAG_PROXY); + auto outbound = GenerateOutboundEntry("vmess", vConf, streaming.toJson(), {}, "0.0.0.0", OUTBOUND_TAG_PROXY); // root["outbounds"] = QJsonArray() << outbound; // If previous alias is empty, just the PS is needed, else, append a "_" diff --git a/src/core/settings/SettingsBackend.cpp b/src/core/settings/SettingsBackend.cpp index f03431cb..6cee454a 100644 --- a/src/core/settings/SettingsBackend.cpp +++ b/src/core/settings/SettingsBackend.cpp @@ -12,7 +12,7 @@ namespace Qv2ray::core::config void SaveGlobalSettings() { - QString str = StructToJsonString(GlobalConfig); + QString str = JsonToString(GlobalConfig.toJson()); StringToFile(str, QV2RAY_CONFIG_FILE); } diff --git a/src/core/settings/SettingsUpgrade.cpp b/src/core/settings/SettingsUpgrade.cpp index edbaad98..1cdd6df8 100644 --- a/src/core/settings/SettingsUpgrade.cpp +++ b/src/core/settings/SettingsUpgrade.cpp @@ -83,8 +83,7 @@ namespace Qv2ray _conf.address = item.value().toString(); _conf.lastUpdated = system_clock::to_time_t(system_clock::now()); _conf.updateInterval = 5; - auto value = GetRootObject(_conf); - newSubscriptions[key] = value; + newSubscriptions[key] = _conf.toJson(); } root["subscriptions"] = newSubscriptions; diff --git a/src/main.cpp b/src/main.cpp index 8055c6ed..8cc3dcdc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -312,7 +312,7 @@ int main(int argc, char *argv[]) } // Load config object from upgraded config QJsonObject - auto confObject = StructFromJsonString(JsonToString(conf)); + auto confObject = Qv2rayConfig::fromJson(conf); if (confObject.uiConfig.language.isEmpty()) { diff --git a/src/ui/editors/w_OutboundEditor.cpp b/src/ui/editors/w_OutboundEditor.cpp index 40b426fd..6beeaea5 100644 --- a/src/ui/editors/w_OutboundEditor.cpp +++ b/src/ui/editors/w_OutboundEditor.cpp @@ -80,7 +80,7 @@ QString OutboundEditor::GetFriendlyName() OUTBOUND OutboundEditor::GenerateConnectionJson() { OUTBOUNDSETTING settings; - auto streaming = GetRootObject(streamSettingsWidget->GetStreamSettings()); + auto streaming = streamSettingsWidget->GetStreamSettings().toJson(); if (outboundType == "vmess") { @@ -88,7 +88,7 @@ OUTBOUND OutboundEditor::GenerateConnectionJson() QJsonArray vnext; vmess.address = address; vmess.port = port; - vnext.append(GetRootObject(vmess)); + vnext.append(vmess.toJson()); settings.insert("vnext", vnext); } else if (outboundType == "shadowsocks") @@ -98,7 +98,7 @@ OUTBOUND OutboundEditor::GenerateConnectionJson() QJsonArray servers; shadowsocks.address = address; shadowsocks.port = port; - servers.append(GetRootObject(shadowsocks)); + servers.append(shadowsocks.toJson()); settings["servers"] = servers; } else if (outboundType == "socks") @@ -113,7 +113,7 @@ OUTBOUND OutboundEditor::GenerateConnectionJson() streaming = QJsonObject(); LOG(MODULE_CONNECTION, "Socks outbound does not need StreamSettings.") QJsonArray servers; - servers.append(GetRootObject(socks)); + servers.append(socks.toJson()); settings["servers"] = servers; } else @@ -149,7 +149,7 @@ void OutboundEditor::ReloadGUI() outboundType = originalConfig["protocol"].toString("vmess"); muxConfig = originalConfig["mux"].toObject(); useForwardProxy = originalConfig[QV2RAY_USE_FPROXY_KEY].toBool(false); - streamSettingsWidget->SetStreamObject(StructFromJsonString(JsonToString(originalConfig["streamSettings"].toObject()))); + streamSettingsWidget->SetStreamObject(StreamSettingsObject::fromJson(originalConfig["streamSettings"].toObject())); // useFPCB->setChecked(useForwardProxy); muxEnabledCB->setChecked(muxConfig["enabled"].toBool()); @@ -160,7 +160,7 @@ void OutboundEditor::ReloadGUI() if (outboundType == "vmess") { outBoundTypeCombo->setCurrentIndex(0); - vmess = StructFromJsonString(JsonToString(settings["vnext"].toArray().first().toObject())); + vmess = VMessServerObject::fromJson(settings["vnext"].toArray().first().toObject()); if (vmess.users.empty()) { vmess.users.push_back({}); @@ -174,7 +174,7 @@ void OutboundEditor::ReloadGUI() else if (outboundType == "shadowsocks") { outBoundTypeCombo->setCurrentIndex(1); - shadowsocks = StructFromJsonString(JsonToString(settings["servers"].toArray().first().toObject())); + shadowsocks = ShadowSocksServerObject::fromJson(settings["servers"].toArray().first().toObject()); address = shadowsocks.address; port = shadowsocks.port; // ShadowSocks Configs @@ -187,7 +187,7 @@ void OutboundEditor::ReloadGUI() else if (outboundType == "socks") { outBoundTypeCombo->setCurrentIndex(2); - socks = StructFromJsonString(JsonToString(settings["servers"].toArray().first().toObject())); + socks = SocksServerObject::fromJson(settings["servers"].toArray().first().toObject()); address = socks.address; port = socks.port; if (socks.users.empty()) diff --git a/src/ui/editors/w_RoutesEditor.cpp b/src/ui/editors/w_RoutesEditor.cpp index 14479a16..863d7da6 100644 --- a/src/ui/editors/w_RoutesEditor.cpp +++ b/src/ui/editors/w_RoutesEditor.cpp @@ -106,7 +106,7 @@ RouteEditor::RouteEditor(QJsonObject connection, QWidget *parent) : QDialog(pare for (auto item : root["routing"].toObject()["rules"].toArray()) { - AddRule(StructFromJsonString(JsonToString(item.toObject()))); + AddRule(RuleObject::fromJson(item.toObject())); } // Set default outboung combo text AFTER adding all outbounds. @@ -312,7 +312,7 @@ CONFIGROOT RouteEditor::OpenEditor() for (auto i = 0; i < ruleListWidget->count(); i++) { auto _rule = rules[ruleListWidget->item(i)->text()]; - auto ruleJsonObject = GetRootObject(_rule); + auto ruleJsonObject = _rule.toJson(); // Process balancer for a rule if (_rule.QV2RAY_RULE_USE_BALANCER) diff --git a/src/ui/widgets/RouteSettingsMatrix.cpp b/src/ui/widgets/RouteSettingsMatrix.cpp index 6742bea3..b39c2061 100644 --- a/src/ui/widgets/RouteSettingsMatrix.cpp +++ b/src/ui/widgets/RouteSettingsMatrix.cpp @@ -104,7 +104,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(ACCESS_OPTIONAL_VALUE(filePath)); - auto scheme = StructFromJsonString(content); + auto scheme = Qv2rayRouteScheme::fromJson(JsonFromString(content)); // show the information of this scheme to user, // and ask user if he/she wants to import and apply this. @@ -174,7 +174,7 @@ void RouteSettingsMatrixWidget::on_exportSchemeBtn_clicked() scheme.domains = config.domains; // serialize and write out - auto content = StructToJsonString(scheme); + auto content = JsonToString(scheme.toJson()); StringToFile(content, ACCESS_OPTIONAL_VALUE(savePath)); // done diff --git a/src/ui/widgets/StreamSettingsWidget.cpp b/src/ui/widgets/StreamSettingsWidget.cpp index eea7fa3e..4fdd6312 100644 --- a/src/ui/widgets/StreamSettingsWidget.cpp +++ b/src/ui/widgets/StreamSettingsWidget.cpp @@ -40,8 +40,8 @@ void StreamSettingsWidget::SetStreamObject(const StreamSettingsObject &sso) alpnTxt->setPlainText(stream.tlsSettings.alpn.join(NEWLINE)); // TCP tcpHeaderTypeCB->setCurrentText(stream.tcpSettings.header.type); - tcpRequestTxt->setPlainText(StructToJsonString(stream.tcpSettings.header.request)); - tcpRespTxt->setPlainText(StructToJsonString(stream.tcpSettings.header.response)); + tcpRequestTxt->setPlainText(JsonToString(stream.tcpSettings.header.request.toJson())); + tcpRespTxt->setPlainText(JsonToString(stream.tcpSettings.header.response.toJson())); // HTTP httpHostTxt->setPlainText(stream.httpSettings.host.join(NEWLINE)); httpPathTxt->setText(stream.httpSettings.path); @@ -251,18 +251,18 @@ void StreamSettingsWidget::on_dsPathTxt_textEdited(const QString &arg1) void StreamSettingsWidget::on_tcpRequestEditBtn_clicked() { JsonEditor w(JsonFromString(tcpRequestTxt->toPlainText()), this); - auto rString = JsonToString(w.OpenEditor()); - tcpRequestTxt->setPlainText(rString); - auto tcpReqObject = StructFromJsonString(rString); + auto rJson = w.OpenEditor(); + tcpRequestTxt->setPlainText(JsonToString(rJson)); + auto tcpReqObject = HTTPRequestObject::fromJson(rJson); stream.tcpSettings.header.request = tcpReqObject; } void StreamSettingsWidget::on_tcpResponseEditBtn_clicked() { JsonEditor w(JsonFromString(tcpRespTxt->toPlainText()), this); - auto rString = JsonToString(w.OpenEditor()); - tcpRespTxt->setPlainText(rString); - auto tcpRspObject = StructFromJsonString(rString); + auto rJson = w.OpenEditor(); + tcpRespTxt->setPlainText(JsonToString(rJson)); + auto tcpRspObject = HTTPResponseObject::fromJson(rJson); stream.tcpSettings.header.response = tcpRspObject; } From 4e73e6ece0fcac9d3f7b13785aca8cb2affec53a Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Tue, 28 Apr 2020 20:57:24 +0800 Subject: [PATCH 011/385] format: formatted headers --- CMakeLists.txt | 1 + makespec/BUILDVERSION | 2 +- src/base/models/CoreObjectModels.hpp | 114 +++++++-------------------- 3 files changed, 31 insertions(+), 86 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6a89bcee..5dbca442 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,6 +8,7 @@ if(NOT CMAKE_BUILD_TYPE STREQUAL "Release") math(EXPR QV2RAY_BUILD_VERSION "1 + ${QV2RAY_BUILD_VERSION}") message("Increasing BUILDVERSION") file(WRITE "${CMAKE_SOURCE_DIR}/makespec/BUILDVERSION" ${QV2RAY_BUILD_VERSION}) + add_definitions(-DNODE_DEBUG_DRAWING) endif() set(QV2RAY_VERSION_STRING "${QV2RAY_VERSION}${QV2RAY_VERSION_SUFFIX}") diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 00d32773..90e9994f 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5339 \ No newline at end of file +5341 \ No newline at end of file diff --git a/src/base/models/CoreObjectModels.hpp b/src/base/models/CoreObjectModels.hpp index 990b117f..d73a584c 100644 --- a/src/base/models/CoreObjectModels.hpp +++ b/src/base/models/CoreObjectModels.hpp @@ -13,6 +13,7 @@ namespace Qv2ray::base::objects { QString user; QString pass; + AccountObject() : user(), pass(){}; JSONSTRUCT_REGISTER(AccountObject, F(user, pass)) }; // @@ -21,9 +22,7 @@ namespace Qv2ray::base::objects { QString tag; QList services; - ApiObject() : tag("api"), services() - { - } + ApiObject() : tag("api"), services(){}; JSONSTRUCT_REGISTER(ApiObject, F(tag, services)) }; // @@ -32,9 +31,7 @@ namespace Qv2ray::base::objects { bool statsInboundUplink; bool statsInboundDownlink; - SystemPolicyObject() : statsInboundUplink(), statsInboundDownlink() - { - } + SystemPolicyObject() : statsInboundUplink(), statsInboundDownlink(){}; JSONSTRUCT_REGISTER(SystemPolicyObject, F(statsInboundUplink, statsInboundDownlink)) }; // @@ -48,9 +45,7 @@ namespace Qv2ray::base::objects bool statsUserUplink; bool statsUserDownlink; int bufferSize; - LevelPolicyObject() : handshake(), connIdle(), uplinkOnly(), downlinkOnly(), statsUserUplink(), statsUserDownlink(), bufferSize() - { - } + LevelPolicyObject() : handshake(), connIdle(), uplinkOnly(), downlinkOnly(), statsUserUplink(), statsUserDownlink(), bufferSize(){}; JSONSTRUCT_REGISTER(LevelPolicyObject, F(handshake, connIdle, uplinkOnly, downlinkOnly, statsUserUplink, statsUserDownlink, bufferSize)) }; // @@ -59,9 +54,7 @@ namespace Qv2ray::base::objects { QMap level; QList system; - PolicyObject() : level(), system() - { - } + PolicyObject() : level(), system(){}; JSONSTRUCT_REGISTER(PolicyObject, F(level, system)) }; // @@ -87,9 +80,7 @@ namespace Qv2ray::base::objects QString balancerTag; RuleObject() : QV2RAY_RULE_ENABLED(true), QV2RAY_RULE_USE_BALANCER(false), QV2RAY_RULE_TAG("new rule"), type("field"), domain(), ip(), - port("1-65535"), network(""), source(), user(), inboundTag(), protocol(), attrs(), outboundTag(""), balancerTag("") - { - } + port("1-65535"), network(""), source(), user(), inboundTag(), protocol(), attrs(), outboundTag(""), balancerTag(""){}; JSONSTRUCT_REGISTER(RuleObject, F(QV2RAY_RULE_ENABLED, QV2RAY_RULE_USE_BALANCER, QV2RAY_RULE_TAG, type, domain, ip, port, network, source, user, inboundTag, protocol, attrs, outboundTag, balancerTag)) }; @@ -99,9 +90,7 @@ namespace Qv2ray::base::objects { QString tag; QList selector; - BalancerObject() : tag(), selector() - { - } + BalancerObject() : tag(), selector(){}; JSONSTRUCT_REGISTER(BalancerObject, F(tag, selector)) }; // @@ -114,9 +103,7 @@ namespace Qv2ray::base::objects QString method; QList path; QMap> headers; - HTTPRequestObject() : version("1.1"), method("GET"), path(), headers() - { - } + HTTPRequestObject() : version("1.1"), method("GET"), path(), headers(){}; JSONSTRUCT_REGISTER(HTTPRequestObject, F(version, method, path, headers)) }; // @@ -127,9 +114,7 @@ namespace Qv2ray::base::objects QString status; QString reason; QMap> headers; - HTTPResponseObject() : version("1.1"), status("200"), reason("OK"), headers() - { - } + HTTPResponseObject() : version("1.1"), status("200"), reason("OK"), headers(){}; JSONSTRUCT_REGISTER(HTTPResponseObject, F(version, status, reason, headers)) }; // @@ -139,9 +124,7 @@ namespace Qv2ray::base::objects QString type; HTTPRequestObject request; HTTPResponseObject response; - TCPHeader_M_Object() : type("none"), request(), response() - { - } + TCPHeader_M_Object() : type("none"), request(), response(){}; JSONSTRUCT_REGISTER(TCPHeader_M_Object, F(type, request, response)) }; // @@ -149,9 +132,7 @@ namespace Qv2ray::base::objects struct HeaderObject { QString type; - HeaderObject() : type("none") - { - } + HeaderObject() : type("none"){}; JSONSTRUCT_REGISTER(HeaderObject, F(type)) }; // @@ -159,9 +140,7 @@ namespace Qv2ray::base::objects struct TCPObject { TCPHeader_M_Object header; - TCPObject() : header() - { - } + TCPObject() : header(){}; JSONSTRUCT_REGISTER(TCPObject, F(header)) }; // @@ -176,9 +155,7 @@ namespace Qv2ray::base::objects int readBufferSize = 1; int writeBufferSize = 1; HeaderObject header; - KCPObject() : header() - { - } + KCPObject() : header(){}; JSONSTRUCT_REGISTER(KCPObject, F(mtu, tti, uplinkCapacity, downlinkCapacity, congestion, readBufferSize, writeBufferSize, header)) }; // @@ -187,9 +164,7 @@ namespace Qv2ray::base::objects { QString path; QMap headers; - WebSocketObject() : path("/"), headers() - { - } + WebSocketObject() : path("/"), headers(){}; JSONSTRUCT_REGISTER(WebSocketObject, F(path, headers)) }; // @@ -198,9 +173,7 @@ namespace Qv2ray::base::objects { QList host; QString path; - HttpObject() : host(), path("/") - { - } + HttpObject() : host(), path("/"){}; JSONSTRUCT_REGISTER(HttpObject, F(host, path)) }; // @@ -208,9 +181,7 @@ namespace Qv2ray::base::objects struct DomainSocketObject { QString path; - DomainSocketObject() : path("/") - { - } + DomainSocketObject() : path("/"){}; JSONSTRUCT_REGISTER(DomainSocketObject, F(path)) }; // @@ -220,9 +191,7 @@ namespace Qv2ray::base::objects QString security; QString key; HeaderObject header; - QuicObject() : security(""), key(""), header() - { - } + QuicObject() : security(""), key(""), header(){}; JSONSTRUCT_REGISTER(QuicObject, F(security, key, header)) }; // @@ -232,9 +201,7 @@ namespace Qv2ray::base::objects int mark; bool tcpFastOpen; QString tproxy; - SockoptObject() : mark(0), tcpFastOpen(false), tproxy("off") - { - } + SockoptObject() : mark(0), tcpFastOpen(false), tproxy("off"){}; JSONSTRUCT_REGISTER(SockoptObject, F(mark, tcpFastOpen, tproxy)) }; // @@ -246,9 +213,7 @@ namespace Qv2ray::base::objects QString keyFile; QList certificate; QList key; - CertificateObject() : usage(), certificateFile(), keyFile(), certificate(), key() - { - } + CertificateObject() : usage(), certificateFile(), keyFile(), certificate(), key(){}; JSONSTRUCT_REGISTER(CertificateObject, F(usage, certificateFile, keyFile, certificate, key)) }; // @@ -261,9 +226,7 @@ namespace Qv2ray::base::objects QList alpn; QList certificates; bool disableSystemRoot; - TLSObject() : serverName(), allowInsecure(), allowInsecureCiphers(), certificates(), disableSystemRoot() - { - } + TLSObject() : serverName(), allowInsecure(), allowInsecureCiphers(), certificates(), disableSystemRoot(){}; JSONSTRUCT_REGISTER(TLSObject, F(serverName, allowInsecure, allowInsecureCiphers, alpn, certificates, disableSystemRoot)) }; } // namespace transfer @@ -273,9 +236,7 @@ namespace Qv2ray::base::objects { bool enabled = false; QList destOverride; - SniffingObject() : enabled(), destOverride() - { - } + SniffingObject() : enabled(), destOverride(){}; JSONSTRUCT_REGISTER(SniffingObject, F(enabled, destOverride)) }; // @@ -294,9 +255,7 @@ namespace Qv2ray::base::objects transfer::QuicObject quicSettings; StreamSettingsObject() : network("tcp"), security("none"), sockopt(), tlsSettings(), tcpSettings(), kcpSettings(), wsSettings(), httpSettings(), - dsSettings(), quicSettings() - { - } + dsSettings(), quicSettings(){}; JSONSTRUCT_REGISTER(StreamSettingsObject, F(network, security, sockopt, tcpSettings, tlsSettings, kcpSettings, wsSettings, httpSettings, dsSettings, quicSettings)) }; @@ -306,9 +265,7 @@ namespace Qv2ray::base::objects { bool enabled; int concurrency; - MuxObject() : enabled(), concurrency() - { - } + MuxObject() : enabled(), concurrency(){}; JSONSTRUCT_REGISTER(MuxObject, F(enabled, concurrency)) }; // @@ -321,9 +278,7 @@ namespace Qv2ray::base::objects QString network; QString address; int port; - DNSOut() : network(""), address("0.0.0.0"), port(0) - { - } + DNSOut() : network(""), address("0.0.0.0"), port(0){}; JSONSTRUCT_REGISTER(DNSOut, F(network, address, port)) }; // @@ -335,9 +290,7 @@ namespace Qv2ray::base::objects QString email; int level; QString secret; - UserObject() : email("user@domain.com"), level(0), secret("") - { - } + UserObject() : email("user@domain.com"), level(0), secret(""){}; JSONSTRUCT_REGISTER(UserObject, F(email, level, secret)) }; QList users; @@ -352,12 +305,9 @@ namespace Qv2ray::base::objects QString user; QString pass; int level; - UserObject() : user(), pass(), level(0) - { - } + UserObject() : user(), pass(), level(0){}; JSONSTRUCT_REGISTER(UserObject, F(user, pass, level)) }; - QString address; int port; QList users; @@ -374,18 +324,14 @@ namespace Qv2ray::base::objects int alterId; QString security; int level; - UserObject() : id(""), alterId(64), security("auto"), level(0) - { - } + UserObject() : id(""), alterId(64), security("auto"), level(0){}; JSONSTRUCT_REGISTER(UserObject, F(id, alterId, security, level)) }; QString address; int port; QList users; - VMessServerObject() : address(""), port(0), users() - { - } + VMessServerObject() : address(""), port(0), users(){}; JSONSTRUCT_REGISTER(VMessServerObject, F(address, port, users)) }; // @@ -400,9 +346,7 @@ namespace Qv2ray::base::objects int level; int port; ShadowSocksServerObject() - : email("user@domain.com"), address("0.0.0.0"), method("aes-256-cfb"), password(""), ota(false), level(0), port(0) - { - } + : email("user@domain.com"), address("0.0.0.0"), method("aes-256-cfb"), password(""), ota(false), level(0), port(0){}; JSONSTRUCT_REGISTER(ShadowSocksServerObject, F(email, address, port, method, password, ota, level)) }; } // namespace protocol From deda5a918ec4897a904d0680cc1b4252537f8a99 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Tue, 28 Apr 2020 23:42:46 +0800 Subject: [PATCH 012/385] fix: fixed tProxy combobox translation settings --- makespec/BUILDVERSION | 2 +- src/ui/w_PreferencesWindow.ui | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 040c32b6..371665f9 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5335 \ No newline at end of file +5336 \ No newline at end of file diff --git a/src/ui/w_PreferencesWindow.ui b/src/ui/w_PreferencesWindow.ui index 0e202e08..d87ee367 100644 --- a/src/ui/w_PreferencesWindow.ui +++ b/src/ui/w_PreferencesWindow.ui @@ -1075,12 +1075,12 @@ Custom DNS Settings - redirect + redirect - tproxy + tproxy From 8be0eb14d5185121c67e3b9a391e37030852a99b Mon Sep 17 00:00:00 2001 From: DuckVador Date: Wed, 29 Apr 2020 19:00:38 +0800 Subject: [PATCH 013/385] Allow plugin to know if udp is enabled --- makespec/BUILDVERSION | 2 +- src/core/handler/KernelInstanceHandler.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 90e9994f..9a30556e 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5341 \ No newline at end of file +5342 \ No newline at end of file diff --git a/src/core/handler/KernelInstanceHandler.cpp b/src/core/handler/KernelInstanceHandler.cpp index 1f5b3ac3..620e0ce5 100644 --- a/src/core/handler/KernelInstanceHandler.cpp +++ b/src/core/handler/KernelInstanceHandler.cpp @@ -96,7 +96,7 @@ namespace Qv2ray::core::handlers pluginProcessedOutboundList.append({ originalOutboundTag, inTag, freedomTag }); pluginPort++; } - + pluginInboundPort.insert("enable_udp",GlobalConfig.inboundConfig.socksUDP?1:0); LOG(MODULE_CONNECTION, "Sending connection settings to kernel.") kernel->SetConnectionSettings(GlobalConfig.inboundConfig.listenip, pluginInboundPort, outbound["settings"].toObject()); } @@ -201,6 +201,7 @@ namespace Qv2ray::core::handlers connect(kernel, &QvPluginKernel::OnKernelStatsAvailable, this, &KernelInstanceHandler::OnStatsDataArrived_p); currentConnectionId = id; lastConnectionId = id; + pluginInboundPort.insert("enable_udp",GlobalConfig.inboundConfig.socksUDP?1:0); kernel->SetConnectionSettings(GlobalConfig.inboundConfig.listenip, pluginInboundPort, firstOutbound["settings"].toObject()); bool result = kernel->StartKernel(); if (result) From f237e623cfc93b364154a84b8f7d16d15047ca4e Mon Sep 17 00:00:00 2001 From: ymshenyu Date: Thu, 30 Apr 2020 13:31:42 +0800 Subject: [PATCH 014/385] fix: remove scheme for windows since it will break some application that get proxy information from system --- src/components/proxy/QvProxyConfigurator.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/proxy/QvProxyConfigurator.cpp b/src/components/proxy/QvProxyConfigurator.cpp index 3cb96544..2c24450b 100644 --- a/src/components/proxy/QvProxyConfigurator.cpp +++ b/src/components/proxy/QvProxyConfigurator.cpp @@ -212,7 +212,8 @@ namespace Qv2ray::components::proxy } #ifdef Q_OS_WIN - QString __a = (hasHTTP ? "http://" : "socks5://") + address + ":" + QSTRN(hasHTTP ? httpPort : socksPort); + const auto scheme = (hasHTTP ? "" : "socks5://"); + QString __a = scheme + address + ":" + QSTRN(hasHTTP ? httpPort : socksPort); LOG(MODULE_PROXY, "Windows proxy string: " + __a) auto proxyStrW = new WCHAR[__a.length() + 1]; From c3296dc6e048ac9afb679e471b8d3492e39d3080 Mon Sep 17 00:00:00 2001 From: ymshenyu Date: Thu, 30 Apr 2020 18:03:45 +0800 Subject: [PATCH 015/385] NSIS: kill process before uninstall/install Revert "we need more escape character" This reverts commit e11727eae66d6d506da8b6d1e5ae85fdd50f9932. update deployment.cmake we need more escape character Revert "fix typo?" This reverts commit 30a548cb6f8b3815be1b5c865aef4c94ff3904c1. fix typo? This should work? This should work fix typo kill process before uninstall/install --- cmake/deployment.cmake | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmake/deployment.cmake b/cmake/deployment.cmake index 40f960df..ea055e1f 100644 --- a/cmake/deployment.cmake +++ b/cmake/deployment.cmake @@ -43,6 +43,9 @@ if(MSVC) set(CPACK_NSIS_MUI_UNIICON "${CMAKE_SOURCE_DIR}/assets/icons/qv2ray.ico") set(CPACK_NSIS_DISPLAY_NAME "Qv2ray") set(CPACK_NSIS_PACKAGE_NAME "Qv2ray") + set(CPACK_NSIS_EXTRA_PREINSTALL_COMMANDS " + ExecWait \\\"taskkill /f /im qv2ray.exe\\\" + ") set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS " CreateShortCut \\\"$DESKTOP\\\\Qv2ray.lnk\\\" \\\"$INSTDIR\\\\qv2ray.exe\\\" CreateDirectory \\\"$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Qv2ray\\\" @@ -54,6 +57,7 @@ if(MSVC) WriteRegStr HKLM \\\"Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Uninstall\\\\qv2ray\\\" \\\"URLInfoAbout\\\" \\\"https://github.com/Qv2ray/Qv2ray\\\" ") set(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS " + ExecWait \\\"taskkill /f /im qv2ray.exe\\\" Delete \\\"$DESKTOP\\\\Qv2ray.lnk\\\" Delete \\\"$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Qv2ray\\\\Qv2ray.lnk\\\" RMDir \\\"$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Qv2ray\\\" From 908293242587ecc023cce8d969be5c770c1930c1 Mon Sep 17 00:00:00 2001 From: DuckSoft Date: Fri, 1 May 2020 19:39:18 +0800 Subject: [PATCH 016/385] updating translations --- translations/en_US.ts | 134 +++++++++++++++++++++++++----------------- translations/ja_JP.ts | 107 ++++++++++++++++++++++++++++----- translations/ru_RU.ts | 105 +++++++++++++++++++++++++++++---- translations/zh_CN.ts | 105 +++++++++++++++++++++++++++++---- 4 files changed, 356 insertions(+), 95 deletions(-) diff --git a/translations/en_US.ts b/translations/en_US.ts index 72e74f5a..9c04c324 100644 --- a/translations/en_US.ts +++ b/translations/en_US.ts @@ -569,10 +569,6 @@ Qv2ray - - Subscriptions - - Add @@ -851,6 +847,10 @@ Plugins + + Groups + + OutboundEditor @@ -1656,6 +1656,30 @@ But could damage your server if improperly used. Outbound Mark + + Sniffing + + + + Use V2ray DNS for Freedom Outbound + + + + Bypass Bittorrent Protocol + + + + Note + + + + To recognize the protocol of a connection, one must enable sniffing option in inbound proxy. + + + + tproxy inbound's sniffing is enabled by default. + + QObject @@ -2909,6 +2933,57 @@ Maybe you have downloaded the wrong core? + + w_GroupManager + + Group Editor + + + + Group List + + + + Add Subscription + + + + Remove Subscription + + + + Group Details + + + + Group Name + + + + Subscription Address + + + + Update Interval + + + + Days + + + + Last Updated + + + + Connection List + + + + Update Subscription Data + + + w_PluginManager @@ -2976,55 +3051,4 @@ Maybe you have downloaded the wrong core? - - w_SubscribeEditor - - SubscribeEditor - - - - Subscription List - - - - Add Subscription - - - - Remove Subscription - - - - Subscription Details - - - - Subscription Name - - - - Subscription Address - - - - Update Interval - - - - Days - - - - Last Updated - - - - Connection List - - - - Update Subscription Data - - - diff --git a/translations/ja_JP.ts b/translations/ja_JP.ts index d266e7fa..7814bea0 100644 --- a/translations/ja_JP.ts +++ b/translations/ja_JP.ts @@ -728,7 +728,7 @@ Subscriptions - サブスクリプション + サブスクリプション Stopped @@ -1310,6 +1310,10 @@ Plugins プラグイン + + Groups + グループ + OutboundEditor @@ -2359,6 +2363,30 @@ But could damage your server if improperly used. Outbound Mark アウトバウンドマーク + + Sniffing + フィルター + + + Use V2ray DNS for Freedom Outbound + フリーダムアウトバウンドにはV2Ray DNSを使用 + + + Bypass Bittorrent Protocol + ビットトレントプロトコルをバイパス + + + Note + ご注意 + + + To recognize the protocol of a connection, one must enable sniffing option in inbound proxy. + プロトコルを認識するためには、受信側のプロキシでスニッフィングオプションを有効にする必要があります。 + + + tproxy inbound's sniffing is enabled by default. + tProxyインバウンドのスニッフィングはデフォルトで有効になっています。 + QObject @@ -2396,7 +2424,7 @@ But could damage your server if improperly used. seems like a v1 vmess, we don't support it - V1のVMessリンクの可能性があります,現在非対応です。 + V1のVMessリンクの可能性があります,現在非対応です Warning @@ -3805,6 +3833,57 @@ Maybe you have downloaded the wrong core? /s + + w_GroupManager + + Group Editor + グループ編集 + + + Group List + グループ一覧 + + + Add Subscription + サブスクリプションを追加 + + + Remove Subscription + サブスクリプションを削除 + + + Group Details + 詳細 + + + Group Name + グループ名 + + + Subscription Address + アドレス + + + Update Interval + 更新間隔 + + + Days + 日ごと + + + Last Updated + 最終更新 + + + Connection List + 項目リスト + + + Update Subscription Data + サブスクリプションデータの更新 + + w_PluginManager @@ -3876,11 +3955,11 @@ Maybe you have downloaded the wrong core? w_SubscribeEditor SubscribeEditor - SubscribeEditor + SubscribeEditor Subscription List - サブスクリプション一覧 + サブスクリプション一覧 A @@ -3888,11 +3967,11 @@ Maybe you have downloaded the wrong core? Add Subscription - サブスクリプションを追加 + サブスクリプションを追加 Remove Subscription - サブスクリプションを削除 + サブスクリプションを削除 D @@ -3900,31 +3979,31 @@ Maybe you have downloaded the wrong core? Subscription Details - サブスクリプションの詳細 + サブスクリプションの詳細 Subscription Name - 名称 + 名称 Subscription Address - アドレス + アドレス Update Interval - 更新間隔 + 更新間隔 Days - 日ごと + 日ごと Last Updated - 最終更新 + 最終更新 Connection List - 項目リスト + 項目リスト Update Subscription With System Proxy @@ -3932,7 +4011,7 @@ Maybe you have downloaded the wrong core? Update Subscription Data - サブスクリプションデータの更新 + サブスクリプションデータの更新 diff --git a/translations/ru_RU.ts b/translations/ru_RU.ts index ff28823d..f9b036a4 100644 --- a/translations/ru_RU.ts +++ b/translations/ru_RU.ts @@ -712,7 +712,7 @@ Subscriptions - Подписки + Подписки Stopped @@ -1290,6 +1290,10 @@ Plugins + + Groups + + OutboundEditor @@ -2263,6 +2267,30 @@ But could damage your server if improperly used. Outbound Mark + + Sniffing + + + + Use V2ray DNS for Freedom Outbound + + + + Bypass Bittorrent Protocol + + + + Note + + + + To recognize the protocol of a connection, one must enable sniffing option in inbound proxy. + + + + tproxy inbound's sniffing is enabled by default. + + QObject @@ -3646,6 +3674,57 @@ Maybe you have downloaded the wrong core? /s + + w_GroupManager + + Group Editor + + + + Group List + + + + Add Subscription + Добавить подписку + + + Remove Subscription + Удалить подписку + + + Group Details + + + + Group Name + + + + Subscription Address + Адрес подписки + + + Update Interval + Интервалы обновления + + + Days + Дней + + + Last Updated + Последнее обновление + + + Connection List + Список подключений + + + Update Subscription Data + Обновить данные подписки + + w_PluginManager @@ -3717,11 +3796,11 @@ Maybe you have downloaded the wrong core? w_SubscribeEditor SubscribeEditor - Редактор подписки + Редактор подписки Subscription List - Список подписок + Список подписок A @@ -3729,11 +3808,11 @@ Maybe you have downloaded the wrong core? Add Subscription - Добавить подписку + Добавить подписку Remove Subscription - Удалить подписку + Удалить подписку D @@ -3741,31 +3820,31 @@ Maybe you have downloaded the wrong core? Subscription Details - Детали подписки + Детали подписки Subscription Name - Название подписки + Название подписки Subscription Address - Адрес подписки + Адрес подписки Update Interval - Интервалы обновления + Интервалы обновления Days - Дней + Дней Last Updated - Последнее обновление + Последнее обновление Connection List - Список подключений + Список подключений Update Subscription With System Proxy @@ -3773,7 +3852,7 @@ Maybe you have downloaded the wrong core? Update Subscription Data - Обновить данные подписки + Обновить данные подписки diff --git a/translations/zh_CN.ts b/translations/zh_CN.ts index f88f9481..8b6137d3 100644 --- a/translations/zh_CN.ts +++ b/translations/zh_CN.ts @@ -614,7 +614,7 @@ Subscriptions - 订阅 + 订阅 Add @@ -948,6 +948,10 @@ Plugins 插件 + + Groups + 分组 + OutboundEditor @@ -1989,6 +1993,30 @@ But could damage your server if improperly used. Outbound Mark 出站 Mark + + Sniffing + 嗅探 + + + Use V2ray DNS for Freedom Outbound + 为自由出站使用 V2Ray DNS + + + Bypass Bittorrent Protocol + 绕过 BitTorrent 协议 + + + Note + 注意 + + + To recognize the protocol of a connection, one must enable sniffing option in inbound proxy. + 要识别网络连接的协议,必须要启用入站代理中的嗅探选项。 + + + tproxy inbound's sniffing is enabled by default. + tProxy 入站的嗅探选项默认开启。 + QObject @@ -3347,6 +3375,57 @@ Maybe you have downloaded the wrong core? /s + + w_GroupManager + + Group Editor + 组编辑器 + + + Group List + 组列表 + + + Add Subscription + 添加订阅 + + + Remove Subscription + 删除订阅 + + + Group Details + 分组细节 + + + Group Name + 分组名称 + + + Subscription Address + 订阅地址 + + + Update Interval + 更新间隔 + + + Days + + + + Last Updated + 最后更新 + + + Connection List + 连接列表 + + + Update Subscription Data + 更新订阅数据 + + w_PluginManager @@ -3418,47 +3497,47 @@ Maybe you have downloaded the wrong core? w_SubscribeEditor SubscribeEditor - 订阅编辑器 + 订阅编辑器 Subscription List - 订阅列表 + 订阅列表 Add Subscription - 添加订阅 + 添加订阅 Remove Subscription - 删除订阅 + 删除订阅 Subscription Details - 订阅详情 + 订阅详情 Subscription Name - 订阅名称 + 订阅名称 Subscription Address - 订阅地址 + 订阅地址 Update Interval - 更新间隔 + 更新间隔 Days - + Last Updated - 最后更新 + 最后更新 Connection List - 连接列表 + 连接列表 Update Subscription With System Proxy @@ -3466,7 +3545,7 @@ Maybe you have downloaded the wrong core? Update Subscription Data - 更新订阅数据 + 更新订阅数据 From a5d9c3d2532847b301bbd790fff03fd8d3c2ce6d Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Fri, 1 May 2020 20:22:37 +0800 Subject: [PATCH 017/385] add: added Group Manager UI --- makespec/BUILDVERSION | 2 +- src/ui/w_GroupManager.ui | 325 +++++++++++++++++++++++---------------- src/ui/w_MainWindow.ui | 19 ++- 3 files changed, 203 insertions(+), 143 deletions(-) diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 9a30556e..0cf9f71e 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5342 \ No newline at end of file +5343 diff --git a/src/ui/w_GroupManager.ui b/src/ui/w_GroupManager.ui index afefa712..3b4d7ea3 100644 --- a/src/ui/w_GroupManager.ui +++ b/src/ui/w_GroupManager.ui @@ -6,8 +6,8 @@ 0 0 - 911 - 629 + 900 + 617 @@ -25,18 +25,41 @@ true - - - - - Qt::Horizontal - - - QDialogButtonBox::Ok + + + + + Group Info + + + + + Group Name + + + + + + + + + + Created At + + + + + + + DATE + + + + - + Group List @@ -139,126 +162,167 @@ - - - - false + + + + Qt::Horizontal - - Group Details + + QDialogButtonBox::Ok - - - - - - - Group Name - - - - - - - - - - Subscription Address - - - - - - - - - - Update Interval - - - - - - - - - 365.000000000000000 - - - 0.500000000000000 - - - 5.000000000000000 - - - - - - - Days - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - Last Updated - - - - - - - - - - - - - - - - Connection List - - - - - - - QAbstractItemView::NoEditTriggers - - - QListView::Adjust - - - - - - - - - Update Subscription Data - - - - - - + + + + + + 0 + + + + Connections + + + + + + New Connection + + + + + + + Remove Selection + + + + + + + QAbstractItemView::NoEditTriggers + + + QListView::Adjust + + + + + + + + Subscription + + + + + + Subscription Address + + + + + + + + + + Update Interval + + + + + + + + + + + + + + Last Updated + + + + + + + + + 365.000000000000000 + + + 0.500000000000000 + + + 5.000000000000000 + + + + + + + Days + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Update Subscription Data + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + General Settings + + + + + Inbound Settings + + + + + Routing + + + + + Connection Settings + + @@ -268,10 +332,7 @@ addSubsButton removeSubsButton subAddrTxt - groupNameTxt updateIntervalSB - connectionsList - updateButton diff --git a/src/ui/w_MainWindow.ui b/src/ui/w_MainWindow.ui index f99dc46a..99354834 100644 --- a/src/ui/w_MainWindow.ui +++ b/src/ui/w_MainWindow.ui @@ -22,17 +22,10 @@ - + 5 - - - - Groups - - - @@ -194,7 +187,7 @@ Import Connection - Add + Add Connection @@ -204,6 +197,13 @@ + + + + Groups + + + @@ -546,7 +546,6 @@ - subsButton connectionListWidget importConfigButton From 2bd9cc1a8779baac58a6fab3d7ef4374f1113e53 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Fri, 1 May 2020 22:39:15 +0800 Subject: [PATCH 018/385] add: basic group manager without subscription feature --- CMakeLists.txt | 6 ++ makespec/BUILDVERSION | 2 +- src/ui/w_GroupManager.cpp | 89 ++++++++++++--------- src/ui/w_GroupManager.hpp | 20 +++-- src/ui/w_GroupManager.ui | 77 +++++++----------- src/ui/w_ImportConfig.cpp | 2 +- src/ui/w_MainWindow.cpp | 2 +- src/ui/w_PreferencesWindow.ui | 2 +- src/ui/widgets/ConnectionSettingsWidget.cpp | 16 ++++ src/ui/widgets/ConnectionSettingsWidget.hpp | 16 ++++ src/ui/widgets/ConnectionSettingsWidget.ui | 21 +++++ src/ui/widgets/InboundSettingsWidget.cpp | 26 ++++++ src/ui/widgets/InboundSettingsWidget.hpp | 18 +++++ src/ui/widgets/InboundSettingsWidget.ui | 21 +++++ 14 files changed, 220 insertions(+), 98 deletions(-) create mode 100644 src/ui/widgets/ConnectionSettingsWidget.cpp create mode 100644 src/ui/widgets/ConnectionSettingsWidget.hpp create mode 100644 src/ui/widgets/ConnectionSettingsWidget.ui create mode 100644 src/ui/widgets/InboundSettingsWidget.cpp create mode 100644 src/ui/widgets/InboundSettingsWidget.hpp create mode 100644 src/ui/widgets/InboundSettingsWidget.ui diff --git a/CMakeLists.txt b/CMakeLists.txt index 5dbca442..8f0302ee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -204,6 +204,8 @@ set(QV2RAY_SOURCES src/ui/widgets/QvAutoCompleteTextEdit.cpp src/ui/widgets/StreamSettingsWidget.cpp src/ui/widgets/RouteSettingsMatrix.cpp + src/ui/widgets/InboundSettingsWidget.cpp + src/ui/widgets/ConnectionSettingsWidget.cpp src/ui/w_ImportConfig.cpp src/ui/w_MainWindow.cpp src/ui/w_MainWindow_extra.cpp @@ -222,6 +224,8 @@ set(QV2RAY_SOURCES src/ui/widgets/ConnectionInfoWidget.ui src/ui/widgets/ConnectionItemWidget.ui src/ui/widgets/RouteSettingsMatrix.ui + src/ui/widgets/InboundSettingsWidget.ui + src/ui/widgets/ConnectionSettingsWidget.ui src/ui/w_MainWindow.ui src/ui/w_PreferencesWindow.ui src/ui/w_PluginManager.ui @@ -285,6 +289,8 @@ set(QV2RAY_SOURCES src/ui/widgets/QvAutoCompleteTextEdit.hpp src/ui/widgets/StreamSettingsWidget.hpp src/ui/widgets/RouteSettingsMatrix.hpp + src/ui/widgets/InboundSettingsWidget.hpp + src/ui/widgets/ConnectionSettingsWidget.hpp src/ui/w_ImportConfig.hpp src/ui/w_MainWindow.hpp src/ui/w_PreferencesWindow.hpp diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 0cf9f71e..34f9d9a3 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5343 +5348 \ No newline at end of file diff --git a/src/ui/w_GroupManager.cpp b/src/ui/w_GroupManager.cpp index 21864a9d..74eae0fb 100644 --- a/src/ui/w_GroupManager.cpp +++ b/src/ui/w_GroupManager.cpp @@ -4,106 +4,113 @@ #include "core/handler/ConfigHandler.hpp" #include "core/settings/SettingsBackend.hpp" -SubscriptionEditor::SubscriptionEditor(QWidget *parent) : QDialog(parent) +GroupManager::GroupManager(QWidget *parent) : QDialog(parent) { setupUi(this); - QvMessageBusConnect(SubscriptionEditor); + QvMessageBusConnect(GroupManager); UpdateColorScheme(); - for (auto subs : ConnectionManager->Subscriptions()) + connectionListRCMenu->addMenu(connectionListRCMenu_CopyToMenu); + connectionListRCMenu->addMenu(connectionListRCMenu_MoveToMenu); + for (auto group : ConnectionManager->AllGroups()) { - groupList->addTopLevelItem(new QTreeWidgetItem(QStringList{ GetDisplayName(subs), subs.toString() })); + auto item = new QListWidgetItem(GetDisplayName(group)); + item->setData(Qt::UserRole, group.toString()); + groupList->addItem(item); } - if (groupList->topLevelItemCount() > 0) + if (groupList->count() > 0) { - groupList->setCurrentItem(groupList->topLevelItem(0)); + groupList->setCurrentItem(groupList->item(0)); } } -void SubscriptionEditor::UpdateColorScheme() +void GroupManager::UpdateColorScheme() { - addSubsButton->setIcon(QICON_R("add.png")); - removeSubsButton->setIcon(QICON_R("delete.png")); + addGroupButton->setIcon(QICON_R("add.png")); + removeGroupButton->setIcon(QICON_R("delete.png")); } -QvMessageBusSlotImpl(SubscriptionEditor) +QvMessageBusSlotImpl(GroupManager) { switch (msg) { - MBShowDefaultImpl MBHideDefaultImpl MBRetranslateDefaultImpl MBUpdateColorSchemeDefaultImpl + MBShowDefaultImpl; + MBHideDefaultImpl; + MBRetranslateDefaultImpl; + MBUpdateColorSchemeDefaultImpl } } -tuple SubscriptionEditor::GetSelectedConfig() +tuple GroupManager::GetSelectedConfig() { return { GetDisplayName(currentConnectionId), ConnectionManager->GetConnectionRoot(currentConnectionId) }; } -SubscriptionEditor::~SubscriptionEditor() +GroupManager::~GroupManager() { } -void SubscriptionEditor::on_addSubsButton_clicked() +void GroupManager::on_addGroupButton_clicked() { auto const key = QSTRN(QTime::currentTime().msecsSinceStartOfDay()); auto id = ConnectionManager->CreateGroup(key, true); // - groupList->addTopLevelItem(new QTreeWidgetItem(QStringList{ key, id.toString() })); + auto item = new QListWidgetItem(key); + item->setData(Qt::UserRole, id.toString()); + groupList->addItem(item); } -void SubscriptionEditor::on_updateButton_clicked() +void GroupManager::on_updateButton_clicked() { if (QvMessageBoxAsk(this, tr("Reload Subscription"), tr("Would you like to reload the subscription?")) == QMessageBox::Yes) { this->setEnabled(false); ConnectionManager->UpdateSubscription(currentSubId); // this->setEnabled(true); - on_groupList_itemClicked(groupList->currentItem(), 0); + on_groupList_itemClicked(groupList->currentItem()); } } -void SubscriptionEditor::on_removeSubsButton_clicked() +void GroupManager::on_removeGroupButton_clicked() { if (QvMessageBoxAsk(this, tr("Deleting a subscription"), tr("All connections will be moved to default group, do you want to continue?")) == QMessageBox::Yes) { ConnectionManager->DeleteGroup(currentSubId); // auto item = groupList->currentItem(); - groupList->removeItemWidget(item, 0); + groupList->removeItemWidget(item); delete item; - if (groupList->topLevelItemCount() > 0) + if (groupList->count() > 0) { - groupList->setCurrentItem(groupList->topLevelItem(0)); - on_groupList_itemClicked(groupList->topLevelItem(0), 0); + groupList->setCurrentItem(groupList->item(0)); + on_groupList_itemClicked(groupList->item(0)); } else { - groupBox_2->setEnabled(false); + groupInfoGroupBox->setEnabled(false); } } } -void SubscriptionEditor::on_buttonBox_accepted() +void GroupManager::on_buttonBox_accepted() { // Nothing? } -void SubscriptionEditor::on_groupList_itemSelectionChanged() +void GroupManager::on_groupList_itemSelectionChanged() { - groupBox_2->setEnabled(groupList->selectedItems().count() > 0); + groupInfoGroupBox->setEnabled(groupList->selectedItems().count() > 0); } -void SubscriptionEditor::on_groupList_itemClicked(QTreeWidgetItem *item, int column) +void GroupManager::on_groupList_itemClicked(QListWidgetItem *item) { - Q_UNUSED(column) if (item == nullptr) { return; } - // - groupBox_2->setEnabled(true); - currentSubId = GroupId(item->text(1)); + groupInfoGroupBox->setEnabled(true); + currentSubId = GroupId(item->data(Qt::UserRole).toString()); // groupNameTxt->setText(GetDisplayName(currentSubId)); auto const [addr, lastUpdated, updateInterval] = ConnectionManager->GetSubscriptionData(currentSubId); @@ -119,31 +126,31 @@ void SubscriptionEditor::on_groupList_itemClicked(QTreeWidgetItem *item, int col } } -void SubscriptionEditor::on_groupList_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous) +void GroupManager::on_groupList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous) { Q_UNUSED(previous) - on_groupList_itemClicked(current, 0); + on_groupList_itemClicked(current); } -void SubscriptionEditor::on_subNameTxt_textEdited(const QString &arg1) +void GroupManager::on_subNameTxt_textEdited(const QString &arg1) { - groupList->selectedItems().first()->setText(0, arg1); + groupList->selectedItems().first()->setText(arg1); ConnectionManager->RenameGroup(currentSubId, arg1.trimmed()); } -void SubscriptionEditor::on_subAddrTxt_textEdited(const QString &arg1) +void GroupManager::on_subAddrTxt_textEdited(const QString &arg1) { auto newUpdateInterval = updateIntervalSB->value(); ConnectionManager->SetSubscriptionData(currentSubId, arg1, newUpdateInterval); } -void SubscriptionEditor::on_updateIntervalSB_valueChanged(double arg1) +void GroupManager::on_updateIntervalSB_valueChanged(double arg1) { auto newAddress = subAddrTxt->text().trimmed(); ConnectionManager->SetSubscriptionData(currentSubId, newAddress, arg1); } -void SubscriptionEditor::on_connectionsList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous) +void GroupManager::on_connectionsList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous) { Q_UNUSED(previous) if (current != nullptr) @@ -151,3 +158,9 @@ void SubscriptionEditor::on_connectionsList_currentItemChanged(QListWidgetItem * currentConnectionId = ConnectionManager->GetConnectionIdByDisplayName(current->text(), currentSubId); } } + +void GroupManager::on_connectionsList_customContextMenuRequested(const QPoint &pos) +{ + Q_UNUSED(pos) + connectionListRCMenu->popup(QCursor::pos()); +} diff --git a/src/ui/w_GroupManager.hpp b/src/ui/w_GroupManager.hpp index a29cdfc7..debdf6c0 100644 --- a/src/ui/w_GroupManager.hpp +++ b/src/ui/w_GroupManager.hpp @@ -6,35 +6,36 @@ #include "ui_w_GroupManager.h" #include +#include -class SubscriptionEditor +class GroupManager : public QDialog , private Ui::w_GroupManager { Q_OBJECT public: - explicit SubscriptionEditor(QWidget *parent = nullptr); - ~SubscriptionEditor(); + explicit GroupManager(QWidget *parent = nullptr); + ~GroupManager(); tuple GetSelectedConfig(); private: QvMessageBusSlotDecl; private slots: - void on_addSubsButton_clicked(); + void on_addGroupButton_clicked(); void on_updateButton_clicked(); - void on_removeSubsButton_clicked(); + void on_removeGroupButton_clicked(); void on_buttonBox_accepted(); void on_groupList_itemSelectionChanged(); - void on_groupList_itemClicked(QTreeWidgetItem *item, int column); + void on_groupList_itemClicked(QListWidgetItem *item); - void on_groupList_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous); + void on_groupList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous); void on_subNameTxt_textEdited(const QString &arg1); @@ -44,7 +45,12 @@ class SubscriptionEditor void on_connectionsList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous); + void on_connectionsList_customContextMenuRequested(const QPoint &pos); + private: + QMenu *connectionListRCMenu = new QMenu(this); + QMenu *connectionListRCMenu_CopyToMenu = new QMenu(tr("Copy to...")); + QMenu *connectionListRCMenu_MoveToMenu = new QMenu(tr("Move to...")); void UpdateColorScheme(); bool isUpdateInProgress = false; GroupId currentSubId = NullGroupId; diff --git a/src/ui/w_GroupManager.ui b/src/ui/w_GroupManager.ui index 3b4d7ea3..4bb0bf74 100644 --- a/src/ui/w_GroupManager.ui +++ b/src/ui/w_GroupManager.ui @@ -27,7 +27,7 @@ - + Group Info @@ -66,27 +66,13 @@ - + QAbstractItemView::SingleSelection QAbstractItemView::SelectRows - - false - - - false - - - true - - - - 1 - - @@ -105,7 +91,7 @@ - + 0 @@ -125,7 +111,7 @@ - + 0 @@ -198,9 +184,15 @@ + + Qt::CustomContextMenu + QAbstractItemView::NoEditTriggers + + QAbstractItemView::MultiSelection + QListView::Adjust @@ -210,41 +202,41 @@ - Subscription + Subscription Settings - + Subscription Address - + - + Update Interval - + - + Last Updated - + @@ -281,14 +273,14 @@ - + Update Subscription Data - + Qt::Vertical @@ -301,36 +293,23 @@ + + + + This group is a subscription + + + - - - General Settings - - - - - Inbound Settings - - - - - Routing - - - - - Connection Settings - - groupList - addSubsButton - removeSubsButton + addGroupButton + removeGroupButton subAddrTxt updateIntervalSB diff --git a/src/ui/w_ImportConfig.cpp b/src/ui/w_ImportConfig.cpp index 19e13618..5df57199 100644 --- a/src/ui/w_ImportConfig.cpp +++ b/src/ui/w_ImportConfig.cpp @@ -288,7 +288,7 @@ void ImportConfigWindow::on_cancelImportBtn_clicked() void ImportConfigWindow::on_subscriptionButton_clicked() { hide(); - SubscriptionEditor w(this); + GroupManager w(this); w.exec(); auto importToComplex = !keepImportedInboundCheckBox->isEnabled(); connections.clear(); diff --git a/src/ui/w_MainWindow.cpp b/src/ui/w_MainWindow.cpp index e1f604f0..b11c6ab6 100644 --- a/src/ui/w_MainWindow.cpp +++ b/src/ui/w_MainWindow.cpp @@ -604,7 +604,7 @@ void MainWindow::on_action_RCM_EditAsComplex_triggered() void MainWindow::on_subsButton_clicked() { - SubscriptionEditor().exec(); + GroupManager().exec(); } void MainWindow::on_connectionListWidget_itemDoubleClicked(QTreeWidgetItem *item, int column) diff --git a/src/ui/w_PreferencesWindow.ui b/src/ui/w_PreferencesWindow.ui index 8e50a231..f00a6b9d 100644 --- a/src/ui/w_PreferencesWindow.ui +++ b/src/ui/w_PreferencesWindow.ui @@ -33,7 +33,7 @@ - 0 + 3 diff --git a/src/ui/widgets/ConnectionSettingsWidget.cpp b/src/ui/widgets/ConnectionSettingsWidget.cpp new file mode 100644 index 00000000..98dfe1f0 --- /dev/null +++ b/src/ui/widgets/ConnectionSettingsWidget.cpp @@ -0,0 +1,16 @@ +#include "ConnectionSettingsWidget.hpp" + +ConnectionSettingsWidget::ConnectionSettingsWidget(QWidget *parent) : QWidget(parent) +{ + setupUi(this); +} + +void ConnectionSettingsWidget::changeEvent(QEvent *e) +{ + QWidget::changeEvent(e); + switch (e->type()) + { + case QEvent::LanguageChange: retranslateUi(this); break; + default: break; + } +} diff --git a/src/ui/widgets/ConnectionSettingsWidget.hpp b/src/ui/widgets/ConnectionSettingsWidget.hpp new file mode 100644 index 00000000..32bb1c3c --- /dev/null +++ b/src/ui/widgets/ConnectionSettingsWidget.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include "ui_ConnectionSettingsWidget.h" + +class ConnectionSettingsWidget + : public QWidget + , private Ui::ConnectionSettingsWidget +{ + Q_OBJECT + + public: + explicit ConnectionSettingsWidget(QWidget *parent = nullptr); + + protected: + void changeEvent(QEvent *e); +}; diff --git a/src/ui/widgets/ConnectionSettingsWidget.ui b/src/ui/widgets/ConnectionSettingsWidget.ui new file mode 100644 index 00000000..bb32e6ad --- /dev/null +++ b/src/ui/widgets/ConnectionSettingsWidget.ui @@ -0,0 +1,21 @@ + + + + + ConnectionSettingsWidget + + + + 0 + 0 + 400 + 300 + + + + Form + + + + + diff --git a/src/ui/widgets/InboundSettingsWidget.cpp b/src/ui/widgets/InboundSettingsWidget.cpp new file mode 100644 index 00000000..2810577f --- /dev/null +++ b/src/ui/widgets/InboundSettingsWidget.cpp @@ -0,0 +1,26 @@ +#include "InboundSettingsWidget.hpp" +InboundSettingsWidget::InboundSettingsWidget(QWidget *parent) : QWidget(parent) +{ + setupUi(this); +} + +QvMessageBusSlotImpl(InboundSettingsWidget) +{ + switch (msg) + { + MBRetranslateDefaultImpl; + case HIDE_WINDOWS: + case SHOW_WINDOWS: break; + default: break; + } +} + +void InboundSettingsWidget::changeEvent(QEvent *e) +{ + QWidget::changeEvent(e); + switch (e->type()) + { + case QEvent::LanguageChange: retranslateUi(this); break; + default: break; + } +} diff --git a/src/ui/widgets/InboundSettingsWidget.hpp b/src/ui/widgets/InboundSettingsWidget.hpp new file mode 100644 index 00000000..1f1c1241 --- /dev/null +++ b/src/ui/widgets/InboundSettingsWidget.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include "ui/messaging/QvMessageBus.hpp" +#include "ui_InboundSettingsWidget.h" + +class InboundSettingsWidget + : public QWidget + , private Ui::InboundSettingsWidget +{ + Q_OBJECT + QvMessageBusSlotDecl; + + public: + explicit InboundSettingsWidget(QWidget *parent = nullptr); + + protected: + void changeEvent(QEvent *e); +}; diff --git a/src/ui/widgets/InboundSettingsWidget.ui b/src/ui/widgets/InboundSettingsWidget.ui new file mode 100644 index 00000000..cb8d57f7 --- /dev/null +++ b/src/ui/widgets/InboundSettingsWidget.ui @@ -0,0 +1,21 @@ + + + + + InboundSettingsWidget + + + + 0 + 0 + 400 + 300 + + + + Form + + + + + From a351e95df9cd566e5fbed414042528df01df7725 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Sat, 2 May 2020 00:50:05 +0800 Subject: [PATCH 019/385] add: test support connection copy/movement --- makespec/BUILDVERSION | 2 +- src/core/handler/ConfigHandler.cpp | 2 +- src/ui/w_GroupManager.cpp | 101 +++++++++++++++++++++++++---- src/ui/w_GroupManager.hpp | 6 +- src/ui/w_GroupManager.ui | 22 +++---- 5 files changed, 105 insertions(+), 28 deletions(-) diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 34f9d9a3..638f0142 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5348 \ No newline at end of file +5349 \ No newline at end of file diff --git a/src/core/handler/ConfigHandler.cpp b/src/core/handler/ConfigHandler.cpp index 04335a8a..a01c86f6 100644 --- a/src/core/handler/ConfigHandler.cpp +++ b/src/core/handler/ConfigHandler.cpp @@ -282,7 +282,7 @@ namespace Qv2ray::core::handlers QDir().mkpath(newDir); } // - if (!QFile(oldPath).rename(newPath)) + if (!QFile::rename(oldPath, newPath)) { LOG(MODULE_FILEIO, "Cannot rename") } diff --git a/src/ui/w_GroupManager.cpp b/src/ui/w_GroupManager.cpp index 74eae0fb..1e63f23b 100644 --- a/src/ui/w_GroupManager.cpp +++ b/src/ui/w_GroupManager.cpp @@ -4,6 +4,8 @@ #include "core/handler/ConfigHandler.hpp" #include "core/settings/SettingsBackend.hpp" +#include + GroupManager::GroupManager(QWidget *parent) : QDialog(parent) { setupUi(this); @@ -11,6 +13,19 @@ GroupManager::GroupManager(QWidget *parent) : QDialog(parent) UpdateColorScheme(); connectionListRCMenu->addMenu(connectionListRCMenu_CopyToMenu); connectionListRCMenu->addMenu(connectionListRCMenu_MoveToMenu); + // + connect(ConnectionManager, &QvConfigHandler::OnConnectionGroupChanged, // + [&](const ConnectionId &, const GroupId &, const GroupId &) { // + this->loadConnectionList(currentGroupId); // + }); + // + connect(ConnectionManager, &QvConfigHandler::OnConnectionCreated, // + [&](const ConnectionId &id, const QString &) { // + const auto groupId = GetConnectionGroupId(id); // + if (groupId == currentGroupId) // + this->loadConnectionList(groupId); // + }); // + // for (auto group : ConnectionManager->AllGroups()) { auto item = new QListWidgetItem(GetDisplayName(group)); @@ -21,6 +36,68 @@ GroupManager::GroupManager(QWidget *parent) : QDialog(parent) { groupList->setCurrentItem(groupList->item(0)); } + ReloadGroupAction(); +} + +void GroupManager::ReloadGroupAction() +{ + connectionListRCMenu_CopyToMenu->clear(); + connectionListRCMenu_MoveToMenu->clear(); + for (const auto &group : ConnectionManager->AllGroups()) + { + auto cpAction = new QAction(GetDisplayName(group), connectionListRCMenu_CopyToMenu); + auto mvAction = new QAction(GetDisplayName(group), connectionListRCMenu_MoveToMenu); + // + cpAction->setData(group.toString()); + mvAction->setData(group.toString()); + // + connectionListRCMenu_CopyToMenu->addAction(cpAction); + connectionListRCMenu_MoveToMenu->addAction(mvAction); + // + connect(cpAction, &QAction::triggered, this, &GroupManager::onRCMActionTriggered_Copy); + connect(mvAction, &QAction::triggered, this, &GroupManager::onRCMActionTriggered_Move); + } +} + +void GroupManager::loadConnectionList(const GroupId &group) +{ + connectionsList->clear(); + for (auto conn : ConnectionManager->Connections(group)) + { + auto item = new QListWidgetItem(GetDisplayName(conn), connectionsList); + item->setData(Qt::UserRole, conn.toString()); + connectionsList->addItem(item); // + } +} + +void GroupManager::onRCMActionTriggered_Copy() +{ + const auto _sender = qobject_cast(sender()); + const GroupId groupId{ _sender->data().toString() }; + // + const auto &connectionList = connectionsList->selectedItems(); + for (const auto &connItem : connectionList) + { + const auto &connectionId = ConnectionId(connItem->data(Qt::UserRole).toString()); + ConnectionManager->CreateConnection(GetDisplayName(connectionId), groupId, ConnectionManager->GetConnectionRoot(connectionId), true); + } +} +void GroupManager::onRCMActionTriggered_Move() +{ + const auto _sender = qobject_cast(sender()); + const GroupId groupId{ _sender->data().toString() }; + // + const auto &connectionList = connectionsList->selectedItems(); + for (const auto &connItem : connectionList) + { + if (!connItem) + { + LOG(MODULE_UI, "Invalid?") + continue; + } + const auto &id = connItem->data(Qt::UserRole); + ConnectionManager->MoveConnectionGroup(ConnectionId(id.toString()), groupId); + } } void GroupManager::UpdateColorScheme() @@ -64,7 +141,7 @@ void GroupManager::on_updateButton_clicked() if (QvMessageBoxAsk(this, tr("Reload Subscription"), tr("Would you like to reload the subscription?")) == QMessageBox::Yes) { this->setEnabled(false); - ConnectionManager->UpdateSubscription(currentSubId); // + ConnectionManager->UpdateSubscription(currentGroupId); this->setEnabled(true); on_groupList_itemClicked(groupList->currentItem()); } @@ -75,7 +152,7 @@ void GroupManager::on_removeGroupButton_clicked() if (QvMessageBoxAsk(this, tr("Deleting a subscription"), tr("All connections will be moved to default group, do you want to continue?")) == QMessageBox::Yes) { - ConnectionManager->DeleteGroup(currentSubId); // + ConnectionManager->DeleteGroup(currentGroupId); // auto item = groupList->currentItem(); groupList->removeItemWidget(item); delete item; @@ -110,20 +187,16 @@ void GroupManager::on_groupList_itemClicked(QListWidgetItem *item) } groupInfoGroupBox->setEnabled(true); - currentSubId = GroupId(item->data(Qt::UserRole).toString()); + currentGroupId = GroupId(item->data(Qt::UserRole).toString()); // - groupNameTxt->setText(GetDisplayName(currentSubId)); - auto const [addr, lastUpdated, updateInterval] = ConnectionManager->GetSubscriptionData(currentSubId); + groupNameTxt->setText(GetDisplayName(currentGroupId)); + auto const [addr, lastUpdated, updateInterval] = ConnectionManager->GetSubscriptionData(currentGroupId); subAddrTxt->setText(addr); lastUpdatedLabel->setText(timeToString(lastUpdated)); updateIntervalSB->setValue(updateInterval); // connectionsList->clear(); - - for (auto conn : ConnectionManager->Connections(currentSubId)) - { - connectionsList->addItem(GetDisplayName(conn)); // - } + loadConnectionList(currentGroupId); } void GroupManager::on_groupList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous) @@ -135,19 +208,19 @@ void GroupManager::on_groupList_currentItemChanged(QListWidgetItem *current, QLi void GroupManager::on_subNameTxt_textEdited(const QString &arg1) { groupList->selectedItems().first()->setText(arg1); - ConnectionManager->RenameGroup(currentSubId, arg1.trimmed()); + ConnectionManager->RenameGroup(currentGroupId, arg1.trimmed()); } void GroupManager::on_subAddrTxt_textEdited(const QString &arg1) { auto newUpdateInterval = updateIntervalSB->value(); - ConnectionManager->SetSubscriptionData(currentSubId, arg1, newUpdateInterval); + ConnectionManager->SetSubscriptionData(currentGroupId, arg1, newUpdateInterval); } void GroupManager::on_updateIntervalSB_valueChanged(double arg1) { auto newAddress = subAddrTxt->text().trimmed(); - ConnectionManager->SetSubscriptionData(currentSubId, newAddress, arg1); + ConnectionManager->SetSubscriptionData(currentGroupId, newAddress, arg1); } void GroupManager::on_connectionsList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous) @@ -155,7 +228,7 @@ void GroupManager::on_connectionsList_currentItemChanged(QListWidgetItem *curren Q_UNUSED(previous) if (current != nullptr) { - currentConnectionId = ConnectionManager->GetConnectionIdByDisplayName(current->text(), currentSubId); + currentConnectionId = ConnectionId(current->data(Qt::UserRole).toString()); } } diff --git a/src/ui/w_GroupManager.hpp b/src/ui/w_GroupManager.hpp index debdf6c0..9ebcd4aa 100644 --- a/src/ui/w_GroupManager.hpp +++ b/src/ui/w_GroupManager.hpp @@ -48,11 +48,15 @@ class GroupManager void on_connectionsList_customContextMenuRequested(const QPoint &pos); private: + void loadConnectionList(const GroupId &group); + void onRCMActionTriggered_Move(); + void onRCMActionTriggered_Copy(); + void ReloadGroupAction(); QMenu *connectionListRCMenu = new QMenu(this); QMenu *connectionListRCMenu_CopyToMenu = new QMenu(tr("Copy to...")); QMenu *connectionListRCMenu_MoveToMenu = new QMenu(tr("Move to...")); void UpdateColorScheme(); bool isUpdateInProgress = false; - GroupId currentSubId = NullGroupId; + GroupId currentGroupId = NullGroupId; ConnectionId currentConnectionId = NullConnectionId; }; diff --git a/src/ui/w_GroupManager.ui b/src/ui/w_GroupManager.ui index 4bb0bf74..36c8f252 100644 --- a/src/ui/w_GroupManager.ui +++ b/src/ui/w_GroupManager.ui @@ -205,6 +205,13 @@ Subscription Settings + + + + This group is a subscription + + + @@ -216,9 +223,9 @@ - + - Update Interval + Last Updated @@ -230,9 +237,9 @@ - + - Last Updated + Update Interval @@ -293,13 +300,6 @@ - - - - This group is a subscription - - - From 917d053cbca3db467626fb2d2f15f6a97be06af0 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Sat, 2 May 2020 10:27:05 +0800 Subject: [PATCH 020/385] add: added gRPC version string log output --- makespec/BUILDVERSION | 2 +- src/core/kernel/APIBackend.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 638f0142..e9d178a3 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5349 \ No newline at end of file +5350 \ No newline at end of file diff --git a/src/core/kernel/APIBackend.cpp b/src/core/kernel/APIBackend.cpp index 96b87f6c..2a86b7e6 100644 --- a/src/core/kernel/APIBackend.cpp +++ b/src/core/kernel/APIBackend.cpp @@ -75,6 +75,7 @@ namespace Qv2ray::core::kernel { auto channelAddress = "127.0.0.1:" + QString::number(GlobalConfig.apiConfig.statsPort); #ifndef BACKEND_LIBQVB + LOG(MODULE_VCORE, "gRPC Version: " + QString::fromStdString(grpc::Version())) Channel = grpc::CreateChannel(channelAddress.toStdString(), grpc::InsecureChannelCredentials()); StatsService service; Stub = service.NewStub(Channel); From c2c693babb67d32d8186f88b98974c065047b04b Mon Sep 17 00:00:00 2001 From: DuckSoft Date: Sat, 2 May 2020 11:21:25 +0800 Subject: [PATCH 021/385] updating qv2ray.desktop --- assets/qv2ray.desktop | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/assets/qv2ray.desktop b/assets/qv2ray.desktop index fe95a5fa..f448ffbc 100644 --- a/assets/qv2ray.desktop +++ b/assets/qv2ray.desktop @@ -2,25 +2,48 @@ Version=1.0 Name=Qv2ray Type=Application -GenericName=V2ray Graphical Frontend -GenericName[zh_CN]=V2ray 图形前端 -Comment=A Cross-platform v2ray Qt GUI Client -Comment[zh_CN]=跨平台的 v2ray Qt 客户端 +GenericName=V2Ray Graphical Frontend +GenericName[zh_CN]=V2Ray 图形前端 +GenericName[ja]=V2Rayグラフィックフロントエンド +Comment=A Cross-Platform V2Ray Qt GUI Client +Comment[zh_CN]=跨平台的 V2Ray Qt 图形客户端 +Comment[ja]=とあるクロスプラットフォームV2Rayグラフィックフロントエンド Keywords=Internet;VPN;Proxy;v2ray;Qt;qv; -Keywords[zh_CN]=Internet;VPN;Proxy;v2ray;Qt;qv;代理;翻墙;梯子; +Keywords[zh_CN]=Internet;VPN;Proxy;v2ray;Qt;qv;代理; +Keywords[ja]=インターネット;VPN;プロキシ;Qt;v2ray;qv; Categories=Network;Qt; Terminal=false Icon=qv2ray Exec=qv2ray -Actions=noAPI;withToolbarPlugin; +Actions=debug;noPlugin;noAPI;withToolbarPlugin; + +[Desktop Action debug] +Name=Start with Debug Mode +Name[zh_CN]=以调试模式运行 +Name[ja]=デバッグモードで起動 +Exec=qv2ray --debug + +[Desktop Action noPlugin] +Name=Start without Plugins +Name[zh_CN]=不使用插件启动 +Name[ja]=プラグイン使用せずに起動 +Exec=qv2ray --noPlugin + +[Desktop Action noScaleFactor] +Name=Start without Scaling Factor +Name[zh_CN]=禁用界面缩放启动 +Name[ja]=インターフェーススケーリング使用せずに起動 +Exec=qv2ray --noScaleFactor [Desktop Action noAPI] Name=Start without gRPC API Name[zh_CN]=不使用 gRPC API 启动 +Name[ja]=gRPC使用せずに起動 Exec=qv2ray --noAPI [Desktop Action withToolbarPlugin] Name=Start with Toolbar Plugin Name[zh_CN]=使用工具栏插件启动 +Name[ja]=ツールバープラグイン使用するで起動 Exec=qv2ray --withToolbarPlugin From d55f873f960b14d7f297b8f03f6e6b4bfbad2f21 Mon Sep 17 00:00:00 2001 From: DuckSoft Date: Sat, 2 May 2020 11:34:08 +0800 Subject: [PATCH 022/385] fixing up desktop file --- assets/qv2ray.desktop | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/qv2ray.desktop b/assets/qv2ray.desktop index f448ffbc..09b129ac 100644 --- a/assets/qv2ray.desktop +++ b/assets/qv2ray.desktop @@ -15,7 +15,7 @@ Categories=Network;Qt; Terminal=false Icon=qv2ray Exec=qv2ray -Actions=debug;noPlugin;noAPI;withToolbarPlugin; +Actions=debug;noPlugin;noScaleFactor;noAPI;withToolbarPlugin; [Desktop Action debug] Name=Start with Debug Mode From 9528cd6b2854e4ac520d76225a4f90cfe7ad809a Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Sat, 2 May 2020 12:46:05 +0800 Subject: [PATCH 023/385] fix: fixed crash on moving/copying connections --- src/ui/w_GroupManager.cpp | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/ui/w_GroupManager.cpp b/src/ui/w_GroupManager.cpp index 1e63f23b..1bcf0e85 100644 --- a/src/ui/w_GroupManager.cpp +++ b/src/ui/w_GroupManager.cpp @@ -5,7 +5,15 @@ #include "core/settings/SettingsBackend.hpp" #include - +#define GET_DATA(type, typeConv) \ + [&](const QList list) { \ + QList _list; \ + for (const auto &item : list) \ + { \ + _list.push_back(item->data(Qt::UserRole).to##typeConv()); \ + } \ + return _list; \ + } GroupManager::GroupManager(QWidget *parent) : QDialog(parent) { setupUi(this); @@ -75,28 +83,23 @@ void GroupManager::onRCMActionTriggered_Copy() const auto _sender = qobject_cast(sender()); const GroupId groupId{ _sender->data().toString() }; // - const auto &connectionList = connectionsList->selectedItems(); - for (const auto &connItem : connectionList) + const auto list = GET_DATA(QString, String)(connectionsList->selectedItems()); + for (const auto &connId : list) { - const auto &connectionId = ConnectionId(connItem->data(Qt::UserRole).toString()); + const auto &connectionId = ConnectionId(connId); ConnectionManager->CreateConnection(GetDisplayName(connectionId), groupId, ConnectionManager->GetConnectionRoot(connectionId), true); } } + void GroupManager::onRCMActionTriggered_Move() { const auto _sender = qobject_cast(sender()); const GroupId groupId{ _sender->data().toString() }; // - const auto &connectionList = connectionsList->selectedItems(); - for (const auto &connItem : connectionList) + const auto list = GET_DATA(QString, String)(connectionsList->selectedItems()); + for (const auto &connId : list) { - if (!connItem) - { - LOG(MODULE_UI, "Invalid?") - continue; - } - const auto &id = connItem->data(Qt::UserRole); - ConnectionManager->MoveConnectionGroup(ConnectionId(id.toString()), groupId); + ConnectionManager->MoveConnectionGroup(ConnectionId(connId), groupId); } } @@ -237,3 +240,4 @@ void GroupManager::on_connectionsList_customContextMenuRequested(const QPoint &p Q_UNUSED(pos) connectionListRCMenu->popup(QCursor::pos()); } +#undef GET_DATA From f219edeb91cd759d167ef25fb64cd77edab40ee9 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Sat, 2 May 2020 14:00:46 +0800 Subject: [PATCH 024/385] fix: fixed MainWindow button size issue --- src/ui/w_MainWindow.ui | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/ui/w_MainWindow.ui b/src/ui/w_MainWindow.ui index 99354834..0d75a4e3 100644 --- a/src/ui/w_MainWindow.ui +++ b/src/ui/w_MainWindow.ui @@ -177,24 +177,12 @@ - - - 0 - 0 - - Import Connection Add Connection - - - 22 - 22 - - From ad114a91510f50f68a57a692cb7402b717aa3f61 Mon Sep 17 00:00:00 2001 From: ymshenyu Date: Sat, 2 May 2020 16:29:40 +0800 Subject: [PATCH 025/385] update build-qv2ray-cmake.yml and nsis.yml --- .github/workflows/build-qv2ray-cmake.yml | 2 +- .github/workflows/nsis.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-qv2ray-cmake.yml b/.github/workflows/build-qv2ray-cmake.yml index af4e0b14..9022233b 100644 --- a/.github/workflows/build-qv2ray-cmake.yml +++ b/.github/workflows/build-qv2ray-cmake.yml @@ -74,7 +74,7 @@ jobs: path: ../Qt key: QtCache-${{ matrix.platform }}-${{ matrix.arch }}-${{ matrix.qt_version }} - name: Installing Qt - ${{ matrix.arch }} - uses: jurplel/install-qt-action@v2.5.0 + uses: jurplel/install-qt-action@v2 with: version: ${{ matrix.qt_version }} arch: ${{ matrix.qtarch }} diff --git a/.github/workflows/nsis.yml b/.github/workflows/nsis.yml index 6b84868c..36f9dbc9 100644 --- a/.github/workflows/nsis.yml +++ b/.github/workflows/nsis.yml @@ -68,7 +68,7 @@ jobs: path: ../Qt key: QtCache-${{ matrix.platform }}-${{ matrix.arch }}-${{ matrix.qt_version }} - name: Installing Qt - ${{ matrix.arch }} - uses: jurplel/install-qt-action@v2.5.0 + uses: jurplel/install-qt-action@v2 with: version: ${{ matrix.qt_version }} arch: ${{ matrix.qtarch }} From 58dac521c51346fea43693dbfebb12af67136e31 Mon Sep 17 00:00:00 2001 From: ymshenyu <39402395+ymshenyu@users.noreply.github.com> Date: Sat, 2 May 2020 20:11:01 +0800 Subject: [PATCH 026/385] downgrade gcc to 7.5 (#566) * Update build-qv2ray-cmake.yml * switch from xenial to bionic * Remove unneeded PPA * grpc: upgrade to 1.26.0 * Revert "grpc: upgrade to 1.26.0" This reverts commit 2da82a9d3ffe2dfb389dba06e47e8fff30201967. * Revert "Remove unneeded PPA" This reverts commit d74c0c8accd39b8cfcf791f04cf8edef9047279a. * Revert "switch from xenial to bionic" This reverts commit caee8cd8c7cfd38918578398f994a3ff6b4f2c6f. Co-authored-by: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> --- .github/workflows/build-qv2ray-cmake.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-qv2ray-cmake.yml b/.github/workflows/build-qv2ray-cmake.yml index 9022233b..6093b391 100644 --- a/.github/workflows/build-qv2ray-cmake.yml +++ b/.github/workflows/build-qv2ray-cmake.yml @@ -142,8 +142,8 @@ jobs: if: matrix.platform == 'ubuntu-16.04' shell: bash env: - CC: /usr/bin/gcc-9 - CXX: /usr/bin/g++-9 + CC: /usr/bin/gcc-7 + CXX: /usr/bin/g++-7 run: | mkdir build cd build From 6a46dedc46624408707c03aa521a4579a5930e13 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Sat, 2 May 2020 22:20:40 +0800 Subject: [PATCH 027/385] add: added Connection Removing and exporting via GroupManager --- makespec/BUILDVERSION | 2 +- src/common/QvHelpers.cpp | 10 +- src/core/handler/ConfigHandler.cpp | 9 +- src/core/handler/ConfigHandler.hpp | 2 +- src/ui/w_GroupManager.cpp | 151 +++++++++++++++++--- src/ui/w_GroupManager.hpp | 19 ++- src/ui/w_GroupManager.ui | 218 ++++++++++++++++------------- 7 files changed, 280 insertions(+), 131 deletions(-) diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index e9d178a3..8e008abc 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5350 \ No newline at end of file +5360 diff --git a/src/common/QvHelpers.cpp b/src/common/QvHelpers.cpp index b4a7dffb..5113bb28 100644 --- a/src/common/QvHelpers.cpp +++ b/src/common/QvHelpers.cpp @@ -12,7 +12,7 @@ namespace Qv2ray::common { const QString GenerateRandomString(int len) { - const QString possibleCharacters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); + const QString possibleCharacters("abcdefghijklmnopqrstuvwxyz"); QString randomString; for (int i = 0; i < len; ++i) @@ -41,18 +41,14 @@ namespace Qv2ray::common if (!wasOpened) source.close(); // - QTextCodec::ConverterState state; QTextCodec *codec = QTextCodec::codecForName("UTF-8"); + QTextCodec::ConverterState state; const QString text = codec->toUnicode(byteArray.constData(), byteArray.size(), &state); if (state.invalidChars > 0) { LOG(MODULE_FILEIO, "Not a valid UTF-8 sequence: " + source.fileName()) - return byteArray; - } - else - { - return text; } + return state.invalidChars > 0 ? byteArray : text; } bool StringToFile(const QString &text, const QString &targetpath) diff --git a/src/core/handler/ConfigHandler.cpp b/src/core/handler/ConfigHandler.cpp index a01c86f6..6d4c2112 100644 --- a/src/core/handler/ConfigHandler.cpp +++ b/src/core/handler/ConfigHandler.cpp @@ -446,13 +446,14 @@ namespace Qv2ray::core::handlers return { groups[id].address, groups[id].lastUpdated, groups[id].updateInterval }; } - bool QvConfigHandler::SetSubscriptionData(const GroupId &id, const QString &address, float updateInterval) + bool QvConfigHandler::SetSubscriptionData(const GroupId &id, bool isSubscription, const QString &address, float updateInterval) { CheckGroupExistanceEx(id, false); if (!groups.contains(id)) { return false; } + groups[id].isSubscription = isSubscription; if (!address.isEmpty()) { groups[id].address = address; @@ -618,3 +619,9 @@ namespace Qv2ray::core::handlers } } // namespace Qv2ray::core::handlers + +#undef CheckIdExistance +#undef CheckGroupExistanceEx +#undef CheckGroupExistance +#undef CheckConnectionExistanceEx +#undef CheckConnectionExistance diff --git a/src/core/handler/ConfigHandler.hpp b/src/core/handler/ConfigHandler.hpp index 4f5dc971..c0e2b898 100644 --- a/src/core/handler/ConfigHandler.hpp +++ b/src/core/handler/ConfigHandler.hpp @@ -98,7 +98,7 @@ namespace Qv2ray::core::handlers // const optional DuplicateGroup(const GroupId &id); // // Subscriptions - bool SetSubscriptionData(const GroupId &id, const QString &address = "", float updateInterval = -1); + bool SetSubscriptionData(const GroupId &id, bool isSubscription, const QString &address = "", float updateInterval = -1); bool UpdateSubscription(const GroupId &id); // bool UpdateSubscriptionASync(const GroupId &id, bool useSystemProxy); const tuple GetSubscriptionData(const GroupId &id) const; diff --git a/src/ui/w_GroupManager.cpp b/src/ui/w_GroupManager.cpp index 1bcf0e85..2f7f456d 100644 --- a/src/ui/w_GroupManager.cpp +++ b/src/ui/w_GroupManager.cpp @@ -1,9 +1,11 @@ #include "w_GroupManager.hpp" #include "common/QvHelpers.hpp" +#include "core/connection/Generation.hpp" #include "core/handler/ConfigHandler.hpp" #include "core/settings/SettingsBackend.hpp" +#include #include #define GET_DATA(type, typeConv) \ [&](const QList list) { \ @@ -19,20 +21,30 @@ GroupManager::GroupManager(QWidget *parent) : QDialog(parent) setupUi(this); QvMessageBusConnect(GroupManager); UpdateColorScheme(); + connectionListRCMenu->addSection(tr("Connection Management")); + connectionListRCMenu->addAction(exportConnectionAction); + connectionListRCMenu->addAction(deleteConnectionAction); + connectionListRCMenu->addSeparator(); connectionListRCMenu->addMenu(connectionListRCMenu_CopyToMenu); connectionListRCMenu->addMenu(connectionListRCMenu_MoveToMenu); // + connect(exportConnectionAction, &QAction::triggered, this, &GroupManager::onRCMExportConnectionTriggered); + connect(deleteConnectionAction, &QAction::triggered, this, &GroupManager::onRCMDeleteConnectionTriggered); + // connect(ConnectionManager, &QvConfigHandler::OnConnectionGroupChanged, // [&](const ConnectionId &, const GroupId &, const GroupId &) { // this->loadConnectionList(currentGroupId); // }); // - connect(ConnectionManager, &QvConfigHandler::OnConnectionCreated, // - [&](const ConnectionId &id, const QString &) { // - const auto groupId = GetConnectionGroupId(id); // - if (groupId == currentGroupId) // - this->loadConnectionList(groupId); // - }); // + connect(ConnectionManager, &QvConfigHandler::OnConnectionCreated, [&](const ConnectionId &id) { // + const auto groupId = GetConnectionGroupId(id); // + if (groupId == currentGroupId) // + this->loadConnectionList(groupId); // + }); // + connect(ConnectionManager, &QvConfigHandler::OnConnectionDeleted, [&](const ConnectionId &, const GroupId &group) { + if (group == currentGroupId) // + this->loadConnectionList(group); // + }); // // for (auto group : ConnectionManager->AllGroups()) { @@ -47,6 +59,66 @@ GroupManager::GroupManager(QWidget *parent) : QDialog(parent) ReloadGroupAction(); } +void GroupManager::onRCMDeleteConnectionTriggered() +{ + const auto list = GET_DATA(QString, String)(connectionsList->selectedItems()); + for (const auto &item : list) + { + ConnectionManager->DeleteConnection(ConnectionId(item)); + } +} + +void GroupManager::onRCMExportConnectionTriggered() +{ + const auto list = GET_DATA(QString, String)(connectionsList->selectedItems()); + QFileDialog d; + switch (list.count()) + { + case 0: return; + case 1: + { + const auto id = ConnectionId(list.first()); + auto filePath = d.getSaveFileName(this, GetDisplayName(id)); + if (filePath.isEmpty()) + return; + auto root = GenerateRuntimeConfig(ConnectionManager->GetConnectionRoot(id)); + // + // Apply export filter + ExportConnectionFilter(root); + // + if (filePath.endsWith(".json")) + { + filePath += ".json"; + } + // + QFile file(filePath); + StringToFile(JsonToString(root), file); + QDesktopServices::openUrl(QUrl::fromLocalFile(QFileInfo(file).absoluteDir().absolutePath())); + break; + } + default: + { + const auto path = d.getExistingDirectory(); + if (path.isEmpty()) + return; + for (const auto &connId : list) + { + ConnectionId id(connId); + auto root = GenerateRuntimeConfig(ConnectionManager->GetConnectionRoot(id)); + // + // Apply export filter + ExportConnectionFilter(root); + // + const auto fileName = RemoveInvalidFileName(GetDisplayName(id)) + ".json"; + QFile file(path + "/" + fileName); + StringToFile(JsonToString(root), file); + } + QDesktopServices::openUrl(QUrl::fromLocalFile(path)); + break; + } + } +} + void GroupManager::ReloadGroupAction() { connectionListRCMenu_CopyToMenu->clear(); @@ -74,7 +146,7 @@ void GroupManager::loadConnectionList(const GroupId &group) { auto item = new QListWidgetItem(GetDisplayName(conn), connectionsList); item->setData(Qt::UserRole, conn.toString()); - connectionsList->addItem(item); // + connectionsList->addItem(item); } } @@ -155,7 +227,7 @@ void GroupManager::on_removeGroupButton_clicked() if (QvMessageBoxAsk(this, tr("Deleting a subscription"), tr("All connections will be moved to default group, do you want to continue?")) == QMessageBox::Yes) { - ConnectionManager->DeleteGroup(currentGroupId); // + ConnectionManager->DeleteGroup(currentGroupId); auto item = groupList->currentItem(); groupList->removeItemWidget(item); delete item; @@ -183,20 +255,20 @@ void GroupManager::on_groupList_itemSelectionChanged() void GroupManager::on_groupList_itemClicked(QListWidgetItem *item) { - if (item == nullptr) { return; } - groupInfoGroupBox->setEnabled(true); currentGroupId = GroupId(item->data(Qt::UserRole).toString()); // groupNameTxt->setText(GetDisplayName(currentGroupId)); - auto const [addr, lastUpdated, updateInterval] = ConnectionManager->GetSubscriptionData(currentGroupId); - subAddrTxt->setText(addr); - lastUpdatedLabel->setText(timeToString(lastUpdated)); - updateIntervalSB->setValue(updateInterval); + const auto &groupMetaObject = ConnectionManager->GetGroupMetaObject(currentGroupId); + groupIsSubscriptionGroup->setChecked(groupMetaObject.isSubscription); + subAddrTxt->setText(groupMetaObject.address); + lastUpdatedLabel->setText(timeToString(groupMetaObject.lastUpdated)); + createdAtLabel->setText(timeToString(groupMetaObject.importDate)); + updateIntervalSB->setValue(groupMetaObject.updateInterval); // connectionsList->clear(); loadConnectionList(currentGroupId); @@ -208,22 +280,16 @@ void GroupManager::on_groupList_currentItemChanged(QListWidgetItem *current, QLi on_groupList_itemClicked(current); } -void GroupManager::on_subNameTxt_textEdited(const QString &arg1) -{ - groupList->selectedItems().first()->setText(arg1); - ConnectionManager->RenameGroup(currentGroupId, arg1.trimmed()); -} - void GroupManager::on_subAddrTxt_textEdited(const QString &arg1) { auto newUpdateInterval = updateIntervalSB->value(); - ConnectionManager->SetSubscriptionData(currentGroupId, arg1, newUpdateInterval); + ConnectionManager->SetSubscriptionData(currentGroupId, true, arg1, newUpdateInterval); } void GroupManager::on_updateIntervalSB_valueChanged(double arg1) { auto newAddress = subAddrTxt->text().trimmed(); - ConnectionManager->SetSubscriptionData(currentGroupId, newAddress, arg1); + ConnectionManager->SetSubscriptionData(currentGroupId, true, newAddress, arg1); } void GroupManager::on_connectionsList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous) @@ -240,4 +306,45 @@ void GroupManager::on_connectionsList_customContextMenuRequested(const QPoint &p Q_UNUSED(pos) connectionListRCMenu->popup(QCursor::pos()); } + +void GroupManager::on_groupIsSubscriptionGroup_clicked(bool checked) +{ + ConnectionManager->SetSubscriptionData(currentGroupId, checked); +} + +void GroupManager::on_groupNameTxt_textEdited(const QString &arg1) +{ + groupList->selectedItems().first()->setText(arg1); + ConnectionManager->RenameGroup(currentGroupId, arg1.trimmed()); +} + +void GroupManager::on_deleteSelectedConnBtn_clicked() +{ + onRCMDeleteConnectionTriggered(); +} + +void GroupManager::on_exportSelectedConnBtn_clicked() +{ + if (connectionsList->selectedItems().isEmpty()) + { + connectionsList->selectAll(); + } + onRCMExportConnectionTriggered(); +} + +void GroupManager::ExportConnectionFilter(CONFIGROOT &root) +{ + root.remove("api"); + QJsonArray inbounds = root["inbounds"].toArray(); + for (int i = root["inbounds"].toArray().count() - 1; i >= 0; i--) + { + auto obj = root["inbounds"].toArray().at(i).toObject(); + if (obj["tag"] == API_TAG_INBOUND) + { + inbounds.removeAt(i); + } + } + root["inbounds"] = inbounds; +} + #undef GET_DATA diff --git a/src/ui/w_GroupManager.hpp b/src/ui/w_GroupManager.hpp index 9ebcd4aa..1892ef90 100644 --- a/src/ui/w_GroupManager.hpp +++ b/src/ui/w_GroupManager.hpp @@ -37,8 +37,6 @@ class GroupManager void on_groupList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous); - void on_subNameTxt_textEdited(const QString &arg1); - void on_subAddrTxt_textEdited(const QString &arg1); void on_updateIntervalSB_valueChanged(double arg1); @@ -47,12 +45,29 @@ class GroupManager void on_connectionsList_customContextMenuRequested(const QPoint &pos); + void on_groupIsSubscriptionGroup_clicked(bool checked); + + void on_groupNameTxt_textEdited(const QString &arg1); + + private slots: + void onRCMDeleteConnectionTriggered(); + void onRCMExportConnectionTriggered(); + + void on_deleteSelectedConnBtn_clicked(); + + void on_exportSelectedConnBtn_clicked(); + private: void loadConnectionList(const GroupId &group); void onRCMActionTriggered_Move(); void onRCMActionTriggered_Copy(); void ReloadGroupAction(); + // + void ExportConnectionFilter(CONFIGROOT &root); + // QMenu *connectionListRCMenu = new QMenu(this); + QAction *exportConnectionAction = new QAction(tr("Export Connection(s)"), connectionListRCMenu); + QAction *deleteConnectionAction = new QAction(tr("Delete Connection(s)"), connectionListRCMenu); QMenu *connectionListRCMenu_CopyToMenu = new QMenu(tr("Copy to...")); QMenu *connectionListRCMenu_MoveToMenu = new QMenu(tr("Move to...")); void UpdateColorScheme(); diff --git a/src/ui/w_GroupManager.ui b/src/ui/w_GroupManager.ui index 36c8f252..92f5db60 100644 --- a/src/ui/w_GroupManager.ui +++ b/src/ui/w_GroupManager.ui @@ -50,9 +50,9 @@ - + - DATE + @@ -168,20 +168,6 @@ Connections - - - - New Connection - - - - - - - Remove Selection - - - @@ -191,103 +177,115 @@ QAbstractItemView::NoEditTriggers - QAbstractItemView::MultiSelection + QAbstractItemView::ExtendedSelection QListView::Adjust + + + + Delete Selection + + + + + + + Export Selection + + + Subscription Settings - - - - + + + + This group is a subscription - - - - - - Subscription Address + + true + + + + + Subscription Address + + + + + + + + + + Last Updated + + + + + + + + + + + + + + Update Interval + + + + + + + + + 365.000000000000000 + + + 0.500000000000000 + + + 5.000000000000000 + + + + + + + Days + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + - - - - - - - Last Updated - - - - - - - - - - - - - - Update Interval - - - - - - - - - 365.000000000000000 - - - 0.500000000000000 - - - 5.000000000000000 - - - - - - - Days - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - Update Subscription Data - - - - + Qt::Vertical @@ -300,6 +298,33 @@ + + + + Update Subscription Data + + + + + + + + Connection Settings + + + + + + + + + + Route Settings + + + + + @@ -310,7 +335,6 @@ groupList addGroupButton removeGroupButton - subAddrTxt updateIntervalSB From cbf4be37d977d8894859d083bf45ab03a5a5d52e Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Sun, 3 May 2020 07:52:03 +0800 Subject: [PATCH 028/385] fix: try preventing port conflict --- src/base/models/QvSettingsObject.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/base/models/QvSettingsObject.hpp b/src/base/models/QvSettingsObject.hpp index c80227ea..3ba65190 100644 --- a/src/base/models/QvSettingsObject.hpp +++ b/src/base/models/QvSettingsObject.hpp @@ -89,8 +89,8 @@ namespace Qv2ray::base::config bool dnsIntercept; Qv2rayInboundsConfig() - : listenip("127.0.0.1"), setSystemProxy(true), useSocks(true), socks_port(1088), socks_useAuth(false), socksUDP(true), - socksLocalIP("127.0.0.1"), socksAccount(), socksSniffing(false), useHTTP(true), http_port(8888), http_useAuth(false), + : listenip("127.0.0.1"), setSystemProxy(true), useSocks(true), socks_port(1089), socks_useAuth(false), socksUDP(true), + socksLocalIP("127.0.0.1"), socksAccount(), socksSniffing(false), useHTTP(true), http_port(8889), http_useAuth(false), httpAccount(), httpSniffing(false), useTPROXY(false), tproxy_ip("127.0.0.1"), tproxy_port(12345), tproxy_use_tcp(true), tproxy_use_udp(false), tproxy_followRedirect(true), tproxy_mode("tproxy"), dnsIntercept(true) { From 6e26488e93fff88ee2dd6e9f407d58915604ea02 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Sun, 3 May 2020 11:20:27 +0800 Subject: [PATCH 029/385] refactor: refactored Qv2rayBase.hpp, preparation for config file increament. --- makespec/BUILDVERSION | 2 +- src/base/Qv2rayBase.hpp | 9 +++------ src/core/settings/SettingsBackend.hpp | 6 ++++++ src/core/settings/SettingsUpgrade.cpp | 6 ++++-- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 8e008abc..6a454d92 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5360 +5363 \ No newline at end of file diff --git a/src/base/Qv2rayBase.hpp b/src/base/Qv2rayBase.hpp index 55d89ea8..6a8cdd02 100644 --- a/src/base/Qv2rayBase.hpp +++ b/src/base/Qv2rayBase.hpp @@ -97,11 +97,11 @@ using namespace Qv2ray::base::objects::transfer; #define ACCESS_OPTIONAL_VALUE(obj) (obj.value()) #endif -#define Q_TRAYICON(name) (QIcon(GlobalConfig.uiConfig.useDarkTrayIcon ? ":/assets/icons/ui_dark/" name : ":/assets/icons/ui_light/" name)) +#define QV2RAY_COLORSCHEME_ROOT_X(flag) ((flag) ? QStringLiteral(":/assets/icons/ui_dark/") : QStringLiteral(":/assets/icons/ui_light/")) +#define QV2RAY_COLORSCHEME_ROOT QV2RAY_COLORSCHEME_ROOT_X(GlobalConfig.uiConfig.useDarkTheme) -#define QV2RAY_COLORSCHEME_ROOT \ - ((GlobalConfig.uiConfig.useDarkTheme) ? QStringLiteral(":/assets/icons/ui_dark/") : QStringLiteral(":/assets/icons/ui_light/")) #define QICON_R(file) QIcon(QV2RAY_COLORSCHEME_ROOT + file) +#define Q_TRAYICON(name) (QIcon(QV2RAY_COLORSCHEME_ROOT_X(GlobalConfig.uiConfig.useDarkTrayIcon) + name)) #define QSTRN(num) QString::number(num) @@ -123,9 +123,6 @@ using namespace Qv2ray::base::objects::transfer; namespace Qv2ray { - // Extra header for QvConfigUpgrade.cpp - QJsonObject UpgradeSettingsVersion(int fromVersion, int toVersion, QJsonObject root); - // Qv2ray runtime config inline bool isExiting = false; inline QString Qv2rayConfigPath = ""; diff --git a/src/core/settings/SettingsBackend.hpp b/src/core/settings/SettingsBackend.hpp index cfcc20b1..a1193869 100644 --- a/src/core/settings/SettingsBackend.hpp +++ b/src/core/settings/SettingsBackend.hpp @@ -9,5 +9,11 @@ namespace Qv2ray::core::config bool CheckSettingsPathAvailability(const QString &_path, bool checkExistingConfig); } // namespace Qv2ray::core::config +namespace Qv2ray +{ + // Extra header for QvConfigUpgrade.cpp + QJsonObject UpgradeSettingsVersion(int fromVersion, int toVersion, const QJsonObject &root); +} // namespace Qv2ray + using namespace Qv2ray::core; using namespace Qv2ray::core::config; diff --git a/src/core/settings/SettingsUpgrade.cpp b/src/core/settings/SettingsUpgrade.cpp index 1cdd6df8..135183a8 100644 --- a/src/core/settings/SettingsUpgrade.cpp +++ b/src/core/settings/SettingsUpgrade.cpp @@ -10,8 +10,9 @@ namespace Qv2ray { // Private member - QJsonObject UpgradeConfig_Inc(int fromVersion, QJsonObject root) + QJsonObject UpgradeConfig_Inc(int fromVersion, const QJsonObject &original) { + auto root = original; switch (fromVersion) { // Cases 1, 2, and 3 are not supported anymore. @@ -305,8 +306,9 @@ namespace Qv2ray } // Exported function - QJsonObject UpgradeSettingsVersion(int fromVersion, int toVersion, QJsonObject root) + QJsonObject UpgradeSettingsVersion(int fromVersion, int toVersion, const QJsonObject &original) { + auto root = original; LOG(MODULE_SETTINGS, "Migrating config from version " + QSTRN(fromVersion) + " to " + QSTRN(toVersion)) for (int i = fromVersion; i < toVersion; i++) From 891e8e3f5c5e5b798f9b3acbfb0b941ff2d36a7d Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Sun, 3 May 2020 13:53:53 +0800 Subject: [PATCH 030/385] refactor: get rid of 'using namespace std;' --- src/base/Qv2rayBase.hpp | 8 +- src/base/Qv2rayLog.cpp | 4 +- src/base/Qv2rayLog.hpp | 3 +- src/base/models/QvSafeType.hpp | 1 - src/common/QvHelpers.cpp | 25 +- src/common/QvHelpers.hpp | 11 +- src/common/QvTranslator.cpp | 10 +- src/components/latency/QvTCPing.cpp | 14 +- src/components/plugins/toolbar/QvToolbar.cpp | 282 +++++++++---------- src/components/proxy/QvProxyConfigurator.cpp | 1 + src/core/CoreUtils.cpp | 10 +- src/core/CoreUtils.hpp | 6 +- src/core/handler/ConfigHandler.cpp | 18 +- src/core/handler/ConfigHandler.hpp | 14 +- src/core/kernel/V2rayKernelInteractions.cpp | 2 +- src/core/kernel/V2rayKernelInteractions.hpp | 2 +- src/core/settings/SettingsUpgrade.cpp | 1 + src/main.cpp | 12 +- src/ui/editors/w_JsonEditor.cpp | 2 +- src/ui/editors/w_JsonEditor.hpp | 2 +- src/ui/editors/w_RoutesEditor.hpp | 1 + src/ui/editors/w_RoutesEditor_extra.cpp | 6 +- src/ui/models/InboundNodeModel.hpp | 2 +- src/ui/models/NodeModelsBase.hpp | 1 + src/ui/models/OutboundNodeModel.hpp | 6 +- src/ui/models/RuleNodeModel.hpp | 6 +- src/ui/w_GroupManager.cpp | 2 +- src/ui/w_GroupManager.hpp | 2 +- src/ui/w_MainWindow.cpp | 26 +- src/ui/w_MainWindow.hpp | 4 +- src/ui/w_ScreenShot_Core.cpp | 1 + src/ui/widgets/ConnectionInfoWidget.cpp | 8 +- src/ui/widgets/ConnectionInfoWidget.hpp | 2 +- src/ui/widgets/ConnectionItemWidget.hpp | 2 +- src/ui/widgets/RouteSettingsMatrix.cpp | 5 +- 35 files changed, 250 insertions(+), 252 deletions(-) diff --git a/src/base/Qv2rayBase.hpp b/src/base/Qv2rayBase.hpp index 6a8cdd02..7052357e 100644 --- a/src/base/Qv2rayBase.hpp +++ b/src/base/Qv2rayBase.hpp @@ -21,8 +21,6 @@ #include "base/models/QvSettingsObject.hpp" #include "base/models/QvStartupConfig.hpp" -using namespace std; -using namespace std::chrono; using namespace Qv2ray; using namespace Qv2ray::base; using namespace Qv2ray::base::safetype; @@ -144,12 +142,14 @@ namespace Qv2ray // 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); + // For AppImage? + list << QString(QDir(QApplication::applicationDirPath() + "/../share/qv2ray/" + dirName).absolutePath()); #elif defined(Q_OS_MAC) // macOS platform directories. list << QDir(QApplication::applicationDirPath() + "/../Resources/" + dirName).absolutePath(); #endif + list << QStandardPaths::locateAll(QStandardPaths::AppDataLocation, dirName, QStandardPaths::LocateDirectory); + list << QStandardPaths::locateAll(QStandardPaths::AppConfigLocation, dirName, QStandardPaths::LocateDirectory); // This is the default behavior on Windows list << QApplication::applicationDirPath() + "/" + dirName; list.removeDuplicates(); diff --git a/src/base/Qv2rayLog.cpp b/src/base/Qv2rayLog.cpp index c2bb4f21..ee94d009 100644 --- a/src/base/Qv2rayLog.cpp +++ b/src/base/Qv2rayLog.cpp @@ -15,7 +15,7 @@ namespace Qv2ray::base void __QV2RAY_LOG_FUNC__(int type, const std::string &func, int line, const QString &module, const QString &log) { auto logString = QString("[" % module % "]: " % log); - auto funcPrepend = QString::fromStdString(func + ":" + to_string(line) + " "); + auto funcPrepend = QString::fromStdString(func + ":" + std::to_string(line) + " "); #ifdef QT_DEBUG // Debug build version, we only print info for DEBUG logs and print @@ -40,7 +40,7 @@ namespace Qv2ray::base } } #endif - cout << logString.toStdString() << endl; + std::cout << logString.toStdString() << std::endl; { QMutexLocker _(&__loggerMutex); __loggerBuffer->append(logString + NEWLINE); diff --git a/src/base/Qv2rayLog.hpp b/src/base/Qv2rayLog.hpp index c01499c0..61fbc88f 100644 --- a/src/base/Qv2rayLog.hpp +++ b/src/base/Qv2rayLog.hpp @@ -1,7 +1,6 @@ #pragma once #include -using namespace std; /* * Tiny log module. @@ -18,7 +17,7 @@ namespace Qv2ray::base #define QV2RAY_LOG_NORMAL 0 #define QV2RAY_LOG_DEBUG 1 -#define __LOG_IMPL(LEVEL, MODULE, MSG) __QV2RAY_LOG_FUNC__(LEVEL, Q_FUNC_INFO, __LINE__, MODULE, MSG); +#define __LOG_IMPL(LEVEL, MODULE, MSG) ::Qv2ray::base::__QV2RAY_LOG_FUNC__(LEVEL, Q_FUNC_INFO, __LINE__, MODULE, MSG); #define LOG(MODULE, MSG) __LOG_IMPL(QV2RAY_LOG_NORMAL, (MODULE), (MSG)); #define DEBUG(MODULE, MSG) __LOG_IMPL(QV2RAY_LOG_DEBUG, (MODULE), (MSG)); diff --git a/src/base/models/QvSafeType.hpp b/src/base/models/QvSafeType.hpp index adf17f6d..4298e503 100644 --- a/src/base/models/QvSafeType.hpp +++ b/src/base/models/QvSafeType.hpp @@ -21,7 +21,6 @@ #define nothing #define SAFE_TYPEDEF(Base, name) SAFE_TYPEDEF_EXTRA(Base, name, nothing) -using namespace std; namespace Qv2ray::base::safetype { // To prevent anonying QJsonObject misuse diff --git a/src/common/QvHelpers.cpp b/src/common/QvHelpers.cpp index 5113bb28..f951357e 100644 --- a/src/common/QvHelpers.cpp +++ b/src/common/QvHelpers.cpp @@ -1,12 +1,11 @@ #include "common/QvHelpers.hpp" +#include "base/Qv2rayBase.hpp" #include "libs/puresource/src/PureJson.hpp" #include -#include +#include #include -#include -#include namespace Qv2ray::common { @@ -129,17 +128,17 @@ namespace Qv2ray::common return _string.split(QRegExp("[\r\n]"), QString::SkipEmptyParts); } - list SplitLines_std(const QString &_string) - { - list list; + // list SplitLines_std(const QString &_string) + // { + // list list; - for (auto line : _string.split(QRegExp("[\r\n]"), QString::SkipEmptyParts)) - { - list.push_back(line.toStdString()); - } + // for (auto line : _string.split(QRegExp("[\r\n]"), QString::SkipEmptyParts)) + // { + // list.push_back(line.toStdString()); + // } - return list; - } + // return list; + // } QStringList GetFileList(const QDir &dir) { @@ -191,7 +190,7 @@ namespace Qv2ray::common { std::string _name = fileName.toStdString(); std::replace_if( - _name.begin(), _name.end(), [](char c) { return std::string::npos != string(R"("/\?%&^*;:|><)").find(c); }, '_'); + _name.begin(), _name.end(), [](char c) { return std::string::npos != std::string(R"("/\?%&^*;:|><)").find(c); }, '_'); return QString::fromStdString(_name); } diff --git a/src/common/QvHelpers.hpp b/src/common/QvHelpers.hpp index 584beb19..c4214d22 100644 --- a/src/common/QvHelpers.hpp +++ b/src/common/QvHelpers.hpp @@ -1,8 +1,13 @@ #pragma once -#include "base/Qv2rayBase.hpp" - +#include +#include +#include +#include #include +#include +#include +#include #define REGEX_IPV6_ADDR \ R"(\[\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*\])" @@ -16,7 +21,7 @@ namespace Qv2ray::common QString Base64Encode(const QString &string); QString Base64Decode(const QString &string); QStringList SplitLines(const QString &str); - list SplitLines_std(const QString &_string); + // list SplitLines_std(const QString &_string); bool FileExistsIn(const QDir &dir, const QString &fileName); const QString GenerateRandomString(int len = 12); // diff --git a/src/common/QvTranslator.cpp b/src/common/QvTranslator.cpp index 2c5f5c63..03f983d4 100644 --- a/src/common/QvTranslator.cpp +++ b/src/common/QvTranslator.cpp @@ -1,16 +1,8 @@ #include "QvTranslator.hpp" -#include "base/Qv2rayLog.hpp" +#include "base/Qv2rayBase.hpp" #include "common/QvHelpers.hpp" -#include -#include -#include -#include -#include -#include -#include - using namespace Qv2ray::base; // path searching list. diff --git a/src/components/latency/QvTCPing.cpp b/src/components/latency/QvTCPing.cpp index 0091a4ed..77046fec 100644 --- a/src/components/latency/QvTCPing.cpp +++ b/src/components/latency/QvTCPing.cpp @@ -116,8 +116,8 @@ namespace Qv2ray::components::tcping auto milliseconds = std::chrono::duration_cast(end - start); uint ms = milliseconds.count(); data.avg += ms; - data.worst = min(data.worst, ms); - data.best = max(data.best, ms); + data.worst = std::min(data.worst, ms); + data.best = std::max(data.best, ms); if (ms > 1000) { @@ -132,13 +132,13 @@ namespace Qv2ray::components::tcping data.avg = data.avg / successCount; freeaddrinfo(resolved); - data.avg = min(data.avg, QVTCPING_VALUE_ERROR); - data.worst = min(data.worst, QVTCPING_VALUE_ERROR); - data.best = min(data.best, QVTCPING_VALUE_ERROR); + data.avg = std::min(data.avg, QVTCPING_VALUE_ERROR); + data.worst = std::min(data.worst, QVTCPING_VALUE_ERROR); + data.best = std::min(data.best, QVTCPING_VALUE_ERROR); return data; } - int resolveHost(const string &host, int port, addrinfo **res) + int resolveHost(const std::string &host, int port, addrinfo **res) { if (isExiting) return 0; @@ -154,7 +154,7 @@ namespace Qv2ray::components::tcping hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = 0; - return getaddrinfo(host.c_str(), to_string(port).c_str(), &hints, res); + return getaddrinfo(host.c_str(), std::to_string(port).c_str(), &hints, res); } int testLatency(struct addrinfo *addr, std::chrono::system_clock::time_point *start, std::chrono::system_clock::time_point *end) diff --git a/src/components/plugins/toolbar/QvToolbar.cpp b/src/components/plugins/toolbar/QvToolbar.cpp index bcee2f4e..613ea5bc 100644 --- a/src/components/plugins/toolbar/QvToolbar.cpp +++ b/src/components/plugins/toolbar/QvToolbar.cpp @@ -11,169 +11,169 @@ namespace Qv2ray::components::plugins { void StopProcessingPlugins() { -#ifdef Q_OS_LINUX - _linux::StopMessageQThread(); -#endif -#ifdef Q_OS_WIN - _win::KillNamedPipeThread(); -#endif + //#ifdef Q_OS_LINUX + // //_linux::StopMessageQThread(); + //#endif + //#ifdef Q_OS_WIN + // //_win::KillNamedPipeThread(); + //#endif } /// Public Function - CALL ONLY ONCE - /// To start processing plugins' command. void StartProcessingPlugins() { -#ifdef Q_OS_LINUX - _linux::StartMessageQThread(); -#endif -#ifdef Q_OS_WIN - _win::StartNamedPipeThread(); -#endif + //#ifdef Q_OS_LINUX + // //_linux::StartMessageQThread(); + //#endif + //#ifdef Q_OS_WIN + // //_win::StartNamedPipeThread(); + //#endif } - QString GetAnswerToRequest(const QString &pchRequest) + QString GetAnswerToRequest(const QString &) { - auto req = pchRequest.trimmed(); - QString reply = "{}"; + // auto req = pchRequest.trimmed(); + // QString reply = "{}"; - if (req == "START") - { - emit ConnectionManager->RestartConnection(); - } - else if (req == "STOP") - { - emit ConnectionManager->StopConnection(); - } - else if (req == "RESTART") - { - emit ConnectionManager->RestartConnection(); - } + // if (req == "START") + // { + // emit ConnectionManager->RestartConnection(); + // } + // else if (req == "STOP") + // { + // emit ConnectionManager->StopConnection(); + // } + // else if (req == "RESTART") + // { + // emit ConnectionManager->RestartConnection(); + // } - auto BarConfig = GlobalConfig.toolBarConfig; + // auto BarConfig = GlobalConfig.toolBarConfig; - for (auto i = 0; i < BarConfig.Pages.size(); i++) - { - for (auto j = 0; j < BarConfig.Pages[i].Lines.size(); j++) - { -#define CL BarConfig.Pages[i].Lines[j] + // for (auto i = 0; i < BarConfig.Pages.size(); i++) + // { + // for (auto j = 0; j < BarConfig.Pages[i].Lines.size(); j++) + // { + //#define CL BarConfig.Pages[i].Lines[j] - switch (CL.ContentType) - { - case 0: - { - // Custom Text - // We do nothing... - break; - } + // switch (CL.ContentType) + // { + // case 0: + // { + // // Custom Text + // // We do nothing... + // break; + // } - case 101: - { - // Current Time - CL.Message = QTime().currentTime().toString("hh:mm:ss"); - break; - } + // case 101: + // { + // // Current Time + // CL.Message = QTime().currentTime().toString("hh:mm:ss"); + // break; + // } - case 102: - { - // Current Date - CL.Message = QDate().currentDate().toString("yyyy-MM-dd"); - break; - } + // case 102: + // { + // // Current Date + // CL.Message = QDate().currentDate().toString("yyyy-MM-dd"); + // break; + // } - case 103: - { - // Current Qv2ray Version - CL.Message = QV2RAY_VERSION_STRING; - break; - } + // case 103: + // { + // // Current Qv2ray Version + // CL.Message = QV2RAY_VERSION_STRING; + // break; + // } - case 104: - { - // Current Connection Name - CL.Message = GetDisplayName(KernelInstance->CurrentConnection()); - break; - } + // case 104: + // { + // // Current Connection Name + // CL.Message = GetDisplayName(KernelInstance->CurrentConnection()); + // break; + // } - case 105: - { - // Current Connection Status - CL.Message = KernelInstance->CurrentConnection() == NullConnectionId ? QObject::tr("Not connected") : - QObject::tr("Connected"); - break; - } + // case 105: + // { + // // Current Connection Status + // CL.Message = KernelInstance->CurrentConnection() == NullConnectionId ? QObject::tr("Not connected") : + // QObject::tr("Connected"); + // break; + // } - // case 201: - //{ - // // Total upload speed; - // CL.Message = FormatBytes(get<0>(GetConnectionUsageAmount())) + "/s"; - // break; - //} - // - // case 202: - //{ - // // Total download speed; - // CL.Message = FormatBytes(vinstance->getAllSpeedDown()) + "/s"; - // break; - //} - // - // case 203: - //{ - // // Upload speed for tag - // CL.Message = FormatBytes(vinstance->getTagSpeedUp(CL.Message)) + "/s"; - // break; - //} - // - // case 204: - //{ - // // Download speed for tag - // CL.Message = FormatBytes(vinstance->getTagSpeedDown(CL.Message)) + "/s"; - // break; - //} + // // case 201: + // //{ + // // // Total upload speed; + // // CL.Message = FormatBytes(get<0>(GetConnectionUsageAmount())) + "/s"; + // // break; + // //} + // // + // // case 202: + // //{ + // // // Total download speed; + // // CL.Message = FormatBytes(vinstance->getAllSpeedDown()) + "/s"; + // // break; + // //} + // // + // // case 203: + // //{ + // // // Upload speed for tag + // // CL.Message = FormatBytes(vinstance->getTagSpeedUp(CL.Message)) + "/s"; + // // break; + // //} + // // + // // case 204: + // //{ + // // // Download speed for tag + // // CL.Message = FormatBytes(vinstance->getTagSpeedDown(CL.Message)) + "/s"; + // // break; + // //} - case 301: - { - // Total Upload - CL.Message = FormatBytes(get<0>(GetConnectionUsageAmount(KernelInstance->CurrentConnection()))); - break; - } + // case 301: + // { + // // Total Upload + // CL.Message = FormatBytes(get<0>(GetConnectionUsageAmount(KernelInstance->CurrentConnection()))); + // break; + // } - case 302: - { - // Total download - CL.Message = FormatBytes(get<1>(GetConnectionUsageAmount(KernelInstance->CurrentConnection()))); - break; - } + // case 302: + // { + // // Total download + // CL.Message = FormatBytes(get<1>(GetConnectionUsageAmount(KernelInstance->CurrentConnection()))); + // break; + // } - // case 303: - //{ - // // Upload for tag - // CL.Message = FormatBytes(vinstance->getTagDataUp(CL.Message)); - // break; - //} - // - // case 304: - //{ - // // Download for tag - // CL.Message = FormatBytes(vinstance->getTagDataDown(CL.Message)); - // break; - //} + // // case 303: + // //{ + // // // Upload for tag + // // CL.Message = FormatBytes(vinstance->getTagDataUp(CL.Message)); + // // break; + // //} + // // + // // case 304: + // //{ + // // // Download for tag + // // CL.Message = FormatBytes(vinstance->getTagDataDown(CL.Message)); + // // break; + // //} - case 305: - { - // Connection latency - CL.Message = QSTRN(GetConnectionLatency(KernelInstance->CurrentConnection())) + " ms"; - break; - } - default: - { - CL.Message = "Not Implemented"; - break; - } - } - } - } -#undef CL - reply = JsonToString(BarConfig.toJson()); - return reply; + // case 305: + // { + // // Connection latency + // CL.Message = QSTRN(GetConnectionLatency(KernelInstance->CurrentConnection())) + " ms"; + // break; + // } + // default: + // { + // CL.Message = "Not Implemented"; + // break; + // } + // } + // } + // } + //#undef CL + // reply = JsonToString(BarConfig.toJson()); + return "Stopped Supporting"; } } // namespace Toolbar } // namespace Qv2ray::components::plugins diff --git a/src/components/proxy/QvProxyConfigurator.cpp b/src/components/proxy/QvProxyConfigurator.cpp index 2c24450b..f6fad937 100644 --- a/src/components/proxy/QvProxyConfigurator.cpp +++ b/src/components/proxy/QvProxyConfigurator.cpp @@ -1,5 +1,6 @@ #include "QvProxyConfigurator.hpp" +#include "base/Qv2rayBase.hpp" #include "common/QvHelpers.hpp" #include "components/plugins/QvPluginHost.hpp" #ifdef Q_OS_WIN diff --git a/src/core/CoreUtils.cpp b/src/core/CoreUtils.cpp index 791b5af5..bd6f1706 100644 --- a/src/core/CoreUtils.cpp +++ b/src/core/CoreUtils.cpp @@ -61,7 +61,7 @@ namespace Qv2ray::core } } - const tuple GetConnectionInfo(const ConnectionId &id, bool *status) + const std::tuple GetConnectionInfo(const ConnectionId &id, bool *status) { if (status != nullptr) *status = false; @@ -69,7 +69,7 @@ namespace Qv2ray::core return GetConnectionInfo(root, status); } - const tuple GetConnectionInfo(const CONFIGROOT &out, bool *status) + const std::tuple GetConnectionInfo(const CONFIGROOT &out, bool *status) { if (status != nullptr) *status = false; @@ -102,7 +102,7 @@ namespace Qv2ray::core return { QObject::tr("N/A"), QObject::tr("N/A"), 0 }; } - const tuple GetConnectionUsageAmount(const ConnectionId &id) + const std::tuple GetConnectionUsageAmount(const ConnectionId &id) { auto connection = ConnectionManager->GetConnectionMetaObject(id); return { connection.upLinkData, connection.downLinkData }; @@ -111,13 +111,13 @@ namespace Qv2ray::core uint64_t GetConnectionTotalData(const ConnectionId &id) { auto result = GetConnectionUsageAmount(id); - return get<0>(result) + get<1>(result); + return std::get<0>(result) + std::get<1>(result); } int64_t GetConnectionLatency(const ConnectionId &id) { auto connection = ConnectionManager->GetConnectionMetaObject(id); - return max(connection.latency, {}); + return std::max(connection.latency, {}); } const QString GetConnectionProtocolString(const ConnectionId &id) diff --git a/src/core/CoreUtils.hpp b/src/core/CoreUtils.hpp index 3a1f24ce..ca848c74 100644 --- a/src/core/CoreUtils.hpp +++ b/src/core/CoreUtils.hpp @@ -29,10 +29,10 @@ namespace Qv2ray::core // int64_t GetConnectionLatency(const ConnectionId &id); uint64_t GetConnectionTotalData(const ConnectionId &id); - const tuple GetConnectionUsageAmount(const ConnectionId &id); + const std::tuple GetConnectionUsageAmount(const ConnectionId &id); // - const tuple GetConnectionInfo(const ConnectionId &id, bool *status = nullptr); - const tuple GetConnectionInfo(const CONFIGROOT &out, bool *status = nullptr); + const std::tuple GetConnectionInfo(const ConnectionId &id, bool *status = nullptr); + const std::tuple GetConnectionInfo(const CONFIGROOT &out, bool *status = nullptr); // bool GetOutboundInfo(const OUTBOUND &out, QString *host, int *port, QString *protocol); bool IsComplexConfig(const CONFIGROOT &root); diff --git a/src/core/handler/ConfigHandler.cpp b/src/core/handler/ConfigHandler.cpp index 6d4c2112..f6c4de27 100644 --- a/src/core/handler/ConfigHandler.cpp +++ b/src/core/handler/ConfigHandler.cpp @@ -226,7 +226,7 @@ namespace Qv2ray::core::handlers return; } - const optional QvConfigHandler::RenameConnection(const ConnectionId &id, const QString &newName) + const std::optional QvConfigHandler::RenameConnection(const ConnectionId &id, const QString &newName) { CheckConnectionExistance(id); OnConnectionRenamed(id, connections[id].displayName, newName); @@ -235,7 +235,7 @@ namespace Qv2ray::core::handlers CHSaveConfigData(); return {}; } - const optional QvConfigHandler::DeleteConnection(const ConnectionId &id) + const std::optional QvConfigHandler::DeleteConnection(const ConnectionId &id) { CheckConnectionExistance(id); auto groupId = connections[id].groupId; @@ -266,7 +266,7 @@ namespace Qv2ray::core::handlers return tr("File does not exist."); } - const optional QvConfigHandler::MoveConnectionGroup(const ConnectionId &id, const GroupId &newGroupId) + const std::optional QvConfigHandler::MoveConnectionGroup(const ConnectionId &id, const GroupId &newGroupId) { CheckConnectionExistance(id); auto const oldgid = connections[id].groupId; @@ -297,7 +297,7 @@ namespace Qv2ray::core::handlers return {}; } - const optional QvConfigHandler::DeleteGroup(const GroupId &id) + const std::optional QvConfigHandler::DeleteGroup(const GroupId &id) { CheckGroupExistance(id); if (!groups.contains(id) || id == NullGroupId) @@ -333,7 +333,7 @@ namespace Qv2ray::core::handlers return {}; } - const optional QvConfigHandler::StartConnection(const ConnectionId &id) + const std::optional QvConfigHandler::StartConnection(const ConnectionId &id) { CheckConnectionExistance(id); connections[id].lastConnected = system_clock::to_time_t(system_clock::now()); @@ -420,7 +420,7 @@ namespace Qv2ray::core::handlers return id; } - const optional QvConfigHandler::RenameGroup(const GroupId &id, const QString &newName) + const std::optional QvConfigHandler::RenameGroup(const GroupId &id, const QString &newName) { CheckGroupExistance(id); if (!groups.contains(id)) @@ -433,10 +433,10 @@ namespace Qv2ray::core::handlers return {}; } - const tuple QvConfigHandler::GetSubscriptionData(const GroupId &id) const + const std::tuple QvConfigHandler::GetSubscriptionData(const GroupId &id) const { CheckGroupExistanceEx(id, {}); - tuple result; + std::tuple result; if (!groups[id].isSubscription) { @@ -501,7 +501,7 @@ namespace Qv2ray::core::handlers } // Anyway, we try our best to preserve the connection id. QMultiMap nameMap; - QMultiMap, ConnectionId> typeMap; + QMultiMap, ConnectionId> typeMap; for (const auto &conn : groups[id].connections) { nameMap.insertMulti(GetDisplayName(conn), conn); diff --git a/src/core/handler/ConfigHandler.hpp b/src/core/handler/ConfigHandler.hpp index c0e2b898..8e62f214 100644 --- a/src/core/handler/ConfigHandler.hpp +++ b/src/core/handler/ConfigHandler.hpp @@ -68,7 +68,7 @@ namespace Qv2ray::core::handlers const ConnectionId GetConnectionIdByDisplayName(const QString &displayName, const GroupId &group) const; // // Connectivity Operationss - const optional StartConnection(const ConnectionId &identifier); + const std::optional StartConnection(const ConnectionId &identifier); void StopConnection(); // const ConnectionId &id void RestartConnection(); bool IsConnected(const ConnectionId &id) const; @@ -77,9 +77,9 @@ namespace Qv2ray::core::handlers bool UpdateConnection(const ConnectionId &id, const CONFIGROOT &root, bool skipRestart = false); 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); + const std::optional DeleteConnection(const ConnectionId &id); + const std::optional RenameConnection(const ConnectionId &id, const QString &newName); + const std::optional MoveConnectionGroup(const ConnectionId &id, const GroupId &newGroupId); const ConnectionId CreateConnection(const QString &displayName, const GroupId &groupId, const CONFIGROOT &root, bool skipSaveConfig = false); // @@ -93,15 +93,15 @@ namespace Qv2ray::core::handlers // // Group Operations const GroupId CreateGroup(const QString displayName, bool isSubscription); - const optional DeleteGroup(const GroupId &id); - const optional RenameGroup(const GroupId &id, const QString &newName); + const std::optional DeleteGroup(const GroupId &id); + const std::optional RenameGroup(const GroupId &id, const QString &newName); // const optional DuplicateGroup(const GroupId &id); // // Subscriptions bool SetSubscriptionData(const GroupId &id, bool isSubscription, const QString &address = "", float updateInterval = -1); bool UpdateSubscription(const GroupId &id); // bool UpdateSubscriptionASync(const GroupId &id, bool useSystemProxy); - const tuple GetSubscriptionData(const GroupId &id) const; + const std::tuple GetSubscriptionData(const GroupId &id) const; signals: void OnKernelLogAvailable(const ConnectionId &id, const QString &log); diff --git a/src/core/kernel/V2rayKernelInteractions.cpp b/src/core/kernel/V2rayKernelInteractions.cpp index 5c365bc0..f2dcfc7c 100644 --- a/src/core/kernel/V2rayKernelInteractions.cpp +++ b/src/core/kernel/V2rayKernelInteractions.cpp @@ -193,7 +193,7 @@ namespace Qv2ray::core::kernel KernelStarted = false; } - optional V2rayKernelInstance::StartConnection(const CONFIGROOT &root) + std::optional V2rayKernelInstance::StartConnection(const CONFIGROOT &root) { if (KernelStarted) { diff --git a/src/core/kernel/V2rayKernelInteractions.hpp b/src/core/kernel/V2rayKernelInteractions.hpp index 2b3a1ff7..bd286772 100644 --- a/src/core/kernel/V2rayKernelInteractions.hpp +++ b/src/core/kernel/V2rayKernelInteractions.hpp @@ -21,7 +21,7 @@ namespace Qv2ray::core::kernel qulonglong getAllSpeedUp(); qulonglong getAllSpeedDown(); // - optional StartConnection(const CONFIGROOT &root); + std::optional StartConnection(const CONFIGROOT &root); void StopConnection(); bool KernelStarted = false; // diff --git a/src/core/settings/SettingsUpgrade.cpp b/src/core/settings/SettingsUpgrade.cpp index 135183a8..8879e071 100644 --- a/src/core/settings/SettingsUpgrade.cpp +++ b/src/core/settings/SettingsUpgrade.cpp @@ -3,6 +3,7 @@ // from old to newer versions of Qv2ray. // +#include "base/Qv2rayBase.hpp" #include "common/QvHelpers.hpp" #define UPGRADELOG(msg) LOG(MODULE_SETTINGS, " [" + QSTRN(fromVersion) + "-" + QSTRN(fromVersion + 1) + "] --> " + msg) diff --git a/src/main.cpp b/src/main.cpp index 8cc3dcdc..d76b82cc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -25,7 +25,7 @@ void signalHandler(int signum) { - cout << "Qv2ray: Interrupt signal (" << signum << ") received." << endl; + std::cout << "Qv2ray: Interrupt signal (" << signum << ") received." << std::endl; ExitQv2ray(); qApp->exit(-99); } @@ -182,9 +182,9 @@ int main(int argc, char *argv[]) case CommandLineOk: break; case CommandLineError: - cout << "Invalid command line arguments" << endl; - cout << errorMessage.toStdString() << endl; - cout << parser.Parser()->helpText().toStdString() << endl; + std::cout << "Invalid command line arguments" << std::endl; + std::cout << errorMessage.toStdString() << std::endl; + std::cout << parser.Parser()->helpText().toStdString() << std::endl; break; case CommandLineVersionRequested: @@ -193,7 +193,7 @@ int main(int argc, char *argv[]) LOG("QV2RAY_BUILD_EXTRA_INFO", QV2RAY_BUILD_EXTRA_INFO) return 0; - case CommandLineHelpRequested: cout << parser.Parser()->helpText().toStdString() << endl; return 0; + case CommandLineHelpRequested: std::cout << parser.Parser()->helpText().toStdString() << std::endl; return 0; } } #ifdef Q_OS_UNIX @@ -279,7 +279,7 @@ int main(int argc, char *argv[]) LOG(MODULE_INIT, "Qv2ray Start Time: " + QSTRN(QTime::currentTime().msecsSinceStartOfDay())) // #ifdef QT_DEBUG - cout << "WARNING: ========================= This is a debug build, many features are not stable enough. =========================" << endl; + std::cout << "WARNING: =================== This is a debug build, many features are not stable enough. ===================" << std::endl; #endif // // Qv2ray Initialize, find possible config paths and verify them. diff --git a/src/ui/editors/w_JsonEditor.cpp b/src/ui/editors/w_JsonEditor.cpp index e8a541d5..715f56c1 100644 --- a/src/ui/editors/w_JsonEditor.cpp +++ b/src/ui/editors/w_JsonEditor.cpp @@ -6,7 +6,7 @@ JsonEditor::JsonEditor(QJsonObject rootObject, QWidget *parent) : QDialog(parent { setupUi(this); QvMessageBusConnect(JsonEditor); - highlighter = make_unique(jsonEditor->document()); + highlighter = std::make_unique(jsonEditor->document()); // original = rootObject; final = rootObject; diff --git a/src/ui/editors/w_JsonEditor.hpp b/src/ui/editors/w_JsonEditor.hpp index 53b171d3..a51940d3 100644 --- a/src/ui/editors/w_JsonEditor.hpp +++ b/src/ui/editors/w_JsonEditor.hpp @@ -35,5 +35,5 @@ class JsonEditor QJsonObject original; QJsonObject final; // - unique_ptr highlighter; + std::unique_ptr highlighter; }; diff --git a/src/ui/editors/w_RoutesEditor.hpp b/src/ui/editors/w_RoutesEditor.hpp index 8b5d25ae..3dfbd0db 100644 --- a/src/ui/editors/w_RoutesEditor.hpp +++ b/src/ui/editors/w_RoutesEditor.hpp @@ -3,6 +3,7 @@ #include "ConnectionStyle.hpp" #include "Node.hpp" #include "NodeData.hpp" +#include "base/Qv2rayBase.hpp" #include "common/QvHelpers.hpp" #include "ui/messaging/QvMessageBus.hpp" #include "ui_w_RoutesEditor.h" diff --git a/src/ui/editors/w_RoutesEditor_extra.cpp b/src/ui/editors/w_RoutesEditor_extra.cpp index 59b9e49c..cddea2fd 100644 --- a/src/ui/editors/w_RoutesEditor_extra.cpp +++ b/src/ui/editors/w_RoutesEditor_extra.cpp @@ -17,7 +17,7 @@ void RouteEditor::AddInbound(INBOUND in) } in["tag"] = tag; - auto _nodeData = make_unique(make_shared(tag)); + auto _nodeData = std::make_unique(std::make_shared(tag)); auto &node = nodeScene->createNode(std::move(_nodeData)); auto pos = QPointF(); pos.setX(0 + GRAPH_GLOBAL_OFFSET_X); @@ -37,7 +37,7 @@ void RouteEditor::AddOutbound(OUTBOUND out) } out["tag"] = tag; - auto _nodeData = make_unique(make_shared(tag)); + auto _nodeData = std::make_unique(std::make_shared(tag)); auto pos = nodeGraphWidget->pos(); pos.setX(pos.x() + 850 + GRAPH_GLOBAL_OFFSET_X); pos.setY(pos.y() + outboundNodes.count() * 120 + GRAPH_GLOBAL_OFFSET_Y); @@ -76,7 +76,7 @@ void RouteEditor::AddRule(RuleObject rule) auto pos = nodeGraphWidget->pos(); pos.setX(pos.x() + 350 + GRAPH_GLOBAL_OFFSET_X); pos.setY(pos.y() + ruleNodes.count() * 120 + GRAPH_GLOBAL_OFFSET_Y); - auto _nodeData = make_unique(make_shared(rule.QV2RAY_RULE_TAG)); + auto _nodeData = std::make_unique(std::make_shared(rule.QV2RAY_RULE_TAG)); auto &node = nodeScene->createNode(std::move(_nodeData)); nodeScene->setNodePosition(node, pos); diff --git a/src/ui/models/InboundNodeModel.hpp b/src/ui/models/InboundNodeModel.hpp index 5d3e990b..cc859815 100644 --- a/src/ui/models/InboundNodeModel.hpp +++ b/src/ui/models/InboundNodeModel.hpp @@ -53,7 +53,7 @@ class QvInboundNodeModel : public NodeDataModel } void setData(const QString &data) { - _in = make_shared(data); + _in = std::make_shared(data); _label->setText(data); _label->adjustSize(); } diff --git a/src/ui/models/NodeModelsBase.hpp b/src/ui/models/NodeModelsBase.hpp index 687fbdf1..edfbdc41 100644 --- a/src/ui/models/NodeModelsBase.hpp +++ b/src/ui/models/NodeModelsBase.hpp @@ -5,6 +5,7 @@ #include "common/QvHelpers.hpp" #include +#include using QtNodes::NodeData; using QtNodes::NodeDataModel; diff --git a/src/ui/models/OutboundNodeModel.hpp b/src/ui/models/OutboundNodeModel.hpp index 780dee4e..4be5e695 100644 --- a/src/ui/models/OutboundNodeModel.hpp +++ b/src/ui/models/OutboundNodeModel.hpp @@ -47,16 +47,16 @@ class QvOutboundNodeModel : public NodeDataModel return _out; } - void setInData(shared_ptr, int) override + void setInData(std::shared_ptr, int) override { } - void setInData(vector>, int) override + void setInData(std::vector>, int) override { } void setData(const QString &data) { - _out = make_shared(data); + _out = std::make_shared(data); _label->setText(_out->GetOutbound()); _label->adjustSize(); } diff --git a/src/ui/models/RuleNodeModel.hpp b/src/ui/models/RuleNodeModel.hpp index 5839f6de..3362e81d 100644 --- a/src/ui/models/RuleNodeModel.hpp +++ b/src/ui/models/RuleNodeModel.hpp @@ -70,12 +70,12 @@ class QvRuleNodeDataModel : public NodeDataModel void setInData(std::shared_ptr, int) override { } - void setInData(vector>, int) override + void setInData(std::vector>, int) override { } void setData(const QString &data) { - _ruleTag = make_shared(data); + _ruleTag = std::make_shared(data); _label->setText(_ruleTag->GetRuleTag()); _label->adjustSize(); } @@ -103,6 +103,6 @@ class QvRuleNodeDataModel : public NodeDataModel NodeValidationState modelValidationState = NodeValidationState::Warning; QString modelValidationError = tr("Missing or incorrect inputs"); // - shared_ptr _ruleTag; + std::shared_ptr _ruleTag; QLabel *_label; }; diff --git a/src/ui/w_GroupManager.cpp b/src/ui/w_GroupManager.cpp index 2f7f456d..fef79eeb 100644 --- a/src/ui/w_GroupManager.cpp +++ b/src/ui/w_GroupManager.cpp @@ -192,7 +192,7 @@ QvMessageBusSlotImpl(GroupManager) } } -tuple GroupManager::GetSelectedConfig() +std::tuple GroupManager::GetSelectedConfig() { return { GetDisplayName(currentConnectionId), ConnectionManager->GetConnectionRoot(currentConnectionId) }; } diff --git a/src/ui/w_GroupManager.hpp b/src/ui/w_GroupManager.hpp index 1892ef90..a53ccb1f 100644 --- a/src/ui/w_GroupManager.hpp +++ b/src/ui/w_GroupManager.hpp @@ -17,7 +17,7 @@ class GroupManager public: explicit GroupManager(QWidget *parent = nullptr); ~GroupManager(); - tuple GetSelectedConfig(); + std::tuple GetSelectedConfig(); private: QvMessageBusSlotDecl; diff --git a/src/ui/w_MainWindow.cpp b/src/ui/w_MainWindow.cpp index b11c6ab6..af0d5c83 100644 --- a/src/ui/w_MainWindow.cpp +++ b/src/ui/w_MainWindow.cpp @@ -81,7 +81,7 @@ void MainWindow::MWAddConnectionItem_p(const ConnectionId &connection, const Gro MWAddGroupItem_p(groupId); } auto groupItem = groupNodes.value(groupId); - auto connectionItem = make_shared(QStringList{ + auto connectionItem = std::make_shared(QStringList{ "", // GetDisplayName(connection), // NumericString(GetConnectionLatency(connection)), // @@ -98,7 +98,7 @@ void MainWindow::MWAddConnectionItem_p(const ConnectionId &connection, const Gro void MainWindow::MWAddGroupItem_p(const GroupId &groupId) { - auto groupItem = make_shared(QStringList{ "", GetDisplayName(groupId) }); + auto groupItem = std::make_shared(QStringList{ "", GetDisplayName(groupId) }); groupNodes.insert(groupId, groupItem); connectionListWidget->addTopLevelItem(groupItem.get()); connectionListWidget->setItemWidget(groupItem.get(), 0, new ConnectionItemWidget(groupId, connectionListWidget)); @@ -543,11 +543,11 @@ void MainWindow::on_action_RCM_DeleteThese_triggered() { if (widget->IsConnection()) { - connlist.append(get<1>(widget->Identifier())); + connlist.append(std::get<1>(widget->Identifier())); } else { - connlist.append(ConnectionManager->GetGroupMetaObject(get<0>(widget->Identifier())).connections); + connlist.append(ConnectionManager->GetGroupMetaObject(std::get<0>(widget->Identifier())).connections); } } } @@ -587,7 +587,7 @@ void MainWindow::on_action_RCM_EditAsComplex_triggered() CheckCurrentWidget; if (widget->IsConnection()) { - auto id = get<1>(widget->Identifier()); + auto id = std::get<1>(widget->Identifier()); CONFIGROOT root = ConnectionManager->GetConnectionRoot(id); bool isChanged = false; // @@ -882,7 +882,7 @@ void MainWindow::on_action_RCM_DuplicateThese_triggered() auto widget = GetItemWidget(item); if (widget->IsConnection()) { - connlist.append(get<1>(widget->Identifier())); + connlist.append(std::get<1>(widget->Identifier())); } } @@ -905,13 +905,13 @@ void MainWindow::on_action_RCM_DuplicateThese_triggered() void MainWindow::on_action_RCM_EditThis_triggered() { CheckCurrentWidget; - OnEditRequested(get<1>(widget->Identifier())); + OnEditRequested(std::get<1>(widget->Identifier())); } void MainWindow::on_action_RCM_EditAsJson_triggered() { CheckCurrentWidget; - OnEditJsonRequested(get<1>(widget->Identifier())); + OnEditJsonRequested(std::get<1>(widget->Identifier())); } void MainWindow::on_chartVisibilityBtn_clicked() @@ -960,7 +960,7 @@ void MainWindow::on_action_RCM_SetAutoConnection_triggered() if (current != nullptr) { auto widget = GetItemWidget(current); - auto &conn = get<1>(widget->Identifier()); + auto &conn = std::get<1>(widget->Identifier()); GlobalConfig.autoStartId = conn.toString(); if (!GlobalConfig.uiConfig.quietMode) { @@ -979,9 +979,9 @@ void MainWindow::on_action_RCM_ClearUsage_triggered() if (widget) { if (widget->IsConnection()) - ConnectionManager->ClearConnectionUsage(get<1>(widget->Identifier())); + ConnectionManager->ClearConnectionUsage(std::get<1>(widget->Identifier())); else - ConnectionManager->ClearGroupUsage(get<0>(widget->Identifier())); + ConnectionManager->ClearGroupUsage(std::get<0>(widget->Identifier())); } } } @@ -995,9 +995,9 @@ void MainWindow::on_action_RCM_LatencyTest_triggered() if (widget) { if (widget->IsConnection()) - ConnectionManager->StartLatencyTest(get<1>(widget->Identifier())); + ConnectionManager->StartLatencyTest(std::get<1>(widget->Identifier())); else - ConnectionManager->StartLatencyTest(get<0>(widget->Identifier())); + ConnectionManager->StartLatencyTest(std::get<0>(widget->Identifier())); } } } diff --git a/src/ui/w_MainWindow.hpp b/src/ui/w_MainWindow.hpp index cacd61b2..b2f470ed 100644 --- a/src/ui/w_MainWindow.hpp +++ b/src/ui/w_MainWindow.hpp @@ -107,8 +107,8 @@ class MainWindow void closeEvent(QCloseEvent *) override; private: - QHash> groupNodes; - QHash> connectionNodes; + QHash> groupNodes; + QHash> connectionNodes; // Charts SpeedWidget *speedChartWidget; QSystemTrayIcon hTray; diff --git a/src/ui/w_ScreenShot_Core.cpp b/src/ui/w_ScreenShot_Core.cpp index 4f1ecb41..c5afd236 100644 --- a/src/ui/w_ScreenShot_Core.cpp +++ b/src/ui/w_ScreenShot_Core.cpp @@ -1,5 +1,6 @@ #include "w_ScreenShot_Core.hpp" +#include "base/Qv2rayBase.hpp" #include "common/QvHelpers.hpp" #include diff --git a/src/ui/widgets/ConnectionInfoWidget.cpp b/src/ui/widgets/ConnectionInfoWidget.cpp index 438f71d1..eb210a3c 100644 --- a/src/ui/widgets/ConnectionInfoWidget.cpp +++ b/src/ui/widgets/ConnectionInfoWidget.cpp @@ -57,10 +57,10 @@ ConnectionInfoWidget::ConnectionInfoWidget(QWidget *parent) : QWidget(parent) connect(ConnectionManager, &QvConfigHandler::OnConnectionGroupChanged, this, &ConnectionInfoWidget::OnConnectionModified); } -void ConnectionInfoWidget::ShowDetails(const tuple &_identifier) +void ConnectionInfoWidget::ShowDetails(const std::tuple &_identifier) { - this->groupId = get<0>(_identifier); - this->connectionId = get<1>(_identifier); + this->groupId = std::get<0>(_identifier); + this->connectionId = std::get<1>(_identifier); bool isConnection = connectionId != NullConnectionId; // editBtn->setEnabled(isConnection); @@ -110,7 +110,7 @@ void ConnectionInfoWidget::ShowDetails(const tuple &_iden } // groupShareTxt->setPlainText(shareLinks.join(NEWLINE)); - groupSubsLinkTxt->setText(ConnectionManager->IsSubscription(groupId) ? get<0>(ConnectionManager->GetSubscriptionData(groupId)) : + groupSubsLinkTxt->setText(ConnectionManager->IsSubscription(groupId) ? std::get<0>(ConnectionManager->GetSubscriptionData(groupId)) : tr("Not a subscription")); } } diff --git a/src/ui/widgets/ConnectionInfoWidget.hpp b/src/ui/widgets/ConnectionInfoWidget.hpp index c91f4dcd..8b75a8af 100644 --- a/src/ui/widgets/ConnectionInfoWidget.hpp +++ b/src/ui/widgets/ConnectionInfoWidget.hpp @@ -14,7 +14,7 @@ class ConnectionInfoWidget public: explicit ConnectionInfoWidget(QWidget *parent = nullptr); - void ShowDetails(const tuple &_identifier); + void ShowDetails(const std::tuple &_identifier); ~ConnectionInfoWidget(); signals: diff --git a/src/ui/widgets/ConnectionItemWidget.hpp b/src/ui/widgets/ConnectionItemWidget.hpp index d8bc87db..c55bbc00 100644 --- a/src/ui/widgets/ConnectionItemWidget.hpp +++ b/src/ui/widgets/ConnectionItemWidget.hpp @@ -39,7 +39,7 @@ class ConnectionItemWidget return headerMatched || GetDisplayName(connectionId).toLower().contains(searchString); } } - inline const tuple Identifier() const + inline const std::tuple Identifier() const { return { groupId, connectionId }; } diff --git a/src/ui/widgets/RouteSettingsMatrix.cpp b/src/ui/widgets/RouteSettingsMatrix.cpp index b39c2061..d8ad1d3d 100644 --- a/src/ui/widgets/RouteSettingsMatrix.cpp +++ b/src/ui/widgets/RouteSettingsMatrix.cpp @@ -121,7 +121,7 @@ void RouteSettingsMatrixWidget::on_importSchemeBtn_clicked() // done LOG(MODULE_SETTINGS, "Imported route config: " + scheme.name + " by: " + scheme.author) } - catch (exception &e) + catch (std::exception &e) { LOG(MODULE_UI, "Exception: " + QString(e.what())) // TODO: Give some error as Notification @@ -181,9 +181,8 @@ void RouteSettingsMatrixWidget::on_exportSchemeBtn_clicked() // TODO: Give some success as Notification QvMessageBoxInfo(this, dialogTitle, tr("Your route scheme has been successfully exported!")); } - catch (exception &) + catch (...) { - // TODO: Give some error as Notification } } From 7ea717e2492556f92cf58bed00611b729c2faf25 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Sun, 3 May 2020 13:59:55 +0800 Subject: [PATCH 031/385] add: try has std::byte NO_AUR NO_PACMAN NO_DEB --- CMakeLists.txt | 2 +- makespec/BUILDVERSION | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f0302ee..ebb3f24b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,7 +66,7 @@ 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) + add_definitions(-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) diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 6a454d92..9e52a003 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5363 \ No newline at end of file +5365 \ No newline at end of file From 6f358d7cb92eacfcfe45925419317fd3535d9b25 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Sun, 3 May 2020 15:47:41 +0800 Subject: [PATCH 032/385] refactor: some refactors --- makespec/BUILDVERSION | 2 +- src/base/Qv2rayBase.hpp | 6 ------ src/base/models/QvConfigIdentifier.hpp | 12 +++--------- src/core/connection/ConnectionIO.cpp | 12 ++++++------ 4 files changed, 10 insertions(+), 22 deletions(-) diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 9e52a003..2f114b59 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5365 \ No newline at end of file +5366 \ No newline at end of file diff --git a/src/base/Qv2rayBase.hpp b/src/base/Qv2rayBase.hpp index 7052357e..45c1fa61 100644 --- a/src/base/Qv2rayBase.hpp +++ b/src/base/Qv2rayBase.hpp @@ -113,12 +113,6 @@ using namespace Qv2ray::base::objects::transfer; #define QV2RAY_USE_FPROXY_KEY "_QV2RAY_USE_GLOBAL_FORWARD_PROXY_" -#define JSON_ROOT_TRY_REMOVE(obj) \ - if (root.contains(obj)) \ - { \ - root.remove(obj); \ - } - namespace Qv2ray { // Qv2ray runtime config diff --git a/src/base/models/QvConfigIdentifier.hpp b/src/base/models/QvConfigIdentifier.hpp index 0708951f..53bcdb5e 100644 --- a/src/base/models/QvConfigIdentifier.hpp +++ b/src/base/models/QvConfigIdentifier.hpp @@ -14,9 +14,7 @@ namespace Qv2ray::base QString displayName; QList connections; qint64 importDate; - GroupObject_Config() : displayName(), connections(), importDate() - { - } + GroupObject_Config() : displayName(), connections(), importDate(){}; JSONSTRUCT_REGISTER(GroupObject_Config, F(displayName, connections, importDate)) }; @@ -26,9 +24,7 @@ namespace Qv2ray::base QString address; qint64 lastUpdated; float updateInterval; - SubscriptionObject_Config() : address(""), lastUpdated(system_clock::to_time_t(system_clock::now())), updateInterval(10) - { - } + SubscriptionObject_Config() : address(""), lastUpdated(system_clock::to_time_t(system_clock::now())), updateInterval(10){}; JSONSTRUCT_REGISTER(SubscriptionObject_Config, F(lastUpdated, updateInterval, address), B(GroupObject_Config)) }; @@ -42,9 +38,7 @@ namespace Qv2ray::base qint64 downLinkData; ConnectionObject_Config() : displayName(), importDate(system_clock::to_time_t(system_clock::now())), lastConnected(), latency(QVTCPING_VALUE_NODATA), - upLinkData(0), downLinkData(0) - { - } + upLinkData(0), downLinkData(0){}; JSONSTRUCT_REGISTER(ConnectionObject_Config, F(displayName, importDate, lastConnected, latency, upLinkData, downLinkData)) }; } // namespace Qv2ray::base diff --git a/src/core/connection/ConnectionIO.cpp b/src/core/connection/ConnectionIO.cpp index 879784bd..90c00807 100644 --- a/src/core/connection/ConnectionIO.cpp +++ b/src/core/connection/ConnectionIO.cpp @@ -19,14 +19,14 @@ namespace Qv2ray::core::connection if (!importComplex) { - JSON_ROOT_TRY_REMOVE("inbounds") - JSON_ROOT_TRY_REMOVE("routing") + root.remove("inbounds"); + root.remove("routing"); } - JSON_ROOT_TRY_REMOVE("log") - JSON_ROOT_TRY_REMOVE("api") - JSON_ROOT_TRY_REMOVE("stats") - JSON_ROOT_TRY_REMOVE("dns") + root.remove("log"); + root.remove("api"); + root.remove("stats"); + root.remove("dns"); return root; } } // namespace ConnectionIO From eaf274a23205a5a44f429d6dc13e289eca0c8d02 Mon Sep 17 00:00:00 2001 From: ymshenyu Date: Sun, 3 May 2020 20:55:11 +0800 Subject: [PATCH 033/385] update .travis.yml --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 59026963..8ced4e91 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,7 @@ env: addons: snaps: - name: snapcraft - channel: stable + channel: edge confinement: classic - name: lxd channel: stable From fc69cfbf7b31271ed1f17cf7c006f7bb1a7fc9c5 Mon Sep 17 00:00:00 2001 From: ymshenyu Date: Sun, 3 May 2020 20:55:25 +0800 Subject: [PATCH 034/385] Revert "NYI: Revert "snap: testing new feature"" This reverts commit 6b12dd8d73ce732c5c1f8b4c34eebece909ea865. --- snap/snapcraft.yaml | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index c1959052..e3e60a72 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -84,7 +84,6 @@ 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 @@ -110,21 +109,12 @@ 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 +package-repositories: + - type: apt + ppa: ymshenyu/qv2ray-deps \ No newline at end of file From 45a74812d8102194d3bfd919382a27a247a3e00b Mon Sep 17 00:00:00 2001 From: ymshenyu Date: Sun, 3 May 2020 20:55:52 +0800 Subject: [PATCH 035/385] switch from core18 to core20 --- snap/snapcraft.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index e3e60a72..912fa340 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -1,5 +1,5 @@ name: qv2ray -base: core18 +base: core20 adopt-info: qv2ray icon: assets/icons/qv2ray.png From bdb22ff9e64bdcd4dd29df539e2a1640921b1737 Mon Sep 17 00:00:00 2001 From: ymshenyu Date: Sun, 3 May 2020 20:57:05 +0800 Subject: [PATCH 036/385] remove PPA --- snap/snapcraft.yaml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 912fa340..147b1286 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -113,8 +113,4 @@ parts: qt5-gtk-platform: plugin: nil stage-packages: - - qt5-gtk-platformtheme - -package-repositories: - - type: apt - ppa: ymshenyu/qv2ray-deps \ No newline at end of file + - qt5-gtk-platformtheme \ No newline at end of file From 00e012f19b9f5b4429a9b5e04f40fbbf1709770d Mon Sep 17 00:00:00 2001 From: ymshenyu Date: Sun, 3 May 2020 21:37:18 +0800 Subject: [PATCH 037/385] update snapcraft.yaml --- snap/snapcraft.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 147b1286..670baea5 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -70,7 +70,7 @@ parts: - libqt5network5 - libqt5widgets5 - libglib2.0-bin - configflags: + cmake-parameters: - -DCMAKE_INSTALL_PREFIX=/usr - -DCMAKE_BUILD_TYPE=Release - -GNinja From b911e0d335ea37f41fa328e53b09d04d773b9c63 Mon Sep 17 00:00:00 2001 From: DuckSoft Date: Sun, 3 May 2020 21:36:53 +0800 Subject: [PATCH 038/385] updating translations --- translations/en_US.ts | 128 ++++++++++++++++++++++++++++++------------ translations/ja_JP.ts | 111 +++++++++++++++++++++++++++++++++--- translations/ru_RU.ts | 126 +++++++++++++++++++++++++++++++---------- translations/zh_CN.ts | 111 +++++++++++++++++++++++++++++++++--- 4 files changed, 391 insertions(+), 85 deletions(-) diff --git a/translations/en_US.ts b/translations/en_US.ts index 9c04c324..c67ec840 100644 --- a/translations/en_US.ts +++ b/translations/en_US.ts @@ -115,6 +115,13 @@ + + ConnectionSettingsWidget + + Form + + + ConnectionWidget @@ -142,6 +149,45 @@ + + GroupManager + + Connection Management + + + + Reload Subscription + + + + Would you like to reload the subscription? + + + + Deleting a subscription + + + + All connections will be moved to default group, do you want to continue? + + + + Export Connection(s) + + + + Delete Connection(s) + + + + Copy to... + + + + Move to... + + + ImportConfigWindow @@ -516,6 +562,13 @@ + + InboundSettingsWidget + + Form + + + JsonEditor @@ -569,10 +622,6 @@ Qv2ray - - Add - - Preferences @@ -851,6 +900,10 @@ Groups + + Add Connection + + OutboundEditor @@ -1903,14 +1956,6 @@ But could damage your server if improperly used. Invalid ssd link: rc4-md5 encryption is not supported by v2ray-core - - Not connected - - - - Connected - - Custom Text @@ -2871,25 +2916,6 @@ Maybe you have downloaded the wrong core? - - SubscriptionEditor - - Reload Subscription - - - - Would you like to reload the subscription? - - - - Deleting a subscription - - - - All connections will be moved to default group, do you want to continue? - - - misc @@ -2951,10 +2977,6 @@ Maybe you have downloaded the wrong core? Remove Subscription - - Group Details - - Group Name @@ -2976,11 +2998,43 @@ Maybe you have downloaded the wrong core? - Connection List + Update Subscription Data - Update Subscription Data + Group Info + + + + Created At + + + + Connections + + + + Delete Selection + + + + Export Selection + + + + Subscription Settings + + + + This group is a subscription + + + + Connection Settings + + + + Route Settings diff --git a/translations/ja_JP.ts b/translations/ja_JP.ts index 7814bea0..4683fe89 100644 --- a/translations/ja_JP.ts +++ b/translations/ja_JP.ts @@ -150,6 +150,13 @@ + + ConnectionSettingsWidget + + Form + Form + + ConnectionWidget @@ -219,6 +226,45 @@ フローシーンファイル (*.flow) + + GroupManager + + Connection Management + 項目管理 + + + Reload Subscription + サブスクリプションを更新 + + + Would you like to reload the subscription? + サブスクリプションを更新しますか? + + + Deleting a subscription + サブスクリプションを削除する + + + All connections will be moved to default group, do you want to continue? + すべての項目がデフォルトグループに移動します。続行しますか? + + + Export Connection(s) + 項目をエクスポート + + + Delete Connection(s) + 項目を削除 + + + Copy to... + コピーして... + + + Move to... + 移動して... + + ImportConfigWindow @@ -645,6 +691,13 @@ このユーザーは既に存在します. + + InboundSettingsWidget + + Form + Form + + JsonEditor @@ -792,7 +845,7 @@ Add - 追加 + 追加 Preferences @@ -1314,6 +1367,10 @@ Groups グループ + + Add Connection + 項目を作成 + OutboundEditor @@ -2536,11 +2593,11 @@ But could damage your server if improperly used. Not connected - 接続されていません + 接続されていません Connected - 接続済み + 接続済み Disconnected @@ -3775,19 +3832,19 @@ Maybe you have downloaded the wrong core? SubscriptionEditor Reload Subscription - サブスクリプションを更新 + サブスクリプションを更新 Would you like to reload the subscription? - サブスクリプションを更新しますか? + サブスクリプションを更新しますか? Deleting a subscription - サブスクリプションを削除する + サブスクリプションを削除する All connections will be moved to default group, do you want to continue? - すべての項目がデフォルトグループに移動します。続行しますか? + すべての項目がデフォルトグループに移動します。続行しますか? @@ -3853,7 +3910,7 @@ Maybe you have downloaded the wrong core? Group Details - 詳細 + 詳細 Group Name @@ -3877,12 +3934,48 @@ Maybe you have downloaded the wrong core? Connection List - 項目リスト + 項目リスト Update Subscription Data サブスクリプションデータの更新 + + Group Info + グループ情報 + + + Created At + 作成日 + + + Connections + 項目 + + + Delete Selection + 選択したものを削除 + + + Export Selection + 選択したものをエクスポート + + + Subscription Settings + サブスクリプション設定 + + + This group is a subscription + このグループはサブスクリプションです。 + + + Connection Settings + 接続設定 + + + Route Settings + ルート設定 + w_PluginManager diff --git a/translations/ru_RU.ts b/translations/ru_RU.ts index f9b036a4..c075d080 100644 --- a/translations/ru_RU.ts +++ b/translations/ru_RU.ts @@ -134,6 +134,13 @@ + + ConnectionSettingsWidget + + Form + + + ConnectionWidget @@ -203,6 +210,45 @@ Файл сцены потока (*.flow) + + GroupManager + + Connection Management + + + + Reload Subscription + + + + Would you like to reload the subscription? + + + + Deleting a subscription + + + + All connections will be moved to default group, do you want to continue? + + + + Export Connection(s) + + + + Delete Connection(s) + + + + Copy to... + + + + Move to... + + + ImportConfigWindow @@ -629,6 +675,13 @@ Этот пользователь уже существует. + + InboundSettingsWidget + + Form + + + JsonEditor @@ -776,7 +829,7 @@ Add - Добавить + Добавить Preferences @@ -1294,6 +1347,10 @@ Groups + + Add Connection + + OutboundEditor @@ -2438,13 +2495,9 @@ But could damage your server if improperly used. Technical Details Технические детали - - Not connected - - Connected - Подключено + Подключено Disconnected @@ -3612,25 +3665,6 @@ Maybe you have downloaded the wrong core? Не удалось обработать результат из апстрима, проверьте ваш URL. - - SubscriptionEditor - - Reload Subscription - - - - Would you like to reload the subscription? - - - - Deleting a subscription - - - - All connections will be moved to default group, do you want to continue? - - - misc @@ -3692,10 +3726,6 @@ Maybe you have downloaded the wrong core? Remove Subscription Удалить подписку - - Group Details - - Group Name @@ -3718,12 +3748,48 @@ Maybe you have downloaded the wrong core? Connection List - Список подключений + Список подключений Update Subscription Data Обновить данные подписки + + Group Info + + + + Created At + + + + Connections + + + + Delete Selection + Удалить выделение + + + Export Selection + + + + Subscription Settings + + + + This group is a subscription + + + + Connection Settings + Настройки соединения + + + Route Settings + + w_PluginManager diff --git a/translations/zh_CN.ts b/translations/zh_CN.ts index 8b6137d3..bbe3d706 100644 --- a/translations/zh_CN.ts +++ b/translations/zh_CN.ts @@ -122,6 +122,13 @@ + + ConnectionSettingsWidget + + Form + 窗体 + + ConnectionWidget @@ -149,6 +156,45 @@ + + GroupManager + + Connection Management + 连接管理 + + + Reload Subscription + 更新订阅 + + + Would you like to reload the subscription? + 您要更新此订阅吗? + + + Deleting a subscription + 删除订阅 + + + All connections will be moved to default group, do you want to continue? + 本订阅中的所有连接都将移动到默认分组,您确定要继续吗? + + + Export Connection(s) + 导出连接 + + + Delete Connection(s) + 删除连接 + + + Copy to... + 复制到... + + + Move to... + 移动到... + + ImportConfigWindow @@ -555,6 +601,13 @@ 此用户已存在。 + + InboundSettingsWidget + + Form + 窗体 + + JsonEditor @@ -618,7 +671,7 @@ Add - 添加 + 添加 Preferences @@ -952,6 +1005,10 @@ Groups 分组 + + Add Connection + 添加连接 + OutboundEditor @@ -2270,11 +2327,11 @@ But could damage your server if improperly used. Not connected - 未连接 + 未连接 Connected - 已连接 + 已连接 Custom Text @@ -3317,19 +3374,19 @@ Maybe you have downloaded the wrong core? SubscriptionEditor Reload Subscription - 更新订阅 + 更新订阅 Would you like to reload the subscription? - 您要更新此订阅吗? + 您要更新此订阅吗? Deleting a subscription - 删除订阅 + 删除订阅 All connections will be moved to default group, do you want to continue? - 本订阅中的所有连接都将移动到默认分组,您确定要继续吗? + 本订阅中的所有连接都将移动到默认分组,您确定要继续吗? @@ -3395,7 +3452,7 @@ Maybe you have downloaded the wrong core? Group Details - 分组细节 + 分组细节 Group Name @@ -3419,12 +3476,48 @@ Maybe you have downloaded the wrong core? Connection List - 连接列表 + 连接列表 Update Subscription Data 更新订阅数据 + + Group Info + 分组信息 + + + Created At + 创建于 + + + Connections + 连接 + + + Delete Selection + 删除所选项 + + + Export Selection + 导出所选项 + + + Subscription Settings + 订阅设置 + + + This group is a subscription + 此分组是是一个订阅 + + + Connection Settings + 连接设置 + + + Route Settings + 路由设定 + w_PluginManager From 75c9b448dd2efd293cec3be3e68fcb5ccc8156ff Mon Sep 17 00:00:00 2001 From: DuckSoft Date: Sun, 3 May 2020 21:38:16 +0800 Subject: [PATCH 039/385] adopting urlsafe parsers --- src/common/QvHelpers.cpp | 25 +++++++++++++++++++++ src/common/QvHelpers.hpp | 4 ++++ src/core/connection/Serialization.cpp | 2 +- src/core/connection/Serialization_ss.cpp | 2 +- src/core/connection/Serialization_vmess.cpp | 2 +- 5 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/common/QvHelpers.cpp b/src/common/QvHelpers.cpp index f951357e..1c98e760 100644 --- a/src/common/QvHelpers.cpp +++ b/src/common/QvHelpers.cpp @@ -111,6 +111,31 @@ namespace Qv2ray::common return doc.object(); } + // backported from QvPlugin-SSR. + QString SafeBase64Encode(QString string) { + QByteArray ba = string.replace(QChar('-'), QChar('+')).replace(QChar('_'), QChar('/')).toUtf8(); + return QByteArray::fromBase64(ba, QByteArray::Base64Option::OmitTrailingEquals); + } + + // backported from QvPlugin-SSR. + QString SafeBase64Encode(const QString &string, bool trim) + { + QString base64 = string.toUtf8().toBase64(); + if (trim) + { + auto tmp = base64.replace(QChar('+'), QChar('-')).replace(QChar('/'), QChar('_')); + auto crbedin = tmp.crbegin(); + auto idx = tmp.length(); + while (crbedin != tmp.crend() && (*crbedin) == '=') idx -= 1, crbedin++; + return idx != tmp.length() ? tmp.remove(idx, tmp.length() - idx) : tmp; + } + else + { + return base64.replace(QChar('+'), QChar('-')).replace(QChar('/'), QChar('_')); + } + } + + QString Base64Encode(const QString &string) { QByteArray ba = string.toUtf8(); diff --git a/src/common/QvHelpers.hpp b/src/common/QvHelpers.hpp index c4214d22..a271678b 100644 --- a/src/common/QvHelpers.hpp +++ b/src/common/QvHelpers.hpp @@ -18,6 +18,10 @@ namespace Qv2ray::common { QStringList GetFileList(const QDir &dir); + + QString SafeBase64Decode(QString string); + QString SafeBase64Encode(const QString &string, bool trim); + QString Base64Encode(const QString &string); QString Base64Decode(const QString &string); QStringList SplitLines(const QString &str); diff --git a/src/core/connection/Serialization.cpp b/src/core/connection/Serialization.cpp index 56d2bc2c..291a7a01 100644 --- a/src/core/connection/Serialization.cpp +++ b/src/core/connection/Serialization.cpp @@ -122,7 +122,7 @@ namespace Qv2ray::core::connection // 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.contains("://") ? result : Base64Decode(result); + return result.contains("://") ? result : SafeBase64Decode(result); } } // namespace Serialization } // namespace Qv2ray::core::connection diff --git a/src/core/connection/Serialization_ss.cpp b/src/core/connection/Serialization_ss.cpp index 86259b59..3bcf4561 100644 --- a/src/core/connection/Serialization_ss.cpp +++ b/src/core/connection/Serialization_ss.cpp @@ -81,7 +81,7 @@ namespace Qv2ray::core::connection auto x = QUrl::fromUserInput(uri); server.address = x.host(); server.port = x.port(); - QString userInfo = Base64Decode(x.userName()); + QString userInfo = SafeBase64Decode(x.userName()); auto userInfoSp = userInfo.indexOf(':'); // DEBUG(MODULE_CONNECTION, "Userinfo splitter position: " + QSTRN(userInfoSp)) diff --git a/src/core/connection/Serialization_vmess.cpp b/src/core/connection/Serialization_vmess.cpp index 034f7247..ebcdded3 100644 --- a/src/core/connection/Serialization_vmess.cpp +++ b/src/core/connection/Serialization_vmess.cpp @@ -88,7 +88,7 @@ namespace Qv2ray::core::connection return default; } - auto vmessString = Base64Decode(b64Str); + auto vmessString = SafeBase64Decode(b64Str); auto jsonErr = VerifyJsonString(vmessString); if (!jsonErr.isEmpty()) From d1553a1f31789afee87ee2e199cf96c6b1478446 Mon Sep 17 00:00:00 2001 From: ymshenyu <39402395+ymshenyu@users.noreply.github.com> Date: Sun, 3 May 2020 21:41:04 +0800 Subject: [PATCH 040/385] Update w_PluginManager.cpp (#572) Co-authored-by: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> --- src/ui/w_PluginManager.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/ui/w_PluginManager.cpp b/src/ui/w_PluginManager.cpp index 19e46094..6fc51526 100644 --- a/src/ui/w_PluginManager.cpp +++ b/src/ui/w_PluginManager.cpp @@ -122,11 +122,7 @@ void PluginManageWindow::on_openPluginFolder_clicked() { pluginPath.mkpath(QV2RAY_CONFIG_DIR + "plugins/"); } -#ifdef Q_OS_LINUX - QProcess::execute("xdg-open", { pluginPath.absolutePath() }); -#else QDesktopServices::openUrl(QUrl::fromLocalFile(pluginPath.absolutePath())); -#endif } void PluginManageWindow::on_toolButton_clicked() From dacd98689b3aa5f70ca3d11cbfa5a1e79178d120 Mon Sep 17 00:00:00 2001 From: DuckSoft Date: Sun, 3 May 2020 21:44:29 +0800 Subject: [PATCH 041/385] fix typo --- src/common/QvHelpers.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/QvHelpers.cpp b/src/common/QvHelpers.cpp index 1c98e760..ea35a3df 100644 --- a/src/common/QvHelpers.cpp +++ b/src/common/QvHelpers.cpp @@ -112,7 +112,7 @@ namespace Qv2ray::common } // backported from QvPlugin-SSR. - QString SafeBase64Encode(QString string) { + QString SafeBase64Decode(QString string) { QByteArray ba = string.replace(QChar('-'), QChar('+')).replace(QChar('_'), QChar('/')).toUtf8(); return QByteArray::fromBase64(ba, QByteArray::Base64Option::OmitTrailingEquals); } From bdc6ea4688979053668a207d5bc19aea5750efd9 Mon Sep 17 00:00:00 2001 From: ymshenyu Date: Sun, 3 May 2020 22:01:32 +0800 Subject: [PATCH 042/385] update snapcraft.yaml --- snap/snapcraft.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 670baea5..60e41f38 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -57,7 +57,6 @@ parts: - libgrpc++-dev - libprotobuf-dev - protobuf-compiler-grpc - - ninja-build - pkg-config stage-packages: - libgcc1 @@ -73,7 +72,6 @@ parts: cmake-parameters: - -DCMAKE_INSTALL_PREFIX=/usr - -DCMAKE_BUILD_TYPE=Release - - -GNinja - -DEMBED_TRANSLATIONS=ON override-pull: | snapcraftctl pull From 28e4664f356c62a05b7ffb9ac6463e35d232e762 Mon Sep 17 00:00:00 2001 From: DuckSoft Date: Sun, 3 May 2020 22:27:07 +0800 Subject: [PATCH 043/385] adjusting preferencewindow 1. fixed the initial tab pos problem 2. fixed a typo on the preference window 3. added an explanation for network settings --- src/ui/w_PreferencesWindow.ui | 45 +++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/src/ui/w_PreferencesWindow.ui b/src/ui/w_PreferencesWindow.ui index f00a6b9d..87a1fe5c 100644 --- a/src/ui/w_PreferencesWindow.ui +++ b/src/ui/w_PreferencesWindow.ui @@ -33,7 +33,7 @@ - 3 + 0 @@ -274,28 +274,28 @@ Network Settings - + User-Agent - + - + Qv2ray Proxy - + @@ -320,14 +320,14 @@ - + - Curtom Proxy + Custom Proxy - + @@ -341,14 +341,7 @@ - - - - Curtom Proxy - - - - + @@ -375,6 +368,22 @@ + + + + + true + + + + These settings are for Qv2ray itself. +For example, for updating subscriptions. + + + Qt::PlainText + + + @@ -725,8 +734,8 @@ Custom DNS Settings 0 0 - 818 - 558 + 824 + 516 From 780bce46a14ed1fc21adf2add0e9850a107f03ef Mon Sep 17 00:00:00 2001 From: DuckSoft Date: Sun, 3 May 2020 22:30:37 +0800 Subject: [PATCH 044/385] updating translations --- translations/en_US.ts | 9 +++++---- translations/ja_JP.ts | 8 +++++++- translations/ru_RU.ts | 9 +++++---- translations/zh_CN.ts | 8 +++++++- 4 files changed, 24 insertions(+), 10 deletions(-) diff --git a/translations/en_US.ts b/translations/en_US.ts index c67ec840..50c7fdb9 100644 --- a/translations/en_US.ts +++ b/translations/en_US.ts @@ -1681,10 +1681,6 @@ But could damage your server if improperly used. Custom Proxy - - Curtom Proxy - - Network Options @@ -1733,6 +1729,11 @@ But could damage your server if improperly used. tproxy inbound's sniffing is enabled by default. + + These settings are for Qv2ray itself. +For example, for updating subscriptions. + + QObject diff --git a/translations/ja_JP.ts b/translations/ja_JP.ts index 4683fe89..2bef7e70 100644 --- a/translations/ja_JP.ts +++ b/translations/ja_JP.ts @@ -2394,7 +2394,7 @@ But could damage your server if improperly used. Curtom Proxy - カスタムプロキシ + カスタムプロキシ Network Options @@ -2444,6 +2444,12 @@ But could damage your server if improperly used. tproxy inbound's sniffing is enabled by default. tProxyインバウンドのスニッフィングはデフォルトで有効になっています。 + + These settings are for Qv2ray itself. +For example, for updating subscriptions. + これらの設定はQv2ray自体のためのものです。 +例えば、サブスクリプションの更新などです。 + QObject diff --git a/translations/ru_RU.ts b/translations/ru_RU.ts index c075d080..0a979c06 100644 --- a/translations/ru_RU.ts +++ b/translations/ru_RU.ts @@ -2296,10 +2296,6 @@ But could damage your server if improperly used. Custom Proxy - - Curtom Proxy - - Network Options @@ -2348,6 +2344,11 @@ But could damage your server if improperly used. tproxy inbound's sniffing is enabled by default. + + These settings are for Qv2ray itself. +For example, for updating subscriptions. + + QObject diff --git a/translations/zh_CN.ts b/translations/zh_CN.ts index bbe3d706..105023ed 100644 --- a/translations/zh_CN.ts +++ b/translations/zh_CN.ts @@ -2024,7 +2024,7 @@ But could damage your server if improperly used. Curtom Proxy - 自定义代理 + 自定义代理 Network Options @@ -2074,6 +2074,12 @@ But could damage your server if improperly used. tproxy inbound's sniffing is enabled by default. tProxy 入站的嗅探选项默认开启。 + + These settings are for Qv2ray itself. +For example, for updating subscriptions. + 这些设定是针对 Qv2ray 本身的。 +例如,用在更新订阅时。 + QObject From 38c86fddab969fc89c72a891f606caef51a57708 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Sun, 3 May 2020 23:26:44 +0800 Subject: [PATCH 045/385] Have sleep --- src/base/Qv2rayBase.hpp | 2 +- src/base/models/QvConfigIdentifier.hpp | 90 ++++++-- src/base/models/QvSettingsObject.hpp | 216 ++++++++---------- src/common/HTTPRequestHelper.cpp | 6 +- src/components/route/RouteSchemeIO.hpp | 4 +- .../route/presets/RouteScheme_V2rayN.hpp | 2 +- src/core/CoreSafeTypes.hpp | 102 +-------- src/core/connection/Serialization.cpp | 10 +- src/core/connection/Serialization.hpp | 17 +- src/core/connection/Serialization_ss.cpp | 4 +- src/core/connection/Serialization_ssd.cpp | 2 +- src/core/connection/Serialization_vmess.cpp | 4 +- src/core/handler/ConfigHandler.cpp | 127 +++++----- src/core/handler/ConfigHandler.hpp | 10 +- src/core/settings/SettingsBackend.cpp | 2 +- src/core/settings/SettingsBackend.hpp | 2 +- src/core/settings/SettingsUpgrade.cpp | 148 ++++++------ src/main.cpp | 4 +- src/ui/w_GroupManager.cpp | 4 +- src/ui/w_MainWindow_extra.cpp | 2 +- src/ui/w_PluginManager.cpp | 2 +- src/ui/w_PreferencesWindow.cpp | 12 +- src/ui/w_PreferencesWindow.hpp | 2 +- src/ui/widgets/RouteSettingsMatrix.cpp | 10 +- src/ui/widgets/RouteSettingsMatrix.hpp | 6 +- 25 files changed, 361 insertions(+), 429 deletions(-) diff --git a/src/base/Qv2rayBase.hpp b/src/base/Qv2rayBase.hpp index 45c1fa61..3dd2d7b5 100644 --- a/src/base/Qv2rayBase.hpp +++ b/src/base/Qv2rayBase.hpp @@ -118,7 +118,7 @@ namespace Qv2ray // Qv2ray runtime config inline bool isExiting = false; inline QString Qv2rayConfigPath = ""; - inline base::config::Qv2rayConfig GlobalConfig = base::config::Qv2rayConfig(); + inline base::config::Qv2rayConfigObject GlobalConfig = base::config::Qv2rayConfigObject(); // inline void ExitQv2ray() { diff --git a/src/base/models/QvConfigIdentifier.hpp b/src/base/models/QvConfigIdentifier.hpp index 53bcdb5e..d22ec49c 100644 --- a/src/base/models/QvConfigIdentifier.hpp +++ b/src/base/models/QvConfigIdentifier.hpp @@ -5,41 +5,95 @@ #include namespace Qv2ray::base { + template + class IDType final + { + public: + explicit IDType() : m_id("null"){}; + explicit IDType(const QString &id) : m_id(id){}; + friend bool operator==(const IDType &lhs, const IDType &rhs) + { + return lhs.m_id == rhs.m_id; + } + friend bool operator!=(const IDType &lhs, const IDType &rhs) + { + return lhs.toString() != rhs.toString(); + } + const QString &toString() const + { + return m_id; + } + uint qHash(uint seed) const + { + return ::qHash(m_id, seed); + } + + void loadJson(const QJsonValue &d) + { + m_id = d.toString("null"); + } + + QJsonValue toJson() const + { + return m_id; + } + + private: + QString m_id; + }; + + template + uint qHash(const IDType &key, uint seed = 0) + { + return key.qHash(seed); + } + // Define several safetypes to prevent misuse of QString. + class __QvGroup; + class __QvConnection; + typedef IDType<__QvGroup> GroupId; + typedef IDType<__QvConnection> ConnectionId; + // constexpr unsigned int QVTCPING_VALUE_ERROR = 99999; constexpr unsigned int QVTCPING_VALUE_NODATA = QVTCPING_VALUE_ERROR - 1; using namespace std::chrono; - // Common struct for Groups and Subscriptions - struct GroupObject_Config + + struct __Qv2rayConfigObjectBase { QString displayName; - QList connections; - qint64 importDate; - GroupObject_Config() : displayName(), connections(), importDate(){}; - JSONSTRUCT_REGISTER(GroupObject_Config, F(displayName, connections, importDate)) + qint64 creationDate; + qint64 lastUpdatedDate; + __Qv2rayConfigObjectBase() + : displayName(), creationDate(system_clock::to_time_t(system_clock::now())), // + lastUpdatedDate(system_clock::to_time_t(system_clock::now())) // + {}; + JSONSTRUCT_REGISTER(__Qv2rayConfigObjectBase, F(displayName, creationDate, lastUpdatedDate)) }; - struct SubscriptionObject_Config : GroupObject_Config + struct SubscriptionConfigObject { - // QString address; - qint64 lastUpdated; float updateInterval; - SubscriptionObject_Config() : address(""), lastUpdated(system_clock::to_time_t(system_clock::now())), updateInterval(10){}; - JSONSTRUCT_REGISTER(SubscriptionObject_Config, F(lastUpdated, updateInterval, address), B(GroupObject_Config)) + SubscriptionConfigObject() : address(""), updateInterval(10){}; + JSONSTRUCT_REGISTER(SubscriptionConfigObject, F(updateInterval, address)) }; - struct ConnectionObject_Config + struct Qv2rayGroupConfigObject : __Qv2rayConfigObjectBase + { + QList connections; + bool isSubscription; + SubscriptionConfigObject subscriptionSettings; + Qv2rayGroupConfigObject() : __Qv2rayConfigObjectBase(), connections(), isSubscription(false), subscriptionSettings(){}; + JSONSTRUCT_REGISTER(Qv2rayGroupConfigObject, F(connections, isSubscription, subscriptionSettings), B(__Qv2rayConfigObjectBase)) + }; + + struct Qv2rayConnectionObject : __Qv2rayConfigObjectBase { - QString displayName; - qint64 importDate; qint64 lastConnected; qint64 latency; qint64 upLinkData; qint64 downLinkData; - ConnectionObject_Config() - : displayName(), importDate(system_clock::to_time_t(system_clock::now())), lastConnected(), latency(QVTCPING_VALUE_NODATA), - upLinkData(0), downLinkData(0){}; - JSONSTRUCT_REGISTER(ConnectionObject_Config, F(displayName, importDate, lastConnected, latency, upLinkData, downLinkData)) + Qv2rayConnectionObject() : lastConnected(), latency(QVTCPING_VALUE_NODATA), upLinkData(0), downLinkData(0){}; + JSONSTRUCT_REGISTER(Qv2rayConnectionObject, F(lastConnected, latency, upLinkData, downLinkData), B(__Qv2rayConfigObjectBase)) }; } // namespace Qv2ray::base diff --git a/src/base/models/QvSettingsObject.hpp b/src/base/models/QvSettingsObject.hpp index 3ba65190..bd0f7ed1 100644 --- a/src/base/models/QvSettingsObject.hpp +++ b/src/base/models/QvSettingsObject.hpp @@ -5,7 +5,7 @@ #include -const int QV2RAY_CONFIG_VERSION = 11; +const int QV2RAY_CONFIG_VERSION = 12; namespace Qv2ray::base::config { @@ -19,9 +19,7 @@ namespace Qv2ray::base::config QString Message; QvBarLine() : Family("Consolas"), Bold(true), Italic(false), ColorA(255), ColorR(255), ColorG(255), ColorB(255), ContentType(0), Size(9), - Message("") - { - } + Message(""){}; JSONSTRUCT_REGISTER(QvBarLine, F(Bold, Italic, ColorA, ColorR, ColorG, ColorB, Size, Family, Message, ContentType)) }; @@ -29,19 +27,17 @@ namespace Qv2ray::base::config { int OffsetYpx; QList Lines; - QvBarPage() : OffsetYpx(5) - { - } + QvBarPage() : OffsetYpx(5){}; JSONSTRUCT_REGISTER(QvBarPage, F(OffsetYpx, Lines)) }; - struct Qv2rayToolBarConfig + struct Qv2rayConfig_ToolBar { QList Pages; - JSONSTRUCT_REGISTER(Qv2rayToolBarConfig, F(Pages)) + JSONSTRUCT_REGISTER(Qv2rayConfig_ToolBar, F(Pages)) }; - struct Qv2rayForwardProxyConfig + struct Qv2rayConfig_ForwardProxy { bool enableForwardProxy; QString type; @@ -50,14 +46,12 @@ namespace Qv2ray::base::config bool useAuth; QString username; QString password; - Qv2rayForwardProxyConfig() - : enableForwardProxy(false), type("http"), serverAddress("127.0.0.1"), port(8008), useAuth(false), username(), password() - { - } - JSONSTRUCT_REGISTER(Qv2rayForwardProxyConfig, F(enableForwardProxy, type, serverAddress, port, useAuth, username, password)) + Qv2rayConfig_ForwardProxy() + : enableForwardProxy(false), type("http"), serverAddress("127.0.0.1"), port(8008), useAuth(false), username(), password(){}; + JSONSTRUCT_REGISTER(Qv2rayConfig_ForwardProxy, F(enableForwardProxy, type, serverAddress, port, useAuth, username, password)) }; - struct Qv2rayInboundsConfig + struct Qv2rayConfig_Inbounds { QString listenip; bool setSystemProxy; @@ -88,30 +82,26 @@ namespace Qv2ray::base::config QString tproxy_mode; bool dnsIntercept; - Qv2rayInboundsConfig() + Qv2rayConfig_Inbounds() : listenip("127.0.0.1"), setSystemProxy(true), useSocks(true), socks_port(1089), socks_useAuth(false), socksUDP(true), socksLocalIP("127.0.0.1"), socksAccount(), socksSniffing(false), useHTTP(true), http_port(8889), http_useAuth(false), httpAccount(), httpSniffing(false), useTPROXY(false), tproxy_ip("127.0.0.1"), tproxy_port(12345), tproxy_use_tcp(true), - tproxy_use_udp(false), tproxy_followRedirect(true), tproxy_mode("tproxy"), dnsIntercept(true) - { - } + tproxy_use_udp(false), tproxy_followRedirect(true), tproxy_mode("tproxy"), dnsIntercept(true){}; - JSONSTRUCT_REGISTER(Qv2rayInboundsConfig, + JSONSTRUCT_REGISTER(Qv2rayConfig_Inbounds, F(setSystemProxy, listenip, useSocks, useHTTP, socks_port, socks_useAuth, socksAccount, socksSniffing, socksUDP, socksLocalIP, http_port, http_useAuth, httpAccount, httpSniffing, useTPROXY), F(tproxy_ip, tproxy_port, tproxy_use_tcp, tproxy_use_udp, tproxy_followRedirect, tproxy_mode, dnsIntercept)) }; - struct Qv2rayOutboundsConfig + struct Qv2rayConfig_Outbounds { int mark; - Qv2rayOutboundsConfig() : mark(255) - { - } - JSONSTRUCT_REGISTER(Qv2rayOutboundsConfig, F(mark)) + Qv2rayConfig_Outbounds() : mark(255){}; + JSONSTRUCT_REGISTER(Qv2rayConfig_Outbounds, F(mark)) }; - struct Qv2rayUIConfig + struct Qv2rayConfig_UI { QString theme; QString language; @@ -121,96 +111,88 @@ namespace Qv2ray::base::config bool useDarkTrayIcon; int maximumLogLines; int maxJumpListCount; - Qv2rayUIConfig() - : theme("Fusion"), language("en_US"), useDarkTheme(false), useDarkTrayIcon(true), maximumLogLines(500), maxJumpListCount(20) - { - } - JSONSTRUCT_REGISTER(Qv2rayUIConfig, + Qv2rayConfig_UI() + : theme("Fusion"), language("en_US"), useDarkTheme(false), useDarkTrayIcon(true), maximumLogLines(500), maxJumpListCount(20){}; + JSONSTRUCT_REGISTER(Qv2rayConfig_UI, F(theme, language, quietMode, useDarkTheme, useDarkTrayIcon, maximumLogLines, maxJumpListCount, recentConnections)) }; - struct Qv2rayRouteConfig_Impl + struct Qv2rayConfig_Routing { - QList direct; - QList block; - QList proxy; - Qv2rayRouteConfig_Impl(){}; - friend bool operator==(const Qv2rayRouteConfig_Impl &left, const Qv2rayRouteConfig_Impl &right) + struct Qv2rayRouteConfig_Impl { - return left.direct == right.direct && left.block == right.block && left.proxy == left.proxy; - } - Qv2rayRouteConfig_Impl(const QList &_direct, const QList &_block, const QList &_proxy) - : direct(_direct), block(_block), proxy(_proxy){}; - JSONSTRUCT_REGISTER(Qv2rayRouteConfig_Impl, F(proxy, block, direct)) - }; - - struct Qv2rayRouteConfig - { + QList direct; + QList block; + QList proxy; + Qv2rayRouteConfig_Impl(){}; + friend bool operator==(const Qv2rayRouteConfig_Impl &left, const Qv2rayRouteConfig_Impl &right) + { + return left.direct == right.direct && left.block == right.block && left.proxy == left.proxy; + } + Qv2rayRouteConfig_Impl(const QList &_direct, const QList &_block, const QList &_proxy) + : direct(_direct), block(_block), proxy(_proxy){}; + JSONSTRUCT_REGISTER(Qv2rayRouteConfig_Impl, F(proxy, block, direct)) + }; QString domainStrategy; Qv2rayRouteConfig_Impl domains; Qv2rayRouteConfig_Impl ips; - friend bool operator==(const Qv2rayRouteConfig &left, const Qv2rayRouteConfig &right) + friend bool operator==(const Qv2rayConfig_Routing &left, const Qv2rayConfig_Routing &right) { return left.domainStrategy == right.domainStrategy && left.domains == right.domains && left.ips == right.ips; } - Qv2rayRouteConfig(){}; - Qv2rayRouteConfig(const Qv2rayRouteConfig_Impl &_domains, const Qv2rayRouteConfig_Impl &_ips, const QString &ds) + Qv2rayConfig_Routing(){}; + Qv2rayConfig_Routing(const Qv2rayRouteConfig_Impl &_domains, const Qv2rayRouteConfig_Impl &_ips, const QString &ds) : domainStrategy(ds), domains(_domains), ips(_ips){}; - JSONSTRUCT_REGISTER(Qv2rayRouteConfig, F(domainStrategy, domains, ips)) + JSONSTRUCT_REGISTER(Qv2rayConfig_Routing, F(domainStrategy, domains, ips)) }; - struct Qv2rayPluginConfig + struct Qv2rayConfig_Plugin { QMap pluginStates; bool v2rayIntegration; int portAllocationStart; - Qv2rayPluginConfig() : pluginStates(), v2rayIntegration(true), portAllocationStart(15000){}; - JSONSTRUCT_REGISTER(Qv2rayPluginConfig, F(pluginStates, v2rayIntegration, portAllocationStart)) + Qv2rayConfig_Plugin() : pluginStates(), v2rayIntegration(true), portAllocationStart(15000){}; + JSONSTRUCT_REGISTER(Qv2rayConfig_Plugin, F(pluginStates, v2rayIntegration, portAllocationStart)) }; - struct Qv2rayConnectionConfig + struct Qv2rayConfig_Connection { bool bypassCN; bool bypassBT; bool enableProxy; bool v2rayFreedomDNS; bool withLocalDNS; - Qv2rayRouteConfig routeConfig; + Qv2rayConfig_Routing routeConfig; QList dnsList; - Qv2rayForwardProxyConfig forwardProxyConfig; - Qv2rayConnectionConfig() + Qv2rayConfig_ForwardProxy forwardProxyConfig; + Qv2rayConfig_Connection() : bypassCN(true), bypassBT(false), enableProxy(true), v2rayFreedomDNS(false), withLocalDNS(false), routeConfig(), - dnsList(QStringList{ "8.8.4.4", "1.1.1.1" }) - { - } - JSONSTRUCT_REGISTER(Qv2rayConnectionConfig, + dnsList(QStringList{ "8.8.4.4", "1.1.1.1" }){}; + JSONSTRUCT_REGISTER(Qv2rayConfig_Connection, F(bypassCN, bypassBT, enableProxy, v2rayFreedomDNS, withLocalDNS, dnsList, forwardProxyConfig, routeConfig)) }; - struct Qv2rayAPIConfig + struct Qv2rayConfig_API { bool enableAPI; int statsPort; - Qv2rayAPIConfig() : enableAPI(true), statsPort(15490) - { - } - JSONSTRUCT_REGISTER(Qv2rayAPIConfig, F(enableAPI, statsPort)) + Qv2rayConfig_API() : enableAPI(true), statsPort(15490){}; + JSONSTRUCT_REGISTER(Qv2rayConfig_API, F(enableAPI, statsPort)) }; - struct Qv2rayKernelConfig + struct Qv2rayConfig_Kernel { QString v2CorePath_linux; QString v2AssetsPath_linux; QString v2CorePath_macx; QString v2AssetsPath_macx; QString v2CorePath_win; - QString v2AssetsPath_win; // - Qv2rayKernelConfig() + QString v2AssetsPath_win; + Qv2rayConfig_Kernel() : v2CorePath_linux(), v2AssetsPath_linux(), // v2CorePath_macx(), v2AssetsPath_macx(), // v2CorePath_win(), v2AssetsPath_win() // - { - } + {}; // #ifdef Q_OS_LINUX #define _VARNAME_VCOREPATH_ v2CorePath_linux @@ -235,11 +217,11 @@ namespace Qv2ray::base::config #undef _VARNAME_VCOREPATH_ #undef _VARNAME_VASSETSPATH_ - JSONSTRUCT_REGISTER(Qv2rayKernelConfig, + JSONSTRUCT_REGISTER(Qv2rayConfig_Kernel, F(v2CorePath_linux, v2AssetsPath_linux, v2CorePath_macx, v2AssetsPath_macx, v2CorePath_win, v2AssetsPath_win)) }; - struct Qv2rayUpdateConfig + struct Qv2rayConfig_Update { QString ignoredVersion; /// @@ -247,18 +229,18 @@ namespace Qv2ray::base::config /// 0: Stable /// 1: Testing int updateChannel; - JSONSTRUCT_REGISTER(Qv2rayUpdateConfig, F(ignoredVersion, updateChannel)) + JSONSTRUCT_REGISTER(Qv2rayConfig_Update, F(ignoredVersion, updateChannel)) }; - struct Qv2rayAdvancedConfig + struct Qv2rayConfig_Advanced { bool setAllowInsecure; bool setAllowInsecureCiphers; bool testLatencyPeriodcally; - JSONSTRUCT_REGISTER(Qv2rayAdvancedConfig, F(setAllowInsecure, setAllowInsecureCiphers, testLatencyPeriodcally)) + JSONSTRUCT_REGISTER(Qv2rayConfig_Advanced, F(setAllowInsecure, setAllowInsecureCiphers, testLatencyPeriodcally)) }; - struct Qv2rayNetworkConfig + struct Qv2rayConfig_Network { enum Qv2rayProxyType : int { @@ -271,16 +253,16 @@ namespace Qv2ray::base::config QString type; int port; QString userAgent; - Qv2rayNetworkConfig() + Qv2rayConfig_Network() : proxyType(QVPROXY_NONE), // address("127.0.0.1"), // type("http"), // port(8000), // userAgent("Qv2ray/$VERSION WebRequestHelper"){}; - JSONSTRUCT_REGISTER(Qv2rayNetworkConfig, F(proxyType, type, address, port, userAgent)) + JSONSTRUCT_REGISTER(Qv2rayConfig_Network, F(proxyType, type, address, port, userAgent)) }; - struct Qv2rayConfig + struct Qv2rayConfigObject { int config_version; bool tProxySupport; @@ -289,31 +271,27 @@ namespace Qv2ray::base::config QString autoStartId; // // Key = groupId, connectionId - QMap groups; - QMap subscriptions; - /// Connections are used privately. - QMap connections; + QList groups; + QList connections; // - Qv2rayUIConfig uiConfig; - Qv2rayAPIConfig apiConfig; - Qv2rayPluginConfig pluginConfig; - Qv2rayKernelConfig kernelConfig; - Qv2rayUpdateConfig updateConfig; - Qv2rayNetworkConfig networkConfig; - Qv2rayToolBarConfig toolBarConfig; - Qv2rayInboundsConfig inboundConfig; - Qv2rayOutboundsConfig outboundConfig; - Qv2rayAdvancedConfig advancedConfig; - Qv2rayConnectionConfig connectionConfig; + Qv2rayConfig_UI uiConfig; + Qv2rayConfig_API apiConfig; + Qv2rayConfig_Plugin pluginConfig; + Qv2rayConfig_Kernel kernelConfig; + Qv2rayConfig_Update updateConfig; + Qv2rayConfig_Network networkConfig; + Qv2rayConfig_ToolBar toolBarConfig; + Qv2rayConfig_Inbounds inboundConfig; + Qv2rayConfig_Outbounds outboundConfig; + Qv2rayConfig_Advanced advancedConfig; + Qv2rayConfig_Connection connectionConfig; - Qv2rayConfig() + Qv2rayConfigObject() : config_version(QV2RAY_CONFIG_VERSION), // tProxySupport(false), // logLevel(), // autoStartId("null"), // groups(), // - subscriptions(), // - connections(), // uiConfig(), // apiConfig(), // pluginConfig(), // @@ -324,29 +302,25 @@ namespace Qv2ray::base::config inboundConfig(), // outboundConfig(), // advancedConfig(), // - connectionConfig() - { - } + connectionConfig(){}; - JSONSTRUCT_REGISTER(Qv2rayConfig, - F(config_version, // - tProxySupport, // - logLevel, // - uiConfig, // - pluginConfig, // - updateConfig, // - kernelConfig, // - networkConfig, // - groups, // - connections, // - subscriptions, // - autoStartId, // - inboundConfig, // - outboundConfig, // - connectionConfig), - F(toolBarConfig, // - advancedConfig, // - apiConfig // + JSONSTRUCT_REGISTER(Qv2rayConfigObject, + F(config_version, // + tProxySupport, // + logLevel, // + uiConfig, // + pluginConfig, // + updateConfig, // + kernelConfig, // + networkConfig, // + groups, // + autoStartId, // + inboundConfig, // + outboundConfig, // + connectionConfig, // + toolBarConfig, // + advancedConfig, // + apiConfig // )) }; } // namespace Qv2ray::base::config diff --git a/src/common/HTTPRequestHelper.cpp b/src/common/HTTPRequestHelper.cpp index bb37f48a..7295b2d2 100644 --- a/src/common/HTTPRequestHelper.cpp +++ b/src/common/HTTPRequestHelper.cpp @@ -26,18 +26,18 @@ namespace Qv2ray::common { switch (GlobalConfig.networkConfig.proxyType) { - case Qv2rayNetworkConfig::QVPROXY_NONE: + case Qv2rayConfig_Network::QVPROXY_NONE: { DEBUG(MODULE_NETWORK, "Get without proxy.") accessManager.setProxy(QNetworkProxy(QNetworkProxy::ProxyType::NoProxy)); break; } - case Qv2rayNetworkConfig::QVPROXY_SYSTEM: + case Qv2rayConfig_Network::QVPROXY_SYSTEM: { accessManager.setProxy(QNetworkProxyFactory::systemProxyForQuery().first()); break; } - case Qv2rayNetworkConfig::QVPROXY_CUSTOM: + case Qv2rayConfig_Network::QVPROXY_CUSTOM: { QNetworkProxy p{ GlobalConfig.networkConfig.type == "http" ? QNetworkProxy::HttpProxy : QNetworkProxy::Socks5Proxy, // diff --git a/src/components/route/RouteSchemeIO.hpp b/src/components/route/RouteSchemeIO.hpp index 53267313..07fa47a7 100644 --- a/src/components/route/RouteSchemeIO.hpp +++ b/src/components/route/RouteSchemeIO.hpp @@ -2,12 +2,12 @@ #include "base/models/QvSettingsObject.hpp" namespace Qv2ray::components::route { - const inline Qv2ray::base::config::Qv2rayRouteConfig emptyScheme; + const inline Qv2ray::base::config::Qv2rayConfig_Routing emptyScheme; /** * @brief The Qv2rayRouteScheme struct * @author DuckSoft */ - struct Qv2rayRouteScheme : config::Qv2rayRouteConfig + struct Qv2rayRouteScheme : config::Qv2rayConfig_Routing { /** * @brief the name of the scheme. diff --git a/src/components/route/presets/RouteScheme_V2rayN.hpp b/src/components/route/presets/RouteScheme_V2rayN.hpp index b3ae4ed5..ea378815 100644 --- a/src/components/route/presets/RouteScheme_V2rayN.hpp +++ b/src/components/route/presets/RouteScheme_V2rayN.hpp @@ -153,6 +153,6 @@ namespace Qv2ray::components::route::presets::v2rayN }; const inline QList IPsBlock{}; const inline QList IPsDirect{}; - const inline Qv2ray::base::config::Qv2rayRouteConfig v2rayNScheme({ DomainsDirect, DomainsBlock, DomainsProxy }, + const inline Qv2ray::base::config::Qv2rayConfig_Routing v2rayNScheme({ DomainsDirect, DomainsBlock, DomainsProxy }, { IPsDirect, IPsBlock, IPsProxy }, "AsIs"); } // namespace Qv2ray::components::route::presets::v2rayN diff --git a/src/core/CoreSafeTypes.hpp b/src/core/CoreSafeTypes.hpp index 283caa91..ebd2867b 100644 --- a/src/core/CoreSafeTypes.hpp +++ b/src/core/CoreSafeTypes.hpp @@ -8,43 +8,6 @@ namespace Qv2ray::core { - static const inline auto QV2RAY_SERIALIZATION_COMPLEX_CONFIG_PLACEHOLDER = QObject::tr("(Complex config)"); - template - class IDType final - { - public: - explicit IDType() : m_id("null") - { - } - explicit IDType(const QString &id) : m_id(id) - { - } - friend bool operator==(const IDType &lhs, const IDType &rhs) - { - return lhs.m_id == rhs.m_id; - } - friend bool operator!=(const IDType &lhs, const IDType &rhs) - { - return lhs.toString() != rhs.toString(); - } - const QString &toString() const - { - return m_id; - } - uint qHash(uint seed) const - { - return ::qHash(m_id, seed); - } - - private: - QString m_id; - }; - - // Define several safetypes to prevent misuse of QString. - class __QvGroup; - class __QvConnection; - typedef IDType<__QvGroup> GroupId; - typedef IDType<__QvConnection> ConnectionId; inline const static auto NullConnectionId = ConnectionId("null"); inline const static auto NullGroupId = GroupId("null"); @@ -53,12 +16,7 @@ namespace Qv2ray::core QList StringsToIdList(const QList &strings) { QList list; - - for (auto str : strings) - { - list << IDType(str); - } - + for (const auto &str : strings) list << IDType(str); return list; } @@ -66,65 +24,9 @@ namespace Qv2ray::core QList IdListToStrings(const QList &ids) { QList list; - - for (auto id : ids) - { - list << id.toString(); - } - + for (const auto &id : ids) list << id.toString(); return list; } - template - uint qHash(const IDType &key, uint seed = 0) - { - return key.qHash(seed); - } - // - /// Metadata object representing a connection. - struct ConnectionMetaObject final : ConnectionObject_Config - { - GroupId groupId = NullGroupId; - ConnectionMetaObject() : ConnectionObject_Config() - { - } - // Suger for down casting. - ConnectionMetaObject(const ConnectionObject_Config &base) : ConnectionMetaObject() - { - this->latency = base.latency; - this->lastConnected = base.lastConnected; - this->importDate = base.lastConnected; - this->upLinkData = base.upLinkData; - this->downLinkData = base.downLinkData; - this->displayName = base.displayName; - } - }; - - /// Metadata object representing a group. - struct GroupMetaObject final : SubscriptionObject_Config - { - // Implicit base of two types, since group object is actually the group base object. - bool isSubscription = false; - QList connections; - // Suger for down casting. - GroupMetaObject() : connections() - { - } - GroupMetaObject(const GroupObject_Config &base) : GroupMetaObject() - { - this->isSubscription = false; - this->displayName = base.displayName; - this->importDate = base.importDate; - this->connections = StringsToIdList(base.connections); - } - // Suger for down casting. - GroupMetaObject(const SubscriptionObject_Config &base) : GroupMetaObject((GroupObject_Config) base) - { - this->address = base.address; - this->lastUpdated = base.lastUpdated; - this->updateInterval = base.updateInterval; - this->isSubscription = true; - } - }; } // namespace Qv2ray::core using namespace Qv2ray::core; diff --git a/src/core/connection/Serialization.cpp b/src/core/connection/Serialization.cpp index 56d2bc2c..42f8e28f 100644 --- a/src/core/connection/Serialization.cpp +++ b/src/core/connection/Serialization.cpp @@ -15,7 +15,7 @@ namespace Qv2ray::core::connection QMultiHash connectionConf; if (link.startsWith("vmess://")) { - auto conf = ConvertConfigFromVMessString(link, prefix, errMessage); + auto conf = vmess::Deserialize(link, prefix, errMessage); // if (GlobalConfig.advancedConfig.setAllowInsecureCiphers || GlobalConfig.advancedConfig.setAllowInsecure) { @@ -36,13 +36,13 @@ namespace Qv2ray::core::connection } else if (link.startsWith("ss://")) { - auto conf = ConvertConfigFromSSString(link, prefix, errMessage); + auto conf = ss::Deserialize(link, prefix, errMessage); connectionConf.insert(*prefix, conf); } else if (link.startsWith("ssd://")) { QStringList errMessageList; - connectionConf = ConvertConfigFromSSDString(link, newGroupName, &errMessageList); + connectionConf = ssd::Deserialize(link, newGroupName, &errMessageList); *errMessage = errMessageList.join(NEWLINE); } else @@ -91,12 +91,12 @@ namespace Qv2ray::core::connection { auto vmessServer = VMessServerObject::fromJson(settings["vnext"].toArray().first().toObject()); auto transport = StreamSettingsObject::fromJson(outbound["streamSettings"].toObject()); - sharelink = vmess::ConvertConfigToVMessString(transport, vmessServer, alias); + sharelink = vmess::Serialize(transport, vmessServer, alias); } else if (type == "shadowsocks") { auto ssServer = ShadowSocksServerObject::fromJson(settings["servers"].toArray().first().toObject()); - sharelink = ss::ConvertConfigToSSString(ssServer, alias, isSip002); + sharelink = ss::Serialize(ssServer, alias, isSip002); } else { diff --git a/src/core/connection/Serialization.hpp b/src/core/connection/Serialization.hpp index 9db716ac..6000b77d 100644 --- a/src/core/connection/Serialization.hpp +++ b/src/core/connection/Serialization.hpp @@ -6,13 +6,14 @@ namespace Qv2ray::core::connection { namespace Serialization { + const inline auto QV2RAY_SERIALIZATION_COMPLEX_CONFIG_PLACEHOLDER = QObject::tr("(Complex config)"); /** * pattern for the nodes in ssd links. * %1: airport name * %2: node name * %3: rate */ - inline auto QV2RAY_SSD_DEFAULT_NAME_PATTERN = QObject::tr("%1 - %2 (rate %3)"); + const inline auto QV2RAY_SSD_DEFAULT_NAME_PATTERN = QObject::tr("%1 - %2 (rate %3)"); // // General QString DecodeSubscriptionString(const QByteArray &arr); @@ -24,16 +25,15 @@ namespace Qv2ray::core::connection // VMess URI Protocol namespace vmess { - CONFIGROOT ConvertConfigFromVMessString(const QString &vmess, QString *alias, QString *errMessage); - const QString ConvertConfigToVMessString(const StreamSettingsObject &transfer, const VMessServerObject &server, - const QString &alias); + CONFIGROOT Deserialize(const QString &vmess, QString *alias, QString *errMessage); + const QString Serialize(const StreamSettingsObject &transfer, const VMessServerObject &server, const QString &alias); } // namespace vmess // // SS URI Protocol namespace ss { - CONFIGROOT ConvertConfigFromSSString(const QString &ss, QString *alias, QString *errMessage); - const QString ConvertConfigToSSString(const ShadowSocksServerObject &server, const QString &alias, bool isSip002); + CONFIGROOT Deserialize(const QString &ss, QString *alias, QString *errMessage); + const QString Serialize(const ShadowSocksServerObject &server, const QString &alias, bool isSip002); } // namespace ss // // SSD URI Protocol @@ -50,7 +50,7 @@ namespace Qv2ray::core::connection * - log list * in case of error, no objects will be returned. */ - QMultiHash ConvertConfigFromSSDString(const QString &uri, QString *groupName, QStringList *logList); + QMultiHash Deserialize(const QString &uri, QString *groupName, QStringList *logList); } // namespace ssd // } // namespace Serialization @@ -59,6 +59,3 @@ namespace Qv2ray::core::connection using namespace Qv2ray::core; using namespace Qv2ray::core::connection; using namespace Qv2ray::core::connection::Serialization; -using namespace Qv2ray::core::connection::Serialization::ss; -using namespace Qv2ray::core::connection::Serialization::ssd; -using namespace Qv2ray::core::connection::Serialization::vmess; diff --git a/src/core/connection/Serialization_ss.cpp b/src/core/connection/Serialization_ss.cpp index 86259b59..a86df8a3 100644 --- a/src/core/connection/Serialization_ss.cpp +++ b/src/core/connection/Serialization_ss.cpp @@ -7,7 +7,7 @@ namespace Qv2ray::core::connection { namespace Serialization::ss { - CONFIGROOT ConvertConfigFromSSString(const QString &ssUri, QString *alias, QString *errMessage) + CONFIGROOT Deserialize(const QString &ssUri, QString *alias, QString *errMessage) { ShadowSocksServerObject server; QString d_name; @@ -107,7 +107,7 @@ namespace Qv2ray::core::connection return root; } - const QString ConvertConfigToSSString(const ShadowSocksServerObject &server, const QString &alias, bool isSip002) + const QString Serialize(const ShadowSocksServerObject &server, const QString &alias, bool isSip002) { auto myAlias = QUrl::toPercentEncoding(alias); diff --git a/src/core/connection/Serialization_ssd.cpp b/src/core/connection/Serialization_ssd.cpp index f207c3d4..6d2c9622 100644 --- a/src/core/connection/Serialization_ssd.cpp +++ b/src/core/connection/Serialization_ssd.cpp @@ -73,7 +73,7 @@ namespace Qv2ray::core::connection::Serialization // A pair of an error string list, together with some optionally existed pair, which contains a QString for airport name and a List of // pairs that contains a QString for connection name and finally, our ShadowSocksServerObject // - QMultiHash ConvertConfigFromSSDString(const QString &uri, QString *groupName, QStringList *logList) + QMultiHash Deserialize(const QString &uri, QString *groupName, QStringList *logList) { // ssd links should begin with "ssd://" if (!uri.startsWith("ssd://")) diff --git a/src/core/connection/Serialization_vmess.cpp b/src/core/connection/Serialization_vmess.cpp index 034f7247..7703d98e 100644 --- a/src/core/connection/Serialization_vmess.cpp +++ b/src/core/connection/Serialization_vmess.cpp @@ -10,7 +10,7 @@ namespace Qv2ray::core::connection { // From https://github.com/2dust/v2rayN/wiki/分享链接格式说明(ver-2) - const QString ConvertConfigToVMessString(const StreamSettingsObject &transfer, const VMessServerObject &server, const QString &alias) + const QString Serialize(const StreamSettingsObject &transfer, const VMessServerObject &server, const QString &alias) { QJsonObject vmessUriRoot; // Constant @@ -58,7 +58,7 @@ namespace Qv2ray::core::connection return "vmess://" + vmessPart; } // This generates global config containing only one outbound.... - CONFIGROOT ConvertConfigFromVMessString(const QString &vmessStr, QString *alias, QString *errMessage) + CONFIGROOT Deserialize(const QString &vmessStr, QString *alias, QString *errMessage) { #define default CONFIGROOT() LOG(MODULE_SETTINGS, "Trying to convert from a vmess string.") diff --git a/src/core/handler/ConfigHandler.cpp b/src/core/handler/ConfigHandler.cpp index f6c4de27..018beff9 100644 --- a/src/core/handler/ConfigHandler.cpp +++ b/src/core/handler/ConfigHandler.cpp @@ -12,65 +12,65 @@ 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), why? - for (auto i = 0; i < GlobalConfig.connections.count(); i++) - { - auto const &id = ConnectionId(GlobalConfig.connections.keys().at(i)); - connections[id] = GlobalConfig.connections.values().at(i); - } - - for (const auto &key : GlobalConfig.subscriptions.keys()) - { - GroupId gkey(key); - if (gkey == NullGroupId) - { - LOG(MODULE_CORE_HANDLER, "Removed a null subscription id") - continue; - } - auto const &val = GlobalConfig.subscriptions[key]; - groups[gkey] = val; - - for (auto conn : val.connections) - { - connections[ConnectionId(conn)].groupId = GroupId(key); - } - } - - for (const auto &key : GlobalConfig.groups.keys()) - { - GroupId gkey(key); - if (gkey == NullGroupId) - { - LOG(MODULE_CORE_HANDLER, "Removed a null group id") - continue; - } - auto const &val = GlobalConfig.groups.value(key); - groups[gkey] = val; - - for (auto conn : val.connections) - { - connections[ConnectionId(conn)].groupId = GroupId(key); - } - } - - for (const auto &id : connections.keys()) - { - DEBUG(MODULE_CORE_HANDLER, "Loading connection: " + connections.value(id).displayName + " to cache.") - auto const &group = connections.value(id).groupId; - if (group != NullGroupId) - { - auto path = group.toString() + "/" + id.toString() + QV2RAY_CONFIG_FILE_EXTENSION; - path.prepend(groups[group].isSubscription ? QV2RAY_SUBSCRIPTION_DIR : QV2RAY_CONNECTIONS_DIR); - // - connectionRootCache[id] = CONFIGROOT(JsonFromString(StringFromFile(path))); - } - else - { - connections.remove(id); - LOG(MODULE_CORE_HANDLER, "Dropped connection id: " + id.toString() + " since it's not in a group") - } - } + // // Do we need to check how many of them are loaded? + // // Do not use: for (const auto &key : connections), why? + // for (auto i = 0; i < GlobalConfig.connectionConfig.count(); i++) + // { + // auto const &id = ConnectionId(GlobalConfig.connections.keys().at(i)); + // connections[id] = GlobalConfig.connections.values().at(i); + // } + // + // for (const auto &key : GlobalConfig.subscriptions.keys()) + // { + // GroupId gkey(key); + // if (gkey == NullGroupId) + // { + // LOG(MODULE_CORE_HANDLER, "Removed a null subscription id") + // continue; + // } + // auto const &val = GlobalConfig.subscriptions[key]; + // groups[gkey] = val; + // + // for (auto conn : val.connections) + // { + // connections[ConnectionId(conn)].groupId = GroupId(key); + // } + // } + // + // for (const auto &key : GlobalConfig.groups ) + // { + // GroupId gkey(key); + // if (gkey == NullGroupId) + // { + // LOG(MODULE_CORE_HANDLER, "Removed a null group id") + // continue; + // } + // auto const &val = GlobalConfig.groups.value(key); + // groups[gkey] = val; + // + // for (auto conn : val.connections) + // { + // connections[ConnectionId(conn)].groupId = GroupId(key); + // } + // } + // + // for (const auto &id : connections.keys()) + // { + // DEBUG(MODULE_CORE_HANDLER, "Loading connection: " + connections.value(id).displayName + " to cache.") + // auto const &group = connections.value(id).groupId; + // if (group != NullGroupId) + // { + // auto path = group.toString() + "/" + id.toString() + QV2RAY_CONFIG_FILE_EXTENSION; + // path.prepend(groups[group].isSubscription ? QV2RAY_SUBSCRIPTION_DIR : QV2RAY_CONNECTIONS_DIR); + // // + // connectionRootCache[id] = CONFIGROOT(JsonFromString(StringFromFile(path))); + // } + // else + // { + // connections.remove(id); + // LOG(MODULE_CORE_HANDLER, "Dropped connection id: " + id.toString() + " since it's not in a group") + // } + // } // // Force default group name. groups[DefaultGroupId].displayName = tr("Default Group"); @@ -99,7 +99,6 @@ namespace Qv2ray::core::handlers auto &newGlobalConfig = GlobalConfig; newGlobalConfig.connections.clear(); newGlobalConfig.groups.clear(); - newGlobalConfig.subscriptions.clear(); for (auto i = 0; i < connections.count(); i++) { @@ -118,7 +117,7 @@ namespace Qv2ray::core::handlers } else { - GroupObject_Config o = groups.values()[i]; + Qv2rayGroupConfigObject o = groups.values()[i]; o.connections = connections; newGlobalConfig.groups[groups.keys()[i].toString()] = o; } @@ -413,7 +412,7 @@ namespace Qv2ray::core::handlers GroupId id(GenerateRandomString()); groups[id].displayName = displayName; groups[id].isSubscription = isSubscription; - groups[id].importDate = system_clock::to_time_t(system_clock::now()); + groups[id].creationDate = system_clock::to_time_t(system_clock::now()); PluginHost->Send_ConnectionEvent({ displayName, "", Events::ConnectionEntry::ConnectionEvent_Created }); emit OnGroupCreated(id, displayName); CHSaveConfigData(); @@ -443,7 +442,7 @@ namespace Qv2ray::core::handlers return result; } - return { groups[id].address, groups[id].lastUpdated, groups[id].updateInterval }; + return { groups[id].address, groups[id].lastUpdatedDate, groups[id].updateInterval }; } bool QvConfigHandler::SetSubscriptionData(const GroupId &id, bool isSubscription, const QString &address, float updateInterval) @@ -583,7 +582,7 @@ namespace Qv2ray::core::handlers } // Update the time - groups[id].lastUpdated = system_clock::to_time_t(system_clock::now()); + groups[id].lastUpdatedDate = system_clock::to_time_t(system_clock::now()); return hasErrorOccured; } diff --git a/src/core/handler/ConfigHandler.hpp b/src/core/handler/ConfigHandler.hpp index 8e62f214..432ffb91 100644 --- a/src/core/handler/ConfigHandler.hpp +++ b/src/core/handler/ConfigHandler.hpp @@ -38,16 +38,16 @@ namespace Qv2ray::core::handlers CheckGroupExistanceEx(groupId, {}); return groups[groupId].connections; } - inline const QList AllGroups() const + inline QList AllGroups() const { return groups.keys(); } - inline const ConnectionMetaObject GetConnectionMetaObject(const ConnectionId &id) const + inline const Qv2rayConnectionObject GetConnectionMetaObject(const ConnectionId &id) const { CheckConnectionExistanceEx(id, {}); return connections[id]; } - inline const GroupMetaObject GetGroupMetaObject(const GroupId &id) const + inline Qv2rayGroupConfigObject GetGroupMetaObject(const GroupId &id) const { CheckGroupExistanceEx(id, {}); return groups[id]; @@ -140,8 +140,8 @@ namespace Qv2ray::core::handlers int saveTimerId; int pingAllTimerId; int pingConnectionTimerId; - QHash groups; - QHash connections; + QHash groups; + QHash connections; QHash connectionRootCache; private: diff --git a/src/core/settings/SettingsBackend.cpp b/src/core/settings/SettingsBackend.cpp index 6cee454a..af39bfcb 100644 --- a/src/core/settings/SettingsBackend.cpp +++ b/src/core/settings/SettingsBackend.cpp @@ -4,7 +4,7 @@ namespace Qv2ray::core::config { - void SaveGlobalSettings(const Qv2rayConfig &conf) + void SaveGlobalSettings(const Qv2rayConfigObject &conf) { GlobalConfig = conf; SaveGlobalSettings(); diff --git a/src/core/settings/SettingsBackend.hpp b/src/core/settings/SettingsBackend.hpp index a1193869..2e68f805 100644 --- a/src/core/settings/SettingsBackend.hpp +++ b/src/core/settings/SettingsBackend.hpp @@ -4,7 +4,7 @@ namespace Qv2ray::core::config { void SaveGlobalSettings(); - void SaveGlobalSettings(const Qv2rayConfig &conf); + void SaveGlobalSettings(const Qv2rayConfigObject &conf); void SetConfigDirPath(const QString &path); bool CheckSettingsPathAvailability(const QString &_path, bool checkExistingConfig); } // namespace Qv2ray::core::config diff --git a/src/core/settings/SettingsUpgrade.cpp b/src/core/settings/SettingsUpgrade.cpp index 8879e071..6a611d9a 100644 --- a/src/core/settings/SettingsUpgrade.cpp +++ b/src/core/settings/SettingsUpgrade.cpp @@ -19,79 +19,79 @@ namespace Qv2ray // Cases 1, 2, and 3 are not supported anymore. // -------------------------------------------------------------------------------------- // Below is for Qv2ray version 2 - case 4: - { - // We changed the "proxyCN" to "bypassCN" as it's easier to - // understand.... - auto v2_oldProxyCN = root["proxyCN"].toBool(); - // - // From 3 to 4, we changed 'runAsRoot' to 'tProxySupport' - auto v3_oldrunAsRoot = root["runAsRoot"].toBool(); - root.insert("tProxySupport", v3_oldrunAsRoot); - UPGRADELOG("Upgrading runAsRoot to tProxySupport, the value is not changed: " + QSTRN(v3_oldrunAsRoot)) - // - QString path; - path = QV2RAY_DEFAULT_VCORE_PATH; - root["v2CorePath"] = path; - UPGRADELOG("Added v2CorePath to the config file.") - // - QJsonObject uiSettings; - uiSettings["language"] = root["language"].toString("en-US").replace("-", "_"); - root["uiConfig"] = uiSettings; - // - root["inboundConfig"] = root["inBoundSettings"]; - root.remove("inBoundSettings"); - UPGRADELOG("Renamed inBoundSettings to inboundConfig.") - // - // connectionConfig - QJsonObject o; - o["dnsList"] = root["dnsList"]; - o["withLocalDNS"] = root["withLocalDNS"]; - o["enableProxy"] = root["enableProxy"]; - o["bypassCN"] = !v2_oldProxyCN; - o["enableStats"] = true; - o["statsPort"] = 13459; - UPGRADELOG("Default statistics enabled.") - root["connectionConfig"] = o; - UPGRADELOG("Renamed some connection configs to connectionConfig.") - // - // Do we need renaming here? - // //auto inbound = root["inboundConfig"].toObject(); - // //auto pacConfig = inbound["pacConfig"].toObject(); - // //pacConfig["enablePAC"] = pacConfig["usePAC"].toBool(); - // //inbound["pacConfig"] = pacConfig; - // //root["inboundConfig"] = inbound; - // //UPDATELOG("Renamed usePAC to enablePAC.") - // - QJsonObject i; - i["connectionName"] = root["autoStartConfig"].toString(); - root["autoStartConfig"] = i; - UPGRADELOG("Added subscription feature to autoStartConfig.") - break; - } + // case 4: + // { + // // We changed the "proxyCN" to "bypassCN" as it's easier to + // // understand.... + // auto v2_oldProxyCN = root["proxyCN"].toBool(); + // // + // // From 3 to 4, we changed 'runAsRoot' to 'tProxySupport' + // auto v3_oldrunAsRoot = root["runAsRoot"].toBool(); + // root.insert("tProxySupport", v3_oldrunAsRoot); + // UPGRADELOG("Upgrading runAsRoot to tProxySupport, the value is not changed: " + QSTRN(v3_oldrunAsRoot)) + // // + // QString path; + // path = QV2RAY_DEFAULT_VCORE_PATH; + // root["v2CorePath"] = path; + // UPGRADELOG("Added v2CorePath to the config file.") + // // + // QJsonObject uiSettings; + // uiSettings["language"] = root["language"].toString("en-US").replace("-", "_"); + // root["uiConfig"] = uiSettings; + // // + // root["inboundConfig"] = root["inBoundSettings"]; + // root.remove("inBoundSettings"); + // UPGRADELOG("Renamed inBoundSettings to inboundConfig.") + // // + // // connectionConfig + // QJsonObject o; + // o["dnsList"] = root["dnsList"]; + // o["withLocalDNS"] = root["withLocalDNS"]; + // o["enableProxy"] = root["enableProxy"]; + // o["bypassCN"] = !v2_oldProxyCN; + // o["enableStats"] = true; + // o["statsPort"] = 13459; + // UPGRADELOG("Default statistics enabled.") + // root["connectionConfig"] = o; + // UPGRADELOG("Renamed some connection configs to connectionConfig.") + // // + // // Do we need renaming here? + // // //auto inbound = root["inboundConfig"].toObject(); + // // //auto pacConfig = inbound["pacConfig"].toObject(); + // // //pacConfig["enablePAC"] = pacConfig["usePAC"].toBool(); + // // //inbound["pacConfig"] = pacConfig; + // // //root["inboundConfig"] = inbound; + // // //UPDATELOG("Renamed usePAC to enablePAC.") + // // + // QJsonObject i; + // i["connectionName"] = root["autoStartConfig"].toString(); + // root["autoStartConfig"] = i; + // UPGRADELOG("Added subscription feature to autoStartConfig.") + // break; + // } - // Qv2ray version 2, RC 2 - case 5: - { - // Added subscription auto update - auto subs = root["subscribes"].toObject(); - root.remove("subscribes"); - QJsonObject newSubscriptions; + // // Qv2ray version 2, RC 2 + // case 5: + // { + // // Added subscription auto update + // auto subs = root["subscribes"].toObject(); + // root.remove("subscribes"); + // QJsonObject newSubscriptions; - for (auto item = subs.begin(); item != subs.end(); item++) - { - auto key = item.key(); - SubscriptionObject_Config _conf; - _conf.address = item.value().toString(); - _conf.lastUpdated = system_clock::to_time_t(system_clock::now()); - _conf.updateInterval = 5; - newSubscriptions[key] = _conf.toJson(); - } + // for (auto item = subs.begin(); item != subs.end(); item++) + // { + // auto key = item.key(); + // Qv2rayGroupConfigObject _conf; + // _conf..address = item.value().toString(); + // _conf.lastUpdated = system_clock::to_time_t(system_clock::now()); + // _conf.updateInterval = 5; + // newSubscriptions[key] = _conf.toJson(); + // } - root["subscriptions"] = newSubscriptions; - UPGRADELOG("Added subscription renewal options.") - break; - } + // root["subscriptions"] = newSubscriptions; + // UPGRADELOG("Added subscription renewal options.") + // break; + // } // Qv2ray version 2, RC 4 case 6: @@ -286,6 +286,12 @@ namespace Qv2ray } break; } + + // Splitted Qv2ray.conf, + case 11: + { + // + } default: { // @@ -301,7 +307,7 @@ namespace Qv2ray qApp->exit(1); } } - + abort(); root["config_version"] = root["config_version"].toInt() + 1; return root; } diff --git a/src/main.cpp b/src/main.cpp index d76b82cc..068ad6bc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -136,7 +136,7 @@ bool initialiseQv2ray() return false; } - Qv2rayConfig conf; + Qv2rayConfigObject conf; conf.kernelConfig.KernelPath(QString(QV2RAY_DEFAULT_VCORE_PATH)); conf.kernelConfig.AssetsPath(QString(QV2RAY_DEFAULT_VASSETS_PATH)); conf.logLevel = 3; @@ -312,7 +312,7 @@ int main(int argc, char *argv[]) } // Load config object from upgraded config QJsonObject - auto confObject = Qv2rayConfig::fromJson(conf); + auto confObject = Qv2rayConfigObject::fromJson(conf); if (confObject.uiConfig.language.isEmpty()) { diff --git a/src/ui/w_GroupManager.cpp b/src/ui/w_GroupManager.cpp index fef79eeb..0bf686bb 100644 --- a/src/ui/w_GroupManager.cpp +++ b/src/ui/w_GroupManager.cpp @@ -266,8 +266,8 @@ void GroupManager::on_groupList_itemClicked(QListWidgetItem *item) const auto &groupMetaObject = ConnectionManager->GetGroupMetaObject(currentGroupId); groupIsSubscriptionGroup->setChecked(groupMetaObject.isSubscription); subAddrTxt->setText(groupMetaObject.address); - lastUpdatedLabel->setText(timeToString(groupMetaObject.lastUpdated)); - createdAtLabel->setText(timeToString(groupMetaObject.importDate)); + lastUpdatedLabel->setText(timeToString(groupMetaObject.lastUpdatedDate)); + createdAtLabel->setText(timeToString(groupMetaObject.creationDate)); updateIntervalSB->setValue(groupMetaObject.updateInterval); // connectionsList->clear(); diff --git a/src/ui/w_MainWindow_extra.cpp b/src/ui/w_MainWindow_extra.cpp index da957fab..ff3117f6 100644 --- a/src/ui/w_MainWindow_extra.cpp +++ b/src/ui/w_MainWindow_extra.cpp @@ -52,7 +52,7 @@ void MainWindow::CheckSubscriptionsUpdate() if (info.updateInterval == 0) continue; // - const auto &lastRenewDate = QDateTime::fromTime_t(info.lastUpdated); + const auto &lastRenewDate = QDateTime::fromTime_t(info.lastUpdatedDate); const auto &renewTime = lastRenewDate.addSecs(info.updateInterval * 86400); LOG(MODULE_SUBSCRIPTION, // "Subscription \"" + info.displayName + "\": " + // diff --git a/src/ui/w_PluginManager.cpp b/src/ui/w_PluginManager.cpp index 19e46094..03ddbe66 100644 --- a/src/ui/w_PluginManager.cpp +++ b/src/ui/w_PluginManager.cpp @@ -123,7 +123,7 @@ void PluginManageWindow::on_openPluginFolder_clicked() pluginPath.mkpath(QV2RAY_CONFIG_DIR + "plugins/"); } #ifdef Q_OS_LINUX - QProcess::execute("xdg-open", { pluginPath.absolutePath() }); + QProcess::execute("xdg-open", { "\"" + pluginPath.absolutePath() + "\"" }); #else QDesktopServices::openUrl(QUrl::fromLocalFile(pluginPath.absolutePath())); #endif diff --git a/src/ui/w_PreferencesWindow.cpp b/src/ui/w_PreferencesWindow.cpp index a83ae8a9..4dca102d 100644 --- a/src/ui/w_PreferencesWindow.cpp +++ b/src/ui/w_PreferencesWindow.cpp @@ -155,17 +155,17 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent), Current qvNetworkUATxt->setText(CurrentConfig.networkConfig.userAgent); switch (CurrentConfig.networkConfig.proxyType) { - case Qv2rayNetworkConfig::QVPROXY_NONE: + case Qv2rayConfig_Network::QVPROXY_NONE: { qvProxyNoProxy->setChecked(true); break; } - case Qv2rayNetworkConfig::QVPROXY_SYSTEM: + case Qv2rayConfig_Network::QVPROXY_SYSTEM: { qvProxySystemProxy->setChecked(true); break; } - case Qv2rayNetworkConfig::QVPROXY_CUSTOM: + case Qv2rayConfig_Network::QVPROXY_CUSTOM: { qvProxyCustomProxy->setChecked(true); break; @@ -1300,17 +1300,17 @@ void PreferencesWindow::on_dnsIntercept_toggled(bool checked) void PreferencesWindow::on_qvProxyCustomProxy_clicked() { - CurrentConfig.networkConfig.proxyType = Qv2rayNetworkConfig::QVPROXY_CUSTOM; + CurrentConfig.networkConfig.proxyType = Qv2rayConfig_Network::QVPROXY_CUSTOM; } void PreferencesWindow::on_qvProxySystemProxy_clicked() { - CurrentConfig.networkConfig.proxyType = Qv2rayNetworkConfig::QVPROXY_SYSTEM; + CurrentConfig.networkConfig.proxyType = Qv2rayConfig_Network::QVPROXY_SYSTEM; } void PreferencesWindow::on_qvProxyNoProxy_clicked() { - CurrentConfig.networkConfig.proxyType = Qv2rayNetworkConfig::QVPROXY_NONE; + CurrentConfig.networkConfig.proxyType = Qv2rayConfig_Network::QVPROXY_NONE; } void PreferencesWindow::on_DnsFreedomCb_stateChanged(int arg1) diff --git a/src/ui/w_PreferencesWindow.hpp b/src/ui/w_PreferencesWindow.hpp index 962d6520..870d8767 100644 --- a/src/ui/w_PreferencesWindow.hpp +++ b/src/ui/w_PreferencesWindow.hpp @@ -223,5 +223,5 @@ class PreferencesWindow // bool NeedRestart = false; bool finishedLoading = false; - Qv2rayConfig CurrentConfig; + Qv2rayConfigObject CurrentConfig; }; diff --git a/src/ui/widgets/RouteSettingsMatrix.cpp b/src/ui/widgets/RouteSettingsMatrix.cpp index d8ad1d3d..7ebc877b 100644 --- a/src/ui/widgets/RouteSettingsMatrix.cpp +++ b/src/ui/widgets/RouteSettingsMatrix.cpp @@ -48,7 +48,7 @@ QList RouteSettingsMatrixWidget::getBuiltInSchemes() return list; } -QAction *RouteSettingsMatrixWidget::schemeToAction(const QString &name, const Qv2ray::base::config::Qv2rayRouteConfig &scheme) +QAction *RouteSettingsMatrixWidget::schemeToAction(const QString &name, const Qv2ray::base::config::Qv2rayConfig_Routing &scheme) { QAction *action = new QAction(this); action->setText(name); @@ -56,7 +56,7 @@ QAction *RouteSettingsMatrixWidget::schemeToAction(const QString &name, const Qv return action; } -void RouteSettingsMatrixWidget::SetRouteConfig(const Qv2rayRouteConfig &conf) +void RouteSettingsMatrixWidget::SetRouteConfig(const Qv2rayConfig_Routing &conf) { domainStrategyCombo->setCurrentText(conf.domainStrategy); // @@ -69,9 +69,9 @@ void RouteSettingsMatrixWidget::SetRouteConfig(const Qv2rayRouteConfig &conf) proxyIPTxt->setText(conf.ips.proxy.join(NEWLINE)); } -Qv2rayRouteConfig RouteSettingsMatrixWidget::GetRouteConfig() const +Qv2rayConfig_Routing RouteSettingsMatrixWidget::GetRouteConfig() const { - config::Qv2rayRouteConfig conf; + config::Qv2rayConfig_Routing conf; conf.domainStrategy = this->domainStrategyCombo->currentText(); conf.domains.block = SplitLines(blockDomainTxt->toPlainText().replace(" ", "")); conf.domains.direct = SplitLines(directDomainTxt->toPlainText().replace(" ", "")); @@ -116,7 +116,7 @@ void RouteSettingsMatrixWidget::on_importSchemeBtn_clicked() return; // write the scheme onto the window - this->SetRouteConfig(static_cast(scheme)); + this->SetRouteConfig(static_cast(scheme)); // done LOG(MODULE_SETTINGS, "Imported route config: " + scheme.name + " by: " + scheme.author) diff --git a/src/ui/widgets/RouteSettingsMatrix.hpp b/src/ui/widgets/RouteSettingsMatrix.hpp index 248f099a..1bd1d049 100644 --- a/src/ui/widgets/RouteSettingsMatrix.hpp +++ b/src/ui/widgets/RouteSettingsMatrix.hpp @@ -15,15 +15,15 @@ class RouteSettingsMatrixWidget public: RouteSettingsMatrixWidget(const QString &assetsDirPath, QWidget *parent = nullptr); - void SetRouteConfig(const Qv2ray::base::config::Qv2rayRouteConfig &conf); - Qv2ray::base::config::Qv2rayRouteConfig GetRouteConfig() const; + void SetRouteConfig(const Qv2ray::base::config::Qv2rayConfig_Routing &conf); + Qv2ray::base::config::Qv2rayConfig_Routing GetRouteConfig() const; ~RouteSettingsMatrixWidget(); private: std::optional openFileDialog(); std::optional saveFileDialog(); QList getBuiltInSchemes(); - QAction *schemeToAction(const QString &name, const Qv2ray::base::config::Qv2rayRouteConfig &scheme); + QAction *schemeToAction(const QString &name, const Qv2ray::base::config::Qv2rayConfig_Routing &scheme); private: QMenu *builtInSchemesMenu; From ebe6736bba4903459e7d09290b0b029eb0e720a6 Mon Sep 17 00:00:00 2001 From: ymshenyu Date: Sun, 3 May 2020 23:27:14 +0800 Subject: [PATCH 046/385] allow fallback to xdg-open --- CMakeLists.txt | 4 ++++ snap/snapcraft.yaml | 1 + src/ui/w_PluginManager.cpp | 4 ++++ 3 files changed, 9 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index ebb3f24b..e07cb39a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -145,6 +145,10 @@ if(QV2RAY_DISABLE_AUTO_UPDATE) add_definitions(-DDISABLE_AUTO_UPDATE) endif() +if(FALL_BACK_TO_XDG_OPEN) + add_definitions(-DFALL_BACK_TO_XDG_OPEN) +endif() + set(QVPLUGIN_INTERFACE_INCLUDE_DIR "src/components/plugins/interface") include(src/components/plugins/interface/QvPluginInterface.cmake) diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 60e41f38..27eb5fdb 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -73,6 +73,7 @@ parts: - -DCMAKE_INSTALL_PREFIX=/usr - -DCMAKE_BUILD_TYPE=Release - -DEMBED_TRANSLATIONS=ON + - -DFALL_BACK_TO_XDG_OPEN=ON override-pull: | snapcraftctl pull build_number=$(cat makespec/BUILDVERSION) diff --git a/src/ui/w_PluginManager.cpp b/src/ui/w_PluginManager.cpp index 6fc51526..5309f88a 100644 --- a/src/ui/w_PluginManager.cpp +++ b/src/ui/w_PluginManager.cpp @@ -122,7 +122,11 @@ void PluginManageWindow::on_openPluginFolder_clicked() { pluginPath.mkpath(QV2RAY_CONFIG_DIR + "plugins/"); } +#ifdef FALL_BACK_TO_XDG_OPEN + QProcess::execute("xdg-open", { pluginPath.absolutePath() }); +#else QDesktopServices::openUrl(QUrl::fromLocalFile(pluginPath.absolutePath())); +#endif } void PluginManageWindow::on_toolButton_clicked() From 5c66b29879b49643fcb7c3e6df47898077def03b Mon Sep 17 00:00:00 2001 From: DuckSoft Date: Mon, 4 May 2020 15:11:51 +0800 Subject: [PATCH 047/385] adopting url-safe base64 decode for ssd this shall resolve #577 --- src/core/connection/Serialization_ssd.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/connection/Serialization_ssd.cpp b/src/core/connection/Serialization_ssd.cpp index f207c3d4..5d694684 100644 --- a/src/core/connection/Serialization_ssd.cpp +++ b/src/core/connection/Serialization_ssd.cpp @@ -84,7 +84,7 @@ namespace Qv2ray::core::connection::Serialization // decode base64 const auto ssdURIBody = QStringRef(&uri, 6, uri.length() - 6); - const auto decodedJSON = QByteArray::fromBase64(ssdURIBody.toUtf8()); + const auto decodedJSON = SafeBase64Decode(ssdURIBody.toString()).toUtf8(); if (decodedJSON.length() == 0) { From e1db1873b4b4e5af4719c2a2421107463816fe65 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Mon, 4 May 2020 22:32:57 +0800 Subject: [PATCH 048/385] =?UTF-8?q?Have=20sleep,=20=F0=9F=A4=AC=20and=20do?= =?UTF-8?q?n't=20know=20how=20to=20fix=20this=20shit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- makespec/BUILDVERSION | 2 +- src/base/Qv2rayBase.hpp | 11 +- src/base/models/QvConfigIdentifier.hpp | 61 +++- src/base/models/QvSettingsObject.hpp | 6 +- src/core/CoreSafeTypes.hpp | 49 ++- src/core/CoreUtils.cpp | 9 +- src/core/CoreUtils.hpp | 3 +- src/core/connection/Serialization.cpp | 10 +- src/core/handler/ConfigHandler.cpp | 267 +++++++-------- src/core/handler/ConfigHandler.hpp | 47 ++- src/core/handler/KernelInstanceHandler.cpp | 57 ++-- src/core/handler/KernelInstanceHandler.hpp | 20 +- src/core/settings/SettingsUpgrade.cpp | 4 +- src/ui/w_GroupManager.cpp | 22 +- src/ui/w_MainWindow.cpp | 361 +++++++++++---------- src/ui/w_MainWindow.hpp | 12 +- src/ui/w_MainWindow_extra.cpp | 16 +- src/ui/w_PreferencesWindow.cpp | 40 +-- src/ui/w_PreferencesWindow.hpp | 2 - src/ui/widgets/ConnectionInfoWidget.cpp | 32 +- src/ui/widgets/ConnectionInfoWidget.hpp | 6 +- src/ui/widgets/ConnectionItemWidget.cpp | 20 +- src/ui/widgets/ConnectionItemWidget.hpp | 13 +- 23 files changed, 553 insertions(+), 517 deletions(-) diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 2f114b59..0117a108 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5366 \ No newline at end of file +5369 \ No newline at end of file diff --git a/src/base/Qv2rayBase.hpp b/src/base/Qv2rayBase.hpp index 3dd2d7b5..770029f1 100644 --- a/src/base/Qv2rayBase.hpp +++ b/src/base/Qv2rayBase.hpp @@ -42,15 +42,12 @@ using namespace Qv2ray::base::objects::transfer; // Get Configured Config Dir Path #define QV2RAY_CONFIG_DIR (Qv2ray::Qv2rayConfigPath) #define QV2RAY_CONFIG_FILE (QV2RAY_CONFIG_DIR + "Qv2ray.conf") +// +#define QV2RAY_GROUP_DIR (QV2RAY_CONFIG_DIR + "groups/") #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/") -#define QV2RAY_RULES_GFWLIST_PATH (QV2RAY_RULES_DIR + "gfwList.txt") -#define QV2RAY_RULES_PAC_PATH (QV2RAY_RULES_DIR + "pac.txt") - +// #define QV2RAY_CONFIG_FILE_EXTENSION ".qv2ray.json" #define QV2RAY_GENERATED_DIR (QV2RAY_CONFIG_DIR + "generated/") #define QV2RAY_GENERATED_FILE_PATH (QV2RAY_GENERATED_DIR + "config.gen.json") diff --git a/src/base/models/QvConfigIdentifier.hpp b/src/base/models/QvConfigIdentifier.hpp index d22ec49c..0d49841a 100644 --- a/src/base/models/QvConfigIdentifier.hpp +++ b/src/base/models/QvConfigIdentifier.hpp @@ -1,12 +1,15 @@ #pragma once #include "libs/QJsonStruct/QJsonStruct.hpp" +#include +#include #include #include + namespace Qv2ray::base { template - class IDType final + class IDType { public: explicit IDType() : m_id("null"){}; @@ -17,22 +20,20 @@ namespace Qv2ray::base } friend bool operator!=(const IDType &lhs, const IDType &rhs) { - return lhs.toString() != rhs.toString(); + return lhs.m_id != rhs.m_id; } const QString &toString() const { return m_id; } - uint qHash(uint seed) const + uint qHash(uint seed = 0) const { return ::qHash(m_id, seed); } - void loadJson(const QJsonValue &d) { m_id = d.toString("null"); } - QJsonValue toJson() const { return m_id; @@ -42,17 +43,38 @@ namespace Qv2ray::base QString m_id; }; - template - uint qHash(const IDType &key, uint seed = 0) - { - return key.qHash(seed); - } // Define several safetypes to prevent misuse of QString. class __QvGroup; class __QvConnection; typedef IDType<__QvGroup> GroupId; typedef IDType<__QvConnection> ConnectionId; // + inline const static auto NullConnectionId = ConnectionId("null"); + inline const static auto NullGroupId = GroupId("null"); + // + class ConnectionGroupPair + { + public: + ConnectionId connectionId = NullConnectionId; + GroupId groupId = NullGroupId; + ConnectionGroupPair() : connectionId(NullConnectionId), groupId(NullGroupId){}; + ConnectionGroupPair(const ConnectionId &conn, const GroupId &group) : connectionId(conn), groupId(group){}; + void clear() + { + connectionId = NullConnectionId; + groupId = NullGroupId; + } + bool isEmpty() const + { + return connectionId == NullConnectionId; + } + friend bool operator==(const ConnectionGroupPair &lhs, const ConnectionGroupPair &rhs) + { + return lhs.groupId == rhs.groupId && lhs.connectionId == rhs.connectionId; + } + JSONSTRUCT_REGISTER(ConnectionGroupPair, F(connectionId, groupId)) + }; + // constexpr unsigned int QVTCPING_VALUE_ERROR = 99999; constexpr unsigned int QVTCPING_VALUE_NODATA = QVTCPING_VALUE_ERROR - 1; using namespace std::chrono; @@ -64,8 +86,7 @@ namespace Qv2ray::base qint64 lastUpdatedDate; __Qv2rayConfigObjectBase() : displayName(), creationDate(system_clock::to_time_t(system_clock::now())), // - lastUpdatedDate(system_clock::to_time_t(system_clock::now())) // - {}; + lastUpdatedDate(system_clock::to_time_t(system_clock::now())){}; // JSONSTRUCT_REGISTER(__Qv2rayConfigObjectBase, F(displayName, creationDate, lastUpdatedDate)) }; @@ -92,9 +113,25 @@ namespace Qv2ray::base qint64 latency; qint64 upLinkData; qint64 downLinkData; + // + int __qvConnectionRefCount; + // Qv2rayConnectionObject() : lastConnected(), latency(QVTCPING_VALUE_NODATA), upLinkData(0), downLinkData(0){}; JSONSTRUCT_REGISTER(Qv2rayConnectionObject, F(lastConnected, latency, upLinkData, downLinkData), B(__Qv2rayConfigObjectBase)) }; } // namespace Qv2ray::base using namespace Qv2ray::base; +Q_DECLARE_METATYPE(ConnectionGroupPair); +inline uint qHash(const ConnectionId &key, uint seed = 0) +{ + return qHash(key.toString(), seed); +} +inline uint qHash(const GroupId &key, uint seed = 0) +{ + return qHash(key.toString(), seed); +} +uint qHash(const Qv2ray::base::ConnectionGroupPair &pair) +{ + return qHash(pair.connectionId.toString() + pair.groupId.toString()); +} diff --git a/src/base/models/QvSettingsObject.hpp b/src/base/models/QvSettingsObject.hpp index bd0f7ed1..c2764ac7 100644 --- a/src/base/models/QvSettingsObject.hpp +++ b/src/base/models/QvSettingsObject.hpp @@ -105,7 +105,7 @@ namespace Qv2ray::base::config { QString theme; QString language; - QList recentConnections; + QList recentConnections; bool quietMode; bool useDarkTheme; bool useDarkTrayIcon; @@ -268,7 +268,7 @@ namespace Qv2ray::base::config bool tProxySupport; int logLevel; // - QString autoStartId; + ConnectionGroupPair autoStartId; // // Key = groupId, connectionId QList groups; @@ -290,7 +290,7 @@ namespace Qv2ray::base::config : config_version(QV2RAY_CONFIG_VERSION), // tProxySupport(false), // logLevel(), // - autoStartId("null"), // + autoStartId(), // groups(), // uiConfig(), // apiConfig(), // diff --git a/src/core/CoreSafeTypes.hpp b/src/core/CoreSafeTypes.hpp index ebd2867b..bd4860cd 100644 --- a/src/core/CoreSafeTypes.hpp +++ b/src/core/CoreSafeTypes.hpp @@ -1,32 +1,25 @@ -#pragma once +//#pragma once +//#include "base/models/QvConfigIdentifier.hpp" -#include "base/models/QvConfigIdentifier.hpp" +////#include -#include -#include -#include +//// namespace Qv2ray::core +////{ +//// template +//// QList StringsToIdList(const QList &strings) +//// { +//// QList list; +//// for (const auto &str : strings) list << IDType(str); +//// return list; +//// } -namespace Qv2ray::core -{ +//// template +//// QList IdListToStrings(const QList &ids) +//// { +//// QList list; +//// for (const auto &id : ids) list << id.toString(); +//// return list; +//// } +////} // namespace Qv2ray::core - inline const static auto NullConnectionId = ConnectionId("null"); - inline const static auto NullGroupId = GroupId("null"); - - template - QList StringsToIdList(const QList &strings) - { - QList list; - for (const auto &str : strings) list << IDType(str); - return list; - } - - template - QList IdListToStrings(const QList &ids) - { - QList list; - for (const auto &id : ids) list << id.toString(); - return list; - } -} // namespace Qv2ray::core - -using namespace Qv2ray::core; +//// using namespace Qv2ray::core; diff --git a/src/core/CoreUtils.cpp b/src/core/CoreUtils.cpp index bd6f1706..89a9ca8e 100644 --- a/src/core/CoreUtils.cpp +++ b/src/core/CoreUtils.cpp @@ -1,6 +1,7 @@ #include "CoreUtils.hpp" #include "common/QvHelpers.hpp" +#include "core/connection/Serialization.hpp" #include "core/handler/ConfigHandler.hpp" namespace Qv2ray::core @@ -155,10 +156,10 @@ namespace Qv2ray::core return TruncateString(ConnectionManager->GetGroupMetaObject(id).displayName, limit); } - const GroupId GetConnectionGroupId(const ConnectionId &id) - { - return ConnectionManager->GetConnectionMetaObject(id).groupId; - } + // const GroupId GetConnectionGroupId(const ConnectionId &id) + // { + // return ConnectionManager->GetConnectionMetaObject(id).groupId; + // } const QMap GetConfigInboundPorts(const CONFIGROOT &root) { diff --git a/src/core/CoreUtils.hpp b/src/core/CoreUtils.hpp index ca848c74..9ca73751 100644 --- a/src/core/CoreUtils.hpp +++ b/src/core/CoreUtils.hpp @@ -1,5 +1,6 @@ #pragma once #include "base/models/CoreObjectModels.hpp" +#include "base/models/QvConfigIdentifier.hpp" #include "base/models/QvSafeType.hpp" #include "core/CoreSafeTypes.hpp" @@ -43,7 +44,7 @@ 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 GroupId GetConnectionGroupId(const ConnectionId &id); // const QMap GetConfigInboundPorts(const CONFIGROOT &root); const QMap GetConfigInboundPorts(const ConnectionId &id); diff --git a/src/core/connection/Serialization.cpp b/src/core/connection/Serialization.cpp index 42f8e28f..91d5f01d 100644 --- a/src/core/connection/Serialization.cpp +++ b/src/core/connection/Serialization.cpp @@ -69,16 +69,16 @@ namespace Qv2ray::core::connection return connectionConf; } - const QString ConvertConfigToString(const ConnectionId &id, bool isSip002) + const QString ConvertConfigToString(const ConnectionGroupPair &identifier, bool isSip002) { - auto alias = GetDisplayName(id); - if (IsComplexConfig(id)) + auto alias = GetDisplayName(identifier.connectionId); + if (IsComplexConfig(identifier.connectionId)) { DEBUG(MODULE_CONNECTION, "Ignored an complex config: " + alias) return QV2RAY_SERIALIZATION_COMPLEX_CONFIG_PLACEHOLDER; } - auto server = ConnectionManager->GetConnectionRoot(id); - return ConvertConfigToString(alias, GetDisplayName(GetConnectionGroupId(id)), server, isSip002); + auto server = ConnectionManager->GetConnectionRoot(identifier.connectionId); + return ConvertConfigToString(alias, GetDisplayName(identifier.groupId), server, isSip002); } const QString ConvertConfigToString(const QString &alias, const QString &groupName, const CONFIGROOT &server, bool isSip002) diff --git a/src/core/handler/ConfigHandler.cpp b/src/core/handler/ConfigHandler.cpp index 018beff9..4261f58c 100644 --- a/src/core/handler/ConfigHandler.cpp +++ b/src/core/handler/ConfigHandler.cpp @@ -96,34 +96,9 @@ namespace Qv2ray::core::handlers void QvConfigHandler::CHSaveConfigData() { // Do not copy construct. - auto &newGlobalConfig = GlobalConfig; - newGlobalConfig.connections.clear(); - newGlobalConfig.groups.clear(); - - for (auto i = 0; i < connections.count(); i++) - { - newGlobalConfig.connections[connections.keys()[i].toString()] = connections.values()[i]; - } - - for (auto i = 0; i < groups.count(); i++) - { - QStringList connections = IdListToStrings(groups.values()[i].connections); - - if (groups.values()[i].isSubscription) - { - SubscriptionObject_Config o = groups.values()[i]; - o.connections = connections; - newGlobalConfig.subscriptions[groups.keys()[i].toString()] = o; - } - else - { - Qv2rayGroupConfigObject o = groups.values()[i]; - o.connections = connections; - newGlobalConfig.groups[groups.keys()[i].toString()] = o; - } - } - - SaveGlobalSettings(newGlobalConfig); + GlobalConfig.connections = connections.keys(); + GlobalConfig.groups = groups.keys(); + SaveGlobalSettings(); } void QvConfigHandler::timerEvent(QTimerEvent *event) @@ -139,9 +114,9 @@ namespace Qv2ray::core::handlers else if (event->timerId() == pingConnectionTimerId) { auto id = kernelHandler->CurrentConnection(); - if (id != NullConnectionId && GlobalConfig.advancedConfig.testLatencyPeriodcally) + if (!id.isEmpty() && GlobalConfig.advancedConfig.testLatencyPeriodcally) { - StartLatencyTest(id); + StartLatencyTest(id.connectionId); } } } @@ -183,31 +158,31 @@ namespace Qv2ray::core::handlers return subsList; } - const ConnectionId QvConfigHandler::GetConnectionIdByDisplayName(const QString &displayName, const GroupId &group) const - { - CheckGroupExistanceEx(group, NullConnectionId); - for (auto conn : groups[group].connections) - { - if (connections[conn].displayName == displayName) - { - return conn; - } - } + // const ConnectionId QvConfigHandler::GetConnectionIdByDisplayName(const QString &displayName, const GroupId &group) const + // { + // CheckGroupExistanceEx(group, NullConnectionId); + // for (auto conn : groups[group].connections) + // { + // if (connections[conn].displayName == displayName) + // { + // return conn; + // } + // } - return NullConnectionId; - } - const GroupId QvConfigHandler::GetGroupIdByDisplayName(const QString &displayName) const - { - for (auto group : groups.keys()) - { - if (groups[group].displayName == displayName) - { - return group; - } - } + // return NullConnectionId; + // } + // const GroupId QvConfigHandler::GetGroupIdByDisplayName(const QString &displayName) const + // { + // for (auto group : groups.keys()) + // { + // if (groups[group].displayName == displayName) + // { + // return group; + // } + // } - return NullGroupId; - } + // return NullGroupId; + // } void QvConfigHandler::ClearGroupUsage(const GroupId &id) { for (const auto &conn : groups[id].connections) @@ -225,6 +200,21 @@ namespace Qv2ray::core::handlers return; } + const QList QvConfigHandler::GetGroupId(const ConnectionId &connId) const + { + CheckConnectionExistanceEx(connId, {}); + QList grps; + for (const auto &groupId : groups.keys()) + { + const auto &group = groups[groupId]; + if (group.connections.contains(connId)) + { + grps.push_back(groupId); + } + } + return grps; + } + const std::optional QvConfigHandler::RenameConnection(const ConnectionId &id, const QString &newName) { CheckConnectionExistance(id); @@ -234,64 +224,65 @@ namespace Qv2ray::core::handlers CHSaveConfigData(); return {}; } - const std::optional QvConfigHandler::DeleteConnection(const ConnectionId &id) + + const std::optional QvConfigHandler::RemoveConnectionFromGroup(const ConnectionId &id, const GroupId &gid) { CheckConnectionExistance(id); - auto groupId = connections[id].groupId; - 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, "", Events::ConnectionEntry::ConnectionEvent_Deleted }); - connections.remove(id); - groups[groupId].connections.removeAll(id); - // - if (GlobalConfig.autoStartId == id.toString()) - { - GlobalConfig.autoStartId.clear(); - } - // - emit OnConnectionDeleted(id, groupId); - // - bool exists = connectionFile.exists(); - if (exists) - { - bool removed = connectionFile.remove(); - if (removed) - { - return {}; - } - return "Failed to remove file"; - } + // auto groupId = connections[id].groupId; + // 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, "", Events::ConnectionEntry::ConnectionEvent_Deleted }); + // connections.remove(id); + // groups[groupId].connections.removeAll(id); + //// + // if (GlobalConfig.autoStartId == id.toString()) + //{ + // GlobalConfig.autoStartId.clear(); + //} + //// + // emit OnConnectionDeleted(id, groupId); + //// + // bool exists = connectionFile.exists(); + // if (exists) + //{ + // bool removed = connectionFile.remove(); + // if (removed) + // { + // return {}; + // } + // return "Failed to remove file"; + //} return tr("File does not exist."); } const std::optional QvConfigHandler::MoveConnectionGroup(const ConnectionId &id, const GroupId &newGroupId) { CheckConnectionExistance(id); - auto const oldgid = connections[id].groupId; - // - QString oldPath = (groups[oldgid].isSubscription ? QV2RAY_SUBSCRIPTION_DIR : QV2RAY_CONNECTIONS_DIR) + oldgid.toString() + "/" + - id.toString() + QV2RAY_CONFIG_FILE_EXTENSION; - // - auto newDir = (groups[newGroupId].isSubscription ? QV2RAY_SUBSCRIPTION_DIR : QV2RAY_CONNECTIONS_DIR) + newGroupId.toString() + "/"; - QString newPath = newDir + id.toString() + QV2RAY_CONFIG_FILE_EXTENSION; - // - if (!QDir(newDir).exists()) - { - QDir().mkpath(newDir); - } - // - if (!QFile::rename(oldPath, newPath)) - { - LOG(MODULE_FILEIO, "Cannot rename") - } - groups[oldgid].connections.removeAll(id); - groups[newGroupId].connections.append(id); - connections[id].groupId = newGroupId; - // - PluginHost->Send_ConnectionEvent({ connections[id].displayName, "", Events::ConnectionEntry::ConnectionEvent_Updated }); - // - emit OnConnectionGroupChanged(id, oldgid, newGroupId); + // auto const oldgid = connections[id].groupId; + //// + // QString oldPath = (groups[oldgid].isSubscription ? QV2RAY_SUBSCRIPTION_DIR : QV2RAY_CONNECTIONS_DIR) + oldgid.toString() + "/" + + // id.toString() + QV2RAY_CONFIG_FILE_EXTENSION; + //// + // auto newDir = (groups[newGroupId].isSubscription ? QV2RAY_SUBSCRIPTION_DIR : QV2RAY_CONNECTIONS_DIR) + newGroupId.toString() + "/"; + // QString newPath = newDir + id.toString() + QV2RAY_CONFIG_FILE_EXTENSION; + //// + // if (!QDir(newDir).exists()) + //{ + // QDir().mkpath(newDir); + //} + //// + // if (!QFile::rename(oldPath, newPath)) + //{ + // LOG(MODULE_FILEIO, "Cannot rename") + //} + // groups[oldgid].connections.removeAll(id); + // groups[newGroupId].connections.append(id); + // connections[id].groupId = newGroupId; + //// + // PluginHost->Send_ConnectionEvent({ connections[id].displayName, "", Events::ConnectionEntry::ConnectionEvent_Updated }); + //// + // emit OnConnectionGroupChanged(id, oldgid, newGroupId); // return {}; } @@ -306,14 +297,15 @@ namespace Qv2ray::core::handlers // Copy construct auto list = groups[id].connections; - for (auto conn : list) + for (const auto &conn : list) { MoveConnectionGroup(conn, DefaultGroupId); } // + // TODO if (groups[id].isSubscription) { - QDir(QV2RAY_SUBSCRIPTION_DIR + id.toString()).removeRecursively(); + // QDir(QV2RAY_SUBSCRIPTION_DIR + id.toString()).removeRecursively(); } else { @@ -332,12 +324,13 @@ namespace Qv2ray::core::handlers return {}; } - const std::optional QvConfigHandler::StartConnection(const ConnectionId &id) + const std::optional QvConfigHandler::StartConnection(const ConnectionId &id, const GroupId &group) { - CheckConnectionExistance(id); - connections[id].lastConnected = system_clock::to_time_t(system_clock::now()); - CONFIGROOT root = GetConnectionRoot(id); - return kernelHandler->StartConnection(id, root); + return {}; + // CheckConnectionExistance(id); + // connections[id].lastConnected = system_clock::to_time_t(system_clock::now()); + // CONFIGROOT root = GetConnectionRoot(id); + // return kernelHandler->StartConnection(id, root); } void QvConfigHandler::RestartConnection() // const ConnectionId &id @@ -351,16 +344,11 @@ namespace Qv2ray::core::handlers CHSaveConfigData(); } - bool QvConfigHandler::IsConnected(const ConnectionId &id) const - { - return kernelHandler->isConnected(id); - } - - void QvConfigHandler::OnKernelCrashed_p(const ConnectionId &id, const QString &errMessage) + void QvConfigHandler::OnKernelCrashed_p(const ConnectionGroupPair &id, const QString &errMessage) { LOG(MODULE_CORE_HANDLER, "Kernel crashed: " + errMessage) emit OnDisconnected(id); - PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), {}, Events::Connectivity::QvConnecticity_Disconnected }); + PluginHost->Send_ConnectivityEvent({ GetDisplayName(id.connectionId), {}, Events::Connectivity::QvConnecticity_Disconnected }); emit OnKernelCrashed(id, errMessage); } @@ -388,11 +376,8 @@ namespace Qv2ray::core::handlers bool QvConfigHandler::UpdateConnection(const ConnectionId &id, const CONFIGROOT &root, bool skipRestart) { CheckConnectionExistanceEx(id, false); - auto const &groupId = connections[id].groupId; - CheckGroupExistanceEx(groupId, false); // - auto path = (groups[groupId].isSubscription ? QV2RAY_SUBSCRIPTION_DIR : QV2RAY_CONNECTIONS_DIR) + groupId.toString() + "/" + - id.toString() + QV2RAY_CONFIG_FILE_EXTENSION; + auto path = QV2RAY_CONNECTIONS_DIR + "/" + id.toString() + QV2RAY_CONFIG_FILE_EXTENSION; auto content = JsonToString(root); bool result = StringToFile(content, path); // @@ -400,7 +385,7 @@ namespace Qv2ray::core::handlers // emit OnConnectionModified(id); PluginHost->Send_ConnectionEvent({ connections[id].displayName, "", Events::ConnectionEntry::ConnectionEvent_Updated }); - if (!skipRestart && kernelHandler->isConnected(id)) + if (!skipRestart && kernelHandler->CurrentConnection().connectionId == id) { emit RestartConnection(); } @@ -432,18 +417,18 @@ namespace Qv2ray::core::handlers return {}; } - const std::tuple QvConfigHandler::GetSubscriptionData(const GroupId &id) const - { - CheckGroupExistanceEx(id, {}); - std::tuple result; + // const std::tuple QvConfigHandler::GetSubscriptionData(const GroupId &id) const + // { + // CheckGroupExistanceEx(id, {}); + // std::tuple result; - if (!groups[id].isSubscription) - { - return result; - } + // if (!groups[id].isSubscription) + // { + // return result; + // } - return { groups[id].address, groups[id].lastUpdatedDate, groups[id].updateInterval }; - } + // return { groups[id].address, groups[id].lastUpdatedDate, groups[id].updateInterval }; + // } bool QvConfigHandler::SetSubscriptionData(const GroupId &id, bool isSubscription, const QString &address, float updateInterval) { @@ -455,11 +440,11 @@ namespace Qv2ray::core::handlers groups[id].isSubscription = isSubscription; if (!address.isEmpty()) { - groups[id].address = address; + groups[id].subscriptionSettings.address = address; } if (updateInterval != -1) { - groups[id].updateInterval = updateInterval; + groups[id].subscriptionSettings.updateInterval = updateInterval; } return true; } @@ -467,12 +452,12 @@ namespace Qv2ray::core::handlers bool QvConfigHandler::UpdateSubscription(const GroupId &id) { CheckGroupExistanceEx(id, false); - if (isHttpRequestInProgress) + if (isHttpRequestInProgress || !groups[id].isSubscription) { return false; } isHttpRequestInProgress = true; - auto data = httpHelper->Get(groups[id].address); + auto data = httpHelper->Get(groups[id].subscriptionSettings.address); isHttpRequestInProgress = false; return CHUpdateSubscription_p(id, data); } @@ -510,7 +495,7 @@ namespace Qv2ray::core::handlers typeMap.insertMulti({ protocol, host, port }, conn); } } - QDir().mkpath(QV2RAY_SUBSCRIPTION_DIR + id.toString()); + QDir().mkpath(QV2RAY_CONNECTIONS_DIR); bool hasErrorOccured = false; // Copy construct here. auto connectionsOrig = groups[id].connections; @@ -578,7 +563,8 @@ namespace Qv2ray::core::handlers for (auto conn : connectionsOrig) { LOG(MODULE_CORE_HANDLER, "Removing: " + conn.toString()) - DeleteConnection(conn); + abort(); + // DeleteConnection(conn); } // Update the time @@ -604,10 +590,9 @@ namespace Qv2ray::core::handlers LOG(MODULE_CORE_HANDLER, "Creating new connection: " + displayName) ConnectionId newId(GenerateUuid()); groups[groupId].connections << newId; - connections[newId].groupId = groupId; - connections[newId].importDate = system_clock::to_time_t(system_clock::now()); + connections[newId].creationDate = system_clock::to_time_t(system_clock::now()); connections[newId].displayName = displayName; - emit OnConnectionCreated(newId, displayName); + emit OnConnectionCreated(newId, groupId, displayName); PluginHost->Send_ConnectionEvent({ displayName, "", Events::ConnectionEntry::ConnectionEvent_Created }); UpdateConnection(newId, root); if (!skipSaveConfig) diff --git a/src/core/handler/ConfigHandler.hpp b/src/core/handler/ConfigHandler.hpp index 432ffb91..c6fe9d83 100644 --- a/src/core/handler/ConfigHandler.hpp +++ b/src/core/handler/ConfigHandler.hpp @@ -29,6 +29,14 @@ namespace Qv2ray::core::handlers ~QvConfigHandler(); public slots: + // + // + inline std::optional StartConnection(const ConnectionGroupPair &identifier) + { + return StartConnection(identifier.connectionId, identifier.groupId); + } + // + // inline const QList Connections() const { return connections.keys(); @@ -52,32 +60,38 @@ namespace Qv2ray::core::handlers CheckGroupExistanceEx(id, {}); return groups[id]; } - inline bool IsSubscription(const GroupId &id) const + + bool IsConnected(const ConnectionGroupPair &id) const { - CheckGroupExistanceEx(id, {}); - return groups[id].isSubscription; + return kernelHandler->isConnected(id); } + + bool IsConnected(const ConnectionId &id) const + { + return kernelHandler->CurrentConnection().connectionId == id; + } + // // void CHSaveConfigData(); const QList Subscriptions() const; // // Get Options - const GroupId GetGroupIdByDisplayName(const QString &displayName) const; - /// TRY NOT TO USE THIS FUNCTION - const ConnectionId GetConnectionIdByDisplayName(const QString &displayName, const GroupId &group) const; + // const GroupId GetGroupIdByDisplayName(const QString &displayName) const; + // TRY NOT TO USE THIS FUNCTION + // const ConnectionId GetConnectionIdByDisplayName(const QString &displayName, const GroupId &group) const; + const QList GetGroupId(const ConnectionId &connId) const; // // Connectivity Operationss - const std::optional StartConnection(const ConnectionId &identifier); + const std::optional StartConnection(const ConnectionId &identifier, const GroupId &group); void StopConnection(); // const ConnectionId &id void RestartConnection(); - bool IsConnected(const ConnectionId &id) const; // // Connection Operations. bool UpdateConnection(const ConnectionId &id, const CONFIGROOT &root, bool skipRestart = false); void ClearGroupUsage(const GroupId &id); void ClearConnectionUsage(const ConnectionId &id); - const std::optional DeleteConnection(const ConnectionId &id); + const std::optional RemoveConnectionFromGroup(const ConnectionId &id, const GroupId &gid); const std::optional RenameConnection(const ConnectionId &id, const QString &newName); const std::optional MoveConnectionGroup(const ConnectionId &id, const GroupId &newGroupId); const ConnectionId CreateConnection(const QString &displayName, const GroupId &groupId, const CONFIGROOT &root, @@ -101,15 +115,16 @@ namespace Qv2ray::core::handlers bool SetSubscriptionData(const GroupId &id, bool isSubscription, const QString &address = "", float updateInterval = -1); bool UpdateSubscription(const GroupId &id); // bool UpdateSubscriptionASync(const GroupId &id, bool useSystemProxy); - const std::tuple GetSubscriptionData(const GroupId &id) const; + // const std::tuple GetSubscriptionData(const GroupId &id) const; signals: 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); void OnConnectionRenamed(const ConnectionId &id, const QString &originalName, const QString &newName); - void OnConnectionDeleted(const ConnectionId &id, const GroupId &originalGroupId); + void OnConnectionCreated(const ConnectionId &id, const GroupId &groupId, const QString &displayName); + void OnConnectionDeleted(const ConnectionId &id, const GroupId &groupId); + void OnConnectionRemovedFromGroup(const ConnectionId &id, const GroupId &groupId); void OnConnectionModified(const ConnectionId &id); void OnConnectionGroupChanged(const ConnectionId &id, const GroupId &originalGroup, const GroupId &newGroup); // @@ -121,12 +136,12 @@ 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, const QString &errMessage); + void OnConnected(const ConnectionGroupPair &id); + void OnDisconnected(const ConnectionGroupPair &id); + void OnKernelCrashed(const ConnectionGroupPair &id, const QString &errMessage); // private slots: - void OnKernelCrashed_p(const ConnectionId &id, const QString &errMessage); + void OnKernelCrashed_p(const ConnectionGroupPair &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 620e0ce5..7eebd4cd 100644 --- a/src/core/handler/KernelInstanceHandler.cpp +++ b/src/core/handler/KernelInstanceHandler.cpp @@ -27,7 +27,7 @@ namespace Qv2ray::core::handlers { } - std::optional KernelInstanceHandler::StartConnection(const ConnectionId &id, const CONFIGROOT &root) + std::optional KernelInstanceHandler::StartConnection(const ConnectionGroupPair &id, const CONFIGROOT &root) { if (isConnected) { @@ -38,7 +38,7 @@ namespace Qv2ray::core::handlers bool isComplex = IsComplexConfig(root); auto fullConfig = GenerateRuntimeConfig(root); inboundPorts = GetConfigInboundPorts(fullConfig); - PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), inboundPorts, Events::Connectivity::QvConnecticity_Connecting }); + PluginHost->Send_ConnectivityEvent({ GetDisplayName(id.connectionId), inboundPorts, Events::Connectivity::QvConnecticity_Connecting }); QList> inboundInfo; for (const auto &inbound_v : fullConfig["inbounds"].toArray()) { @@ -96,7 +96,7 @@ namespace Qv2ray::core::handlers pluginProcessedOutboundList.append({ originalOutboundTag, inTag, freedomTag }); pluginPort++; } - pluginInboundPort.insert("enable_udp",GlobalConfig.inboundConfig.socksUDP?1:0); + pluginInboundPort.insert("enable_udp", GlobalConfig.inboundConfig.socksUDP ? 1 : 0); LOG(MODULE_CONNECTION, "Sending connection settings to kernel.") kernel->SetConnectionSettings(GlobalConfig.inboundConfig.listenip, pluginInboundPort, outbound["settings"].toObject()); } @@ -150,7 +150,7 @@ namespace Qv2ray::core::handlers } // ================================================================================================ // - currentConnectionId = id; + currentId = id; lastConnectionId = id; bool success = true; for (auto &kernel : activeKernels.keys()) @@ -174,12 +174,14 @@ namespace Qv2ray::core::handlers // if (!result.has_value()) { - emit OnConnected(currentConnectionId); - PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), inboundPorts, Events::Connectivity::QvConnecticity_Connected }); + emit OnConnected(id); + PluginHost->Send_ConnectivityEvent( + { GetDisplayName(id.connectionId), inboundPorts, Events::Connectivity::QvConnecticity_Connected }); } else { - PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), inboundPorts, Events::Connectivity::QvConnecticity_Disconnected }); + PluginHost->Send_ConnectivityEvent( + { GetDisplayName(id.connectionId), inboundPorts, Events::Connectivity::QvConnecticity_Disconnected }); } return result; } @@ -199,15 +201,16 @@ namespace Qv2ray::core::handlers pluginInboundPort[_protocol] = _port; } connect(kernel, &QvPluginKernel::OnKernelStatsAvailable, this, &KernelInstanceHandler::OnStatsDataArrived_p); - currentConnectionId = id; + currentId = id; lastConnectionId = id; - pluginInboundPort.insert("enable_udp",GlobalConfig.inboundConfig.socksUDP?1:0); + pluginInboundPort.insert("enable_udp", GlobalConfig.inboundConfig.socksUDP ? 1 : 0); kernel->SetConnectionSettings(GlobalConfig.inboundConfig.listenip, pluginInboundPort, firstOutbound["settings"].toObject()); bool result = kernel->StartKernel(); if (result) { - emit OnConnected(currentConnectionId); - PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), inboundPorts, Events::Connectivity::QvConnecticity_Connected }); + emit OnConnected(id); + PluginHost->Send_ConnectivityEvent( + { GetDisplayName(id.connectionId), inboundPorts, Events::Connectivity::QvConnecticity_Connected }); return {}; } else @@ -218,17 +221,19 @@ namespace Qv2ray::core::handlers else { LOG(MODULE_CONNECTION, "Starting V2ray without kernel") - currentConnectionId = id; + currentId = id; lastConnectionId = id; auto result = vCoreInstance->StartConnection(fullConfig); if (result.has_value()) { - PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), inboundPorts, Events::Connectivity::QvConnecticity_Disconnected }); + PluginHost->Send_ConnectivityEvent( + { GetDisplayName(id.connectionId), inboundPorts, Events::Connectivity::QvConnecticity_Disconnected }); } else { - emit OnConnected(currentConnectionId); - PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), inboundPorts, Events::Connectivity::QvConnecticity_Connected }); + emit OnConnected(id); + PluginHost->Send_ConnectivityEvent( + { GetDisplayName(id.connectionId), inboundPorts, Events::Connectivity::QvConnecticity_Connected }); } return result; } @@ -244,15 +249,15 @@ namespace Qv2ray::core::handlers void KernelInstanceHandler::OnKernelCrashed_p(const QString &msg) { StopConnection(); - emit OnCrashed(currentConnectionId, msg); - emit OnDisconnected(currentConnectionId); - lastConnectionId = currentConnectionId; - currentConnectionId = NullConnectionId; + emit OnCrashed(currentId, msg); + emit OnDisconnected(currentId); + lastConnectionId = currentId; + currentId.clear(); } void KernelInstanceHandler::OnKernelLogAvailable_p(const QString &log) { - emit OnKernelLogAvailable(currentConnectionId, log); + emit OnKernelLogAvailable(currentId.connectionId, log); } void KernelInstanceHandler::StopConnection() @@ -260,7 +265,7 @@ namespace Qv2ray::core::handlers if (isConnected) { PluginHost->Send_ConnectivityEvent( - { GetDisplayName(currentConnectionId), inboundPorts, Events::Connectivity::QvConnecticity_Disconnecting }); + { GetDisplayName(currentId.connectionId), inboundPorts, Events::Connectivity::QvConnecticity_Disconnecting }); if (vCoreInstance->KernelStarted) { vCoreInstance->StopConnection(); @@ -273,10 +278,10 @@ namespace Qv2ray::core::handlers activeKernels[kernel]->StopKernel(); } // Copy - ConnectionId id = currentConnectionId; - currentConnectionId = NullConnectionId; - emit OnDisconnected(id); - PluginHost->Send_ConnectivityEvent({ GetDisplayName(id), inboundPorts, Events::Connectivity::QvConnecticity_Disconnected }); + emit OnDisconnected(currentId); + PluginHost->Send_ConnectivityEvent( + { GetDisplayName(currentId.connectionId), inboundPorts, Events::Connectivity::QvConnecticity_Disconnected }); + currentId.clear(); } else { @@ -288,7 +293,7 @@ namespace Qv2ray::core::handlers { if (isConnected) { - emit OnStatsDataAvailable(currentConnectionId, uploadSpeed, downloadSpeed); + emit OnStatsDataAvailable(currentId.connectionId, uploadSpeed, downloadSpeed); } } } // namespace Qv2ray::core::handlers diff --git a/src/core/handler/KernelInstanceHandler.hpp b/src/core/handler/KernelInstanceHandler.hpp index fa1d18c8..f5b5f404 100644 --- a/src/core/handler/KernelInstanceHandler.hpp +++ b/src/core/handler/KernelInstanceHandler.hpp @@ -15,16 +15,16 @@ namespace Qv2ray::core::handlers explicit KernelInstanceHandler(QObject *parent = nullptr); ~KernelInstanceHandler(); - std::optional StartConnection(const ConnectionId &id, const CONFIGROOT &root); + std::optional StartConnection(const ConnectionGroupPair &id, const CONFIGROOT &root); void RestartConnection(); void StopConnection(); - const ConnectionId CurrentConnection() const + const ConnectionGroupPair CurrentConnection() const { - return currentConnectionId; + return currentId; } - bool isConnected(const ConnectionId &id) const + bool isConnected(const ConnectionGroupPair &id) const { - return id == currentConnectionId; + return id == currentId; } const QMap InboundPorts() const { @@ -32,9 +32,9 @@ namespace Qv2ray::core::handlers } signals: - void OnConnected(const ConnectionId &id); - void OnDisconnected(const ConnectionId &id); - void OnCrashed(const ConnectionId &id, const QString &errMessage); + void OnConnected(const ConnectionGroupPair &id); + void OnDisconnected(const ConnectionGroupPair &id); + void OnCrashed(const ConnectionGroupPair &id, const QString &errMessage); void OnStatsDataAvailable(const ConnectionId &id, const quint64 uploadSpeed, const quint64 downloadSpeed); void OnKernelLogAvailable(const ConnectionId &id, const QString &log); @@ -49,8 +49,8 @@ namespace Qv2ray::core::handlers QMap inboundPorts; CONFIGROOT root; V2rayKernelInstance *vCoreInstance = nullptr; - ConnectionId currentConnectionId = NullConnectionId; - ConnectionId lastConnectionId = NullConnectionId; + ConnectionGroupPair currentId = {}; + ConnectionGroupPair lastConnectionId = {}; }; inline const KernelInstanceHandler *KernelInstance; } // namespace Qv2ray::core::handlers diff --git a/src/core/settings/SettingsUpgrade.cpp b/src/core/settings/SettingsUpgrade.cpp index 6a611d9a..b2710fb5 100644 --- a/src/core/settings/SettingsUpgrade.cpp +++ b/src/core/settings/SettingsUpgrade.cpp @@ -189,8 +189,8 @@ namespace Qv2ray subs["updateInterval"] = value["updateInterval"]; subs["displayName"] = key; // - auto baseDirPath = QV2RAY_SUBSCRIPTION_DIR + key; - auto newDirPath = QV2RAY_SUBSCRIPTION_DIR + subsUuid; + auto baseDirPath = QV2RAY_CONFIG_DIR + "/subscriptions/" + key; + auto newDirPath = QV2RAY_CONFIG_DIR + "/subscriptions/" + subsUuid; QDir newDir(newDirPath); if (!newDir.exists()) diff --git a/src/ui/w_GroupManager.cpp b/src/ui/w_GroupManager.cpp index 0bf686bb..4f1e6e7e 100644 --- a/src/ui/w_GroupManager.cpp +++ b/src/ui/w_GroupManager.cpp @@ -36,15 +36,13 @@ GroupManager::GroupManager(QWidget *parent) : QDialog(parent) this->loadConnectionList(currentGroupId); // }); // - connect(ConnectionManager, &QvConfigHandler::OnConnectionCreated, [&](const ConnectionId &id) { // - const auto groupId = GetConnectionGroupId(id); // - if (groupId == currentGroupId) // - this->loadConnectionList(groupId); // - }); // - connect(ConnectionManager, &QvConfigHandler::OnConnectionDeleted, [&](const ConnectionId &, const GroupId &group) { - if (group == currentGroupId) // - this->loadConnectionList(group); // - }); // + const auto reloadGroupLambda = [&](const ConnectionId &, const GroupId &groupId) { + if (groupId == currentGroupId) + this->loadConnectionList(groupId); + }; + connect(ConnectionManager, &QvConfigHandler::OnConnectionCreated, reloadGroupLambda); + connect(ConnectionManager, &QvConfigHandler::OnConnectionDeleted, reloadGroupLambda); + connect(ConnectionManager, &QvConfigHandler::OnConnectionRemovedFromGroup, reloadGroupLambda); // for (auto group : ConnectionManager->AllGroups()) { @@ -64,7 +62,7 @@ void GroupManager::onRCMDeleteConnectionTriggered() const auto list = GET_DATA(QString, String)(connectionsList->selectedItems()); for (const auto &item : list) { - ConnectionManager->DeleteConnection(ConnectionId(item)); + ConnectionManager->RemoveConnectionFromGroup(ConnectionId(item), currentGroupId); } } @@ -265,10 +263,10 @@ void GroupManager::on_groupList_itemClicked(QListWidgetItem *item) groupNameTxt->setText(GetDisplayName(currentGroupId)); const auto &groupMetaObject = ConnectionManager->GetGroupMetaObject(currentGroupId); groupIsSubscriptionGroup->setChecked(groupMetaObject.isSubscription); - subAddrTxt->setText(groupMetaObject.address); + subAddrTxt->setText(groupMetaObject.subscriptionSettings.address); lastUpdatedLabel->setText(timeToString(groupMetaObject.lastUpdatedDate)); createdAtLabel->setText(timeToString(groupMetaObject.creationDate)); - updateIntervalSB->setValue(groupMetaObject.updateInterval); + updateIntervalSB->setValue(groupMetaObject.subscriptionSettings.updateInterval); // connectionsList->clear(); loadConnectionList(currentGroupId); diff --git a/src/ui/w_MainWindow.cpp b/src/ui/w_MainWindow.cpp index af0d5c83..867d840d 100644 --- a/src/ui/w_MainWindow.cpp +++ b/src/ui/w_MainWindow.cpp @@ -51,7 +51,7 @@ QvMessageBusSlotImpl(MainWindow) void MainWindow::UpdateColorScheme() { - hTray.setIcon(KernelInstance->CurrentConnection() == NullConnectionId ? Q_TRAYICON("tray.png") : Q_TRAYICON("tray-connected.png")); + hTray.setIcon(KernelInstance->CurrentConnection().isEmpty() ? Q_TRAYICON("tray.png") : Q_TRAYICON("tray-connected.png")); // importConfigButton->setIcon(QICON_R("import.png")); updownImageBox->setStyleSheet("image: url(" + QV2RAY_COLORSCHEME_ROOT + "netspeed_arrow.png)"); @@ -89,9 +89,9 @@ void MainWindow::MWAddConnectionItem_p(const ConnectionId &connection, const Gro "LAST_CONNECTED_NOT_SUPPORTED", // NumericString(GetConnectionTotalData(connection)) // }); - connectionNodes.insert(connection, connectionItem); + connectionNodes.insert({ connection, groupId }, connectionItem); groupItem->addChild(connectionItem.get()); - auto widget = new ConnectionItemWidget(connection, connectionListWidget); + auto widget = new ConnectionItemWidget(connection, groupId, connectionListWidget); connect(widget, &ConnectionItemWidget::RequestWidgetFocus, this, &MainWindow::OnConnectionWidgetFocusRequested); connectionListWidget->setItemWidget(connectionItem.get(), 0, widget); } @@ -114,14 +114,14 @@ void MainWindow::SortConnectionList(MW_ITEM_COL byCol, bool asending) on_locateBtn_clicked(); } -void MainWindow::ReloadRecentConnectionList(const QList &items) +void MainWindow::ReloadRecentConnectionList(const QList &items) { QList newActions; for (const auto &item : items) { auto action = new QAction(tray_RecentConnectionsMenu); - action->setText(GetDisplayName(ConnectionId{ item })); - action->setData(item); + action->setText(GetDisplayName(item.connectionId)); + action->setData(QVariant::fromValue(item)); connect(ConnectionManager, &QvConfigHandler::OnConnectionRenamed, [action](const ConnectionId &_t1, const QString &, const QString &_t3) { if (_t1.toString() == action->data().toString()) @@ -130,7 +130,7 @@ void MainWindow::ReloadRecentConnectionList(const QList &items) } }); connect(action, &QAction::triggered, [action]() { // - emit ConnectionManager->StartConnection(ConnectionId{ action->data().toString() }); + emit ConnectionManager->StartConnection(action->data().value()); }); newActions << action; } @@ -161,7 +161,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) UpdateColorScheme(); // // - connect(ConnectionManager, &QvConfigHandler::OnKernelCrashed, [&](const ConnectionId &, const QString &reason) { + connect(ConnectionManager, &QvConfigHandler::OnKernelCrashed, [&](const ConnectionGroupPair &, const QString &reason) { this->show(); QvMessageBoxWarn(this, tr("Kernel terminated."), tr("The kernel terminated unexpectedly:") + NEWLINE + reason + NEWLINE + NEWLINE + @@ -181,12 +181,14 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) connect(ConnectionManager, &QvConfigHandler::OnGroupDeleted, this, &MainWindow::OnGroupDeleted); // connect(ConnectionManager, &QvConfigHandler::OnConnectionRenamed, [&](const ConnectionId &id, const QString &, const QString &newName) { - if (connectionNodes.contains(id)) - connectionNodes.value(id)->setText(MW_ITEM_COL_NAME, newName); // + ConnectionGroupPair pair = { id, ConnectionManager->GetGroupId(id).first() }; + if (connectionNodes.contains(pair)) + connectionNodes.value(pair)->setText(MW_ITEM_COL_NAME, newName); // }); connect(ConnectionManager, &QvConfigHandler::OnLatencyTestFinished, [&](const ConnectionId &id, const uint avg) { - if (connectionNodes.contains(id)) - connectionNodes.value(id)->setText(MW_ITEM_COL_PING, NumericString(avg)); // + ConnectionGroupPair pair = { id, ConnectionManager->GetGroupId(id).first() }; + if (connectionNodes.contains(pair)) + connectionNodes.value(pair)->setText(MW_ITEM_COL_PING, NumericString(avg)); // }); // connect(infoWidget, &ConnectionInfoWidget::OnEditRequested, this, &MainWindow::OnEditRequested); @@ -223,7 +225,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) // connect(tray_action_ShowHide, &QAction::triggered, this, &MainWindow::ToggleVisibility); connect(tray_action_ShowPreferencesWindow, &QAction::triggered, this, &MainWindow::on_preferencesBtn_clicked); - connect(tray_action_Start, &QAction::triggered, [&] { ConnectionManager->StartConnection(lastConnectedId); }); + connect(tray_action_Start, &QAction::triggered, [&] { ConnectionManager->StartConnection(lastConnectedIdentifier); }); 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); @@ -310,12 +312,11 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) // Find and start if there is an auto-connection auto needShowWindow = true; - if (!GlobalConfig.autoStartId.isEmpty()) + if (GlobalConfig.autoStartId.connectionId != NullConnectionId) { - auto id = ConnectionId(GlobalConfig.autoStartId); // Empty means we are connected, so has_value is false. // So no need to show is false. - needShowWindow = ConnectionManager->StartConnection(id).has_value(); + needShowWindow = ConnectionManager->StartConnection(GlobalConfig.autoStartId.connectionId, GlobalConfig.autoStartId.groupId).has_value(); } if (needShowWindow && connectionListWidget->topLevelItemCount() > 0) { @@ -534,20 +535,27 @@ void MainWindow::on_connectionListWidget_customContextMenuRequested(const QPoint void MainWindow::on_action_RCM_DeleteThese_triggered() { - QList connlist; + QList connlist; - for (auto item : connectionListWidget->selectedItems()) + for (const auto &item : connectionListWidget->selectedItems()) { auto widget = GetItemWidget(item); if (widget) { + const auto identifier = widget->Identifier(); if (widget->IsConnection()) { - connlist.append(std::get<1>(widget->Identifier())); + connlist.append(identifier); } else { - connlist.append(ConnectionManager->GetGroupMetaObject(std::get<0>(widget->Identifier())).connections); + for (const auto &conns : ConnectionManager->GetGroupMetaObject(identifier.groupId).connections) + { + ConnectionGroupPair i; + i.connectionId = conns; + i.groupId = identifier.groupId; + connlist.append(i); + } } } } @@ -565,14 +573,14 @@ void MainWindow::on_action_RCM_DeleteThese_triggered() return; } - for (auto conn : connlist) + for (const auto &conn : connlist) { if (ConnectionManager->IsConnected(conn)) ConnectionManager->StopConnection(); - if (GlobalConfig.autoStartId == conn.toString()) + if (GlobalConfig.autoStartId == conn) GlobalConfig.autoStartId.clear(); - ConnectionManager->DeleteConnection(conn); + ConnectionManager->RemoveConnectionFromGroup(conn.connectionId, conn.groupId); } } @@ -587,8 +595,8 @@ void MainWindow::on_action_RCM_EditAsComplex_triggered() CheckCurrentWidget; if (widget->IsConnection()) { - auto id = std::get<1>(widget->Identifier()); - CONFIGROOT root = ConnectionManager->GetConnectionRoot(id); + auto id = widget->Identifier(); + CONFIGROOT root = ConnectionManager->GetConnectionRoot(id.connectionId); bool isChanged = false; // LOG(MODULE_UI, "INFO: Opening route editor.") @@ -597,7 +605,7 @@ void MainWindow::on_action_RCM_EditAsComplex_triggered() isChanged = routeWindow.result() == QDialog::Accepted; if (isChanged) { - ConnectionManager->UpdateConnection(id, root); + ConnectionManager->UpdateConnection(id.connectionId, root); } } } @@ -619,7 +627,7 @@ void MainWindow::on_connectionListWidget_itemDoubleClicked(QTreeWidgetItem *item } } -void MainWindow::OnDisconnected(const ConnectionId &id) +void MainWindow::OnDisconnected(const ConnectionGroupPair &id) { Q_UNUSED(id) hTray.setIcon(Q_TRAYICON("tray.png")); @@ -627,11 +635,11 @@ void MainWindow::OnDisconnected(const ConnectionId &id) tray_action_Stop->setEnabled(false); tray_action_Restart->setEnabled(false); tray_SystemProxyMenu->setEnabled(false); - lastConnectedId = id; + lastConnectedIdentifier = id; locateBtn->setEnabled(false); if (!GlobalConfig.uiConfig.quietMode) { - this->hTray.showMessage("Qv2ray", tr("Disconnected from: ") + GetDisplayName(id), this->windowIcon()); + this->hTray.showMessage("Qv2ray", tr("Disconnected from: ") + GetDisplayName(id.connectionId), this->windowIcon()); } hTray.setToolTip(TRAY_TOOLTIP_PREFIX); netspeedLabel->setText("0.00 B/s" NEWLINE "0.00 B/s"); @@ -643,7 +651,7 @@ void MainWindow::OnDisconnected(const ConnectionId &id) } } -void MainWindow::OnConnected(const ConnectionId &id) +void MainWindow::OnConnected(const ConnectionGroupPair &id) { Q_UNUSED(id) hTray.setIcon(Q_TRAYICON("tray-connected.png")); @@ -651,10 +659,10 @@ void MainWindow::OnConnected(const ConnectionId &id) tray_action_Stop->setEnabled(true); tray_action_Restart->setEnabled(true); tray_SystemProxyMenu->setEnabled(true); - lastConnectedId = id; + lastConnectedIdentifier = id; locateBtn->setEnabled(true); on_clearlogButton_clicked(); - auto name = GetDisplayName(id); + auto name = GetDisplayName(id.connectionId); if (!GlobalConfig.uiConfig.quietMode) { this->hTray.showMessage("Qv2ray", tr("Connected: ") + name, this->windowIcon()); @@ -662,7 +670,7 @@ void MainWindow::OnConnected(const ConnectionId &id) hTray.setToolTip(TRAY_TOOLTIP_PREFIX NEWLINE + tr("Connected: ") + name); connetionStatusLabel->setText(tr("Connected: ") + name); // - ConnectionManager->StartLatencyTest(id); + ConnectionManager->StartLatencyTest(id.connectionId); if (GlobalConfig.inboundConfig.setSystemProxy) { MWSetSystemProxy(); @@ -753,256 +761,255 @@ void MainWindow::OnStatsAvailable(const ConnectionId &id, const quint64 upS, con NEWLINE "Up: " + totalSpeedUp + " Down: " + totalSpeedDown); // // Set data accordingly - if (connectionNodes.contains(id)) - { - connectionNodes.value(id)->setText(MW_ITEM_COL_DATA, NumericString(GetConnectionTotalData(id))); - } + if (connectionNodes.contains({id, )) + { + connectionNodes.value(id)->setText(MW_ITEM_COL_DATA, NumericString(GetConnectionTotalData(id))); + } } void MainWindow::OnVCoreLogAvailable(const ConnectionId &id, const QString &log) { - Q_UNUSED(id); - FastAppendTextDocument(log.trimmed(), vCoreLogDocument); - // vCoreLogDocument->setPlainText(vCoreLogDocument->toPlainText() + log); - // From https://gist.github.com/jemyzhang/7130092 - auto maxLines = GlobalConfig.uiConfig.maximumLogLines; - auto block = vCoreLogDocument->begin(); + Q_UNUSED(id); + FastAppendTextDocument(log.trimmed(), vCoreLogDocument); + // vCoreLogDocument->setPlainText(vCoreLogDocument->toPlainText() + log); + // From https://gist.github.com/jemyzhang/7130092 + auto maxLines = GlobalConfig.uiConfig.maximumLogLines; + auto block = vCoreLogDocument->begin(); - while (block.isValid()) - { - if (vCoreLogDocument->blockCount() > maxLines) + while (block.isValid()) { - QTextCursor cursor(block); - block = block.next(); - cursor.select(QTextCursor::BlockUnderCursor); - cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor); - cursor.removeSelectedText(); - continue; - } + if (vCoreLogDocument->blockCount() > maxLines) + { + QTextCursor cursor(block); + block = block.next(); + cursor.select(QTextCursor::BlockUnderCursor); + cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor); + cursor.removeSelectedText(); + continue; + } - break; - } + break; + } } void MainWindow::OnEditRequested(const ConnectionId &id) { - auto outBoundRoot = ConnectionManager->GetConnectionRoot(id); - CONFIGROOT root; - bool isChanged = false; + auto outBoundRoot = ConnectionManager->GetConnectionRoot(id); + CONFIGROOT root; + bool isChanged = false; - if (IsComplexConfig(outBoundRoot)) - { - LOG(MODULE_UI, "INFO: Opening route editor.") - RouteEditor routeWindow(outBoundRoot, this); - root = routeWindow.OpenEditor(); - isChanged = routeWindow.result() == QDialog::Accepted; - } - else - { - LOG(MODULE_UI, "INFO: Opening single connection edit window.") - auto out = OUTBOUND(outBoundRoot["outbounds"].toArray().first().toObject()); - OutboundEditor w(out, this); - auto outboundEntry = w.OpenEditor(); - isChanged = w.result() == QDialog::Accepted; - QJsonArray outboundsList; - outboundsList.push_back(outboundEntry); - root.insert("outbounds", outboundsList); - } + if (IsComplexConfig(outBoundRoot)) + { + LOG(MODULE_UI, "INFO: Opening route editor.") + RouteEditor routeWindow(outBoundRoot, this); + root = routeWindow.OpenEditor(); + isChanged = routeWindow.result() == QDialog::Accepted; + } + else + { + LOG(MODULE_UI, "INFO: Opening single connection edit window.") + auto out = OUTBOUND(outBoundRoot["outbounds"].toArray().first().toObject()); + OutboundEditor w(out, this); + auto outboundEntry = w.OpenEditor(); + isChanged = w.result() == QDialog::Accepted; + QJsonArray outboundsList; + outboundsList.push_back(outboundEntry); + root.insert("outbounds", outboundsList); + } - if (isChanged) - { - ConnectionManager->UpdateConnection(id, root); - } + if (isChanged) + { + ConnectionManager->UpdateConnection(id, root); + } } void MainWindow::OnEditJsonRequested(const ConnectionId &id) { - JsonEditor w(ConnectionManager->GetConnectionRoot(id), this); - auto root = CONFIGROOT(w.OpenEditor()); + JsonEditor w(ConnectionManager->GetConnectionRoot(id), this); + auto root = CONFIGROOT(w.OpenEditor()); - if (w.result() == QDialog::Accepted) - { - ConnectionManager->UpdateConnection(id, root); - } + if (w.result() == QDialog::Accepted) + { + ConnectionManager->UpdateConnection(id, root); + } } -void MainWindow::OnConnectionCreated(const ConnectionId &id, const QString &displayName) +void MainWindow::OnConnectionCreated(const ConnectionId &id, const GroupId &groupId, const QString &displayName) { - Q_UNUSED(displayName) - MWAddConnectionItem_p(id, GetConnectionGroupId(id)); + Q_UNUSED(displayName) + MWAddConnectionItem_p(id, groupId); } void MainWindow::OnConnectionDeleted(const ConnectionId &id, const GroupId &groupId) { - auto child = connectionNodes.take(id); - groupNodes.value(groupId)->removeChild(child.get()); + auto child = connectionNodes.take(id); + groupNodes.value(groupId)->removeChild(child.get()); } void MainWindow::OnConnectionGroupChanged(const ConnectionId &id, const GroupId &originalGroup, const GroupId &newGroup) { - delete GetItemWidget(connectionNodes.value(id).get()); - groupNodes.value(originalGroup)->removeChild(connectionNodes.value(id).get()); - connectionNodes.remove(id); - MWAddConnectionItem_p(id, newGroup); + delete GetItemWidget(connectionNodes.value(id).get()); + groupNodes.value(originalGroup)->removeChild(connectionNodes.value(id).get()); + connectionNodes.remove(id); + MWAddConnectionItem_p(id, newGroup); } void MainWindow::OnGroupCreated(const GroupId &id, const QString &displayName) { - Q_UNUSED(displayName) - MWAddGroupItem_p(id); + Q_UNUSED(displayName) + MWAddGroupItem_p(id); } void MainWindow::OnGroupDeleted(const GroupId &id, const QList &connections) { - for (auto conn : connections) - { - groupNodes.value(id)->removeChild(connectionNodes.value(conn).get()); - } - groupNodes.remove(id); + for (const auto &conn : connections) + { + groupNodes.value(id)->removeChild(connectionNodes.value(conn).get()); + } + groupNodes.remove(id); } void MainWindow::on_locateBtn_clicked() { - auto id = KernelInstance->CurrentConnection(); - if (id != NullConnectionId) - { - connectionListWidget->setCurrentItem(connectionNodes.value(id).get()); - connectionListWidget->scrollToItem(connectionNodes.value(id).get()); - on_connectionListWidget_itemClicked(connectionNodes.value(id).get(), 0); - } + auto id = KernelInstance->CurrentConnection(); + if (!id.isEmpty()) + { + connectionListWidget->setCurrentItem(connectionNodes.value(id).get()); + connectionListWidget->scrollToItem(connectionNodes.value(id).get()); + on_connectionListWidget_itemClicked(connectionNodes.value(id).get(), 0); + } } void MainWindow::on_action_RCM_RenameThis_triggered() { - CheckCurrentWidget; - widget->BeginRename(); + CheckCurrentWidget; + widget->BeginRename(); } void MainWindow::on_action_RCM_DuplicateThese_triggered() { - QList connlist; + QList connlist; - for (auto item : connectionListWidget->selectedItems()) - { - auto widget = GetItemWidget(item); - if (widget->IsConnection()) + for (auto item : connectionListWidget->selectedItems()) { - connlist.append(std::get<1>(widget->Identifier())); + auto widget = GetItemWidget(item); + if (widget->IsConnection()) + { + connlist.append(widget->Identifier()); + } } - } - LOG(MODULE_UI, "Selected " + QSTRN(connlist.count()) + " items") + LOG(MODULE_UI, "Selected " + QSTRN(connlist.count()) + " items") - if (connlist.count() > 1 && QvMessageBoxAsk(this, tr("Duplicating Connection(s)"), // - tr("Are you sure to duplicate these connection(s)?")) != QMessageBox::Yes) - { - return; - } + if (connlist.count() > 1 && QvMessageBoxAsk(this, tr("Duplicating Connection(s)"), // + tr("Are you sure to duplicate these connection(s)?")) != QMessageBox::Yes) + { + return; + } - for (auto conn : connlist) - { - ConnectionManager->CreateConnection(GetDisplayName(conn) + tr(" (Copy)"), // - GetConnectionGroupId(conn), // - ConnectionManager->GetConnectionRoot(conn)); - } + for (const auto &conn : connlist) + { + ConnectionManager->CreateConnection(GetDisplayName(conn.connectionId) + tr(" (Copy)"), conn.groupId, + ConnectionManager->GetConnectionRoot(conn.connectionId)); + } } void MainWindow::on_action_RCM_EditThis_triggered() { - CheckCurrentWidget; - OnEditRequested(std::get<1>(widget->Identifier())); + CheckCurrentWidget; + OnEditRequested(widget->Identifier().connectionId); } void MainWindow::on_action_RCM_EditAsJson_triggered() { - CheckCurrentWidget; - OnEditJsonRequested(std::get<1>(widget->Identifier())); + CheckCurrentWidget; + OnEditJsonRequested(widget->Identifier().connectionId); } void MainWindow::on_chartVisibilityBtn_clicked() { - speedChartHolderWidget->setVisible(!speedChartWidget->isVisible()); + speedChartHolderWidget->setVisible(!speedChartWidget->isVisible()); } void MainWindow::on_logVisibilityBtn_clicked() { - masterLogBrowser->setVisible(!masterLogBrowser->isVisible()); + masterLogBrowser->setVisible(!masterLogBrowser->isVisible()); } void MainWindow::on_clearChartBtn_clicked() { - speedChartWidget->Clear(); + speedChartWidget->Clear(); } void MainWindow::on_connectionListWidget_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous) { - Q_UNUSED(previous) - if (current != nullptr && !isExiting) - { - on_connectionListWidget_itemClicked(current, 0); - } + Q_UNUSED(previous) + if (current != nullptr && !isExiting) + { + on_connectionListWidget_itemClicked(current, 0); + } } void MainWindow::on_action_RCM_tovCoreLog_triggered() { - masterLogBrowser->setDocument(vCoreLogDocument); + masterLogBrowser->setDocument(vCoreLogDocument); } void MainWindow::on_action_RCM_toQvLog_triggered() { - masterLogBrowser->setDocument(qvLogDocument); + masterLogBrowser->setDocument(qvLogDocument); } void MainWindow::on_masterLogBrowser_textChanged() { - auto bar = masterLogBrowser->verticalScrollBar(); - bar->setValue(bar->maximum()); + auto bar = masterLogBrowser->verticalScrollBar(); + bar->setValue(bar->maximum()); } void MainWindow::on_action_RCM_SetAutoConnection_triggered() { - auto current = connectionListWidget->currentItem(); - if (current != nullptr) - { - auto widget = GetItemWidget(current); - auto &conn = std::get<1>(widget->Identifier()); - GlobalConfig.autoStartId = conn.toString(); - if (!GlobalConfig.uiConfig.quietMode) + auto current = connectionListWidget->currentItem(); + if (current != nullptr) { - hTray.showMessage(tr("Set auto connection"), tr("Set %1 as auto connect.").arg(GetDisplayName(conn))); + auto widget = GetItemWidget(current); + const auto identifier = widget->Identifier(); + GlobalConfig.autoStartId = identifier; + if (!GlobalConfig.uiConfig.quietMode) + { + hTray.showMessage(tr("Set auto connection"), tr("Set %1 as auto connect.").arg(GetDisplayName(identifier.connectionId))); + } + SaveGlobalSettings(); } - SaveGlobalSettings(); - } } void MainWindow::on_action_RCM_ClearUsage_triggered() { - auto current = connectionListWidget->currentItem(); - if (current != nullptr) - { - auto widget = GetItemWidget(current); - if (widget) + auto current = connectionListWidget->currentItem(); + if (current != nullptr) { - if (widget->IsConnection()) - ConnectionManager->ClearConnectionUsage(std::get<1>(widget->Identifier())); - else - ConnectionManager->ClearGroupUsage(std::get<0>(widget->Identifier())); + auto widget = GetItemWidget(current); + if (widget) + { + if (widget->IsConnection()) + ConnectionManager->ClearConnectionUsage(widget->Identifier().connectionId); + else + ConnectionManager->ClearGroupUsage(widget->Identifier().groupId); + } } - } } void MainWindow::on_action_RCM_LatencyTest_triggered() { - auto current = connectionListWidget->currentItem(); - if (current != nullptr) - { - auto widget = GetItemWidget(current); - if (widget) + auto current = connectionListWidget->currentItem(); + if (current != nullptr) { - if (widget->IsConnection()) - ConnectionManager->StartLatencyTest(std::get<1>(widget->Identifier())); - else - ConnectionManager->StartLatencyTest(std::get<0>(widget->Identifier())); + auto widget = GetItemWidget(current); + if (widget) + { + if (widget->IsConnection()) + ConnectionManager->StartLatencyTest(widget->Identifier().connectionId); + else + ConnectionManager->StartLatencyTest(widget->Identifier().groupId); + } } - } } void MainWindow::on_pluginsBtn_clicked() { - PluginManageWindow(this).exec(); + PluginManageWindow(this).exec(); } diff --git a/src/ui/w_MainWindow.hpp b/src/ui/w_MainWindow.hpp index b2f470ed..c70e1d91 100644 --- a/src/ui/w_MainWindow.hpp +++ b/src/ui/w_MainWindow.hpp @@ -83,13 +83,13 @@ class MainWindow void ToggleVisibility(); void OnEditRequested(const ConnectionId &id); void OnEditJsonRequested(const ConnectionId &id); - void OnConnected(const ConnectionId &id); - void OnDisconnected(const ConnectionId &id); + void OnConnected(const ConnectionGroupPair &id); + void OnDisconnected(const ConnectionGroupPair &id); // void OnStatsAvailable(const ConnectionId &id, const quint64 upS, const quint64 downS, const quint64 upD, const quint64 downD); void OnVCoreLogAvailable(const ConnectionId &id, const QString &log); // - void OnConnectionCreated(const ConnectionId &id, const QString &displayName); + void OnConnectionCreated(const ConnectionId &id, const GroupId &groupId, const QString &displayName); void OnConnectionDeleted(const ConnectionId &id, const GroupId &groupId); void OnConnectionGroupChanged(const ConnectionId &id, const GroupId &originalGroup, const GroupId &newGroup); // @@ -98,7 +98,7 @@ class MainWindow // void SortConnectionList(MW_ITEM_COL byCol, bool asending); // - void ReloadRecentConnectionList(const QList &items); + void ReloadRecentConnectionList(const QList &items); protected: void timerEvent(QTimerEvent *event) override; @@ -108,7 +108,7 @@ class MainWindow private: QHash> groupNodes; - QHash> connectionNodes; + QHash> connectionNodes; // Charts SpeedWidget *speedChartWidget; QSystemTrayIcon hTray; @@ -159,7 +159,7 @@ class MainWindow QAction *action_RCM_tovCoreLog = new QAction(tr("Switch to vCore log"), this); QAction *action_RCM_toQvLog = new QAction(tr("Switch to Qv2ray log"), this); // - ConnectionId lastConnectedId; + ConnectionGroupPair lastConnectedIdentifier; void MWSetSystemProxy(); void MWClearSystemProxy(); void CheckSubscriptionsUpdate(); diff --git a/src/ui/w_MainWindow_extra.cpp b/src/ui/w_MainWindow_extra.cpp index ff3117f6..5cfb2d6d 100644 --- a/src/ui/w_MainWindow_extra.cpp +++ b/src/ui/w_MainWindow_extra.cpp @@ -32,7 +32,7 @@ void MainWindow::MWSetSystemProxy() void MainWindow::MWClearSystemProxy() { ClearSystemProxy(); - hTray.setIcon(KernelInstance->CurrentConnection() == NullConnectionId ? Q_TRAYICON("tray.png") : Q_TRAYICON("tray-connected.png")); + hTray.setIcon(KernelInstance->CurrentConnection().isEmpty() ? Q_TRAYICON("tray.png") : Q_TRAYICON("tray-connected.png")); if (!GlobalConfig.uiConfig.quietMode) { hTray.showMessage("Qv2ray", tr("System proxy removed.")); @@ -49,16 +49,16 @@ void MainWindow::CheckSubscriptionsUpdate() const auto info = ConnectionManager->GetGroupMetaObject(entry); // // The update is ignored. - if (info.updateInterval == 0) + if (info.subscriptionSettings.updateInterval == 0) continue; // const auto &lastRenewDate = QDateTime::fromTime_t(info.lastUpdatedDate); - const auto &renewTime = lastRenewDate.addSecs(info.updateInterval * 86400); - LOG(MODULE_SUBSCRIPTION, // - "Subscription \"" + info.displayName + "\": " + // - NEWLINE + " --> Last renewal time: " + lastRenewDate.toString() + // - NEWLINE + " --> Renew interval: " + QSTRN(info.updateInterval) + // - NEWLINE + " --> Ideal renew time: " + renewTime.toString()) // + const auto &renewTime = lastRenewDate.addSecs(info.subscriptionSettings.updateInterval * 86400); + LOG(MODULE_SUBSCRIPTION, // + "Subscription \"" + info.displayName + "\": " + // + NEWLINE + " --> Last renewal time: " + lastRenewDate.toString() + // + NEWLINE + " --> Renew interval: " + QSTRN(info.subscriptionSettings.updateInterval) + // + NEWLINE + " --> Ideal renew time: " + renewTime.toString()) // if (renewTime <= QDateTime::currentDateTime()) { diff --git a/src/ui/w_PreferencesWindow.cpp b/src/ui/w_PreferencesWindow.cpp index 4dca102d..190e729f 100644 --- a/src/ui/w_PreferencesWindow.cpp +++ b/src/ui/w_PreferencesWindow.cpp @@ -180,7 +180,7 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent), Current setTestLatenctCB->setChecked(CurrentConfig.advancedConfig.testLatencyPeriodcally); // DNSListTxt->clear(); - for (auto dnsStr : CurrentConfig.connectionConfig.dnsList) + for (const auto &dnsStr : CurrentConfig.connectionConfig.dnsList) { auto str = dnsStr.trimmed(); if (!str.isEmpty()) @@ -221,19 +221,19 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent), Current CurrentBarPageId = 0; // // Empty for global config. - auto autoStartConnId = ConnectionId(CurrentConfig.autoStartId); - auto autoStartGroupId = GetConnectionGroupId(autoStartConnId); + auto autoStartConnId = CurrentConfig.autoStartId.connectionId; + auto autoStartGroupId = CurrentConfig.autoStartId.groupId; - for (auto group : ConnectionManager->AllGroups()) + for (const auto &group : ConnectionManager->AllGroups()) { - autoStartSubsCombo->addItem(GetDisplayName(group)); + autoStartSubsCombo->addItem(GetDisplayName(group), group.toString()); } autoStartSubsCombo->setCurrentText(GetDisplayName(autoStartGroupId)); - for (auto conn : ConnectionManager->Connections(autoStartGroupId)) + for (const auto &conn : ConnectionManager->Connections(autoStartGroupId)) { - autoStartConnCombo->addItem(GetDisplayName(conn)); + autoStartConnCombo->addItem(GetDisplayName(conn), conn.toString()); } autoStartConnCombo->setCurrentText(GetDisplayName(autoStartConnId)); @@ -479,7 +479,7 @@ void PreferencesWindow::on_DNSListTxt_textChanged() QStringList hosts = DNSListTxt->toPlainText().replace("\r", "").split("\n"); CurrentConfig.connectionConfig.dnsList.clear(); - for (auto host : hosts) + for (const auto &host : hosts) { if (host != "" && host != "\r") { @@ -790,7 +790,7 @@ void PreferencesWindow::on_nsBarPagesList_currentRowChanged(int currentRow) if (!CurrentBarPage.Lines.empty()) { - for (auto line : CurrentBarPage.Lines) + for (const auto &line : CurrentBarPage.Lines) { auto description = GetBarLineDescription(line); nsBarLinesList->addItem(description); @@ -993,12 +993,6 @@ void PreferencesWindow::on_setSysProxyCB_stateChanged(int arg1) CurrentConfig.inboundConfig.setSystemProxy = arg1 == Qt::Checked; } -void PreferencesWindow::on_pushButton_clicked() -{ - LOADINGCHECK - QDesktopServices::openUrl(QUrl::fromUserInput(QV2RAY_RULES_DIR)); -} - void PreferencesWindow::on_autoStartSubsCombo_currentIndexChanged(const QString &arg1) { LOADINGCHECK if (arg1.isEmpty()) @@ -1008,13 +1002,12 @@ void PreferencesWindow::on_autoStartSubsCombo_currentIndexChanged(const QString } else { - auto groupId = ConnectionManager->GetGroupIdByDisplayName(arg1); - auto list = ConnectionManager->Connections(groupId); + auto list = ConnectionManager->Connections(GroupId(autoStartSubsCombo->currentData().toString())); autoStartConnCombo->clear(); - for (auto id : list) + for (const auto &id : list) { - autoStartConnCombo->addItem(GetDisplayName(id)); + autoStartConnCombo->addItem(GetDisplayName(id), id.toString()); } } } @@ -1030,9 +1023,12 @@ void PreferencesWindow::on_autoStartConnCombo_currentIndexChanged(const QString { // Fully qualify the connection item. // Will not work when duplicated names are in the same group. - CurrentConfig.autoStartId = - ConnectionManager->GetConnectionIdByDisplayName(arg1, ConnectionManager->GetGroupIdByDisplayName(autoStartSubsCombo->currentText())) - .toString(); + CurrentConfig.autoStartId.groupId = GroupId(autoStartSubsCombo->currentData().toString()); + CurrentConfig.autoStartId.connectionId = ConnectionId(autoStartConnCombo->currentData().toString()); + //= autoStartConnCombo->currentData().toString(); + // ConnectionManager->GetConnectionIdByDisplayName(arg1, + // ConnectionManager->GetGroupIdByDisplayName(autoStartSubsCombo->currentText())) + // .toString(); } } diff --git a/src/ui/w_PreferencesWindow.hpp b/src/ui/w_PreferencesWindow.hpp index 870d8767..7fe50d7c 100644 --- a/src/ui/w_PreferencesWindow.hpp +++ b/src/ui/w_PreferencesWindow.hpp @@ -122,8 +122,6 @@ class PreferencesWindow void on_setSysProxyCB_stateChanged(int arg1); - void on_pushButton_clicked(); - void on_autoStartSubsCombo_currentIndexChanged(const QString &arg1); void on_autoStartConnCombo_currentIndexChanged(const QString &arg1); diff --git a/src/ui/widgets/ConnectionInfoWidget.cpp b/src/ui/widgets/ConnectionInfoWidget.cpp index eb210a3c..2753a828 100644 --- a/src/ui/widgets/ConnectionInfoWidget.cpp +++ b/src/ui/widgets/ConnectionInfoWidget.cpp @@ -33,7 +33,7 @@ void ConnectionInfoWidget::UpdateColorScheme() auto isDarkTheme = GlobalConfig.uiConfig.useDarkTheme; qrPixmapBlured = BlurImage(ColorizeImage(qrPixmap, isDarkTheme ? QColor(Qt::black) : QColor(Qt::white), 0.7), 35); qrLabel->setPixmap(IsComplexConfig(connectionId) ? QPixmap(":/assets/icons/qv2ray.ico") : (isRealPixmapShown ? qrPixmap : qrPixmapBlured)); - connectBtn->setIcon(ConnectionManager->IsConnected(connectionId) ? QICON_R("stop.png") : QICON_R("connect.png")); + connectBtn->setIcon(KernelInstance->CurrentConnection().connectionId == connectionId ? QICON_R("stop.png") : QICON_R("connect.png")); } ConnectionInfoWidget::ConnectionInfoWidget(QWidget *parent) : QWidget(parent) @@ -57,10 +57,10 @@ ConnectionInfoWidget::ConnectionInfoWidget(QWidget *parent) : QWidget(parent) connect(ConnectionManager, &QvConfigHandler::OnConnectionGroupChanged, this, &ConnectionInfoWidget::OnConnectionModified); } -void ConnectionInfoWidget::ShowDetails(const std::tuple &_identifier) +void ConnectionInfoWidget::ShowDetails(const ConnectionGroupPair &_identifier) { - this->groupId = std::get<0>(_identifier); - this->connectionId = std::get<1>(_identifier); + this->groupId = _identifier.groupId; + this->connectionId = _identifier.connectionId; bool isConnection = connectionId != NullConnectionId; // editBtn->setEnabled(isConnection); @@ -90,7 +90,7 @@ void ConnectionInfoWidget::ShowDetails(const std::tuple & qrLabel->setPixmap(IsComplexConfig(connectionId) ? QPixmap(":/assets/icons/qv2ray.ico") : qrPixmapBlured); qrLabel->setScaledContents(true); // - connectBtn->setIcon(ConnectionManager->IsConnected(connectionId) ? QICON_R("stop.png") : QICON_R("connect.png")); + connectBtn->setIcon(KernelInstance->CurrentConnection().connectionId == connectionId ? QICON_R("stop.png") : QICON_R("connect.png")); } else { @@ -110,8 +110,8 @@ void ConnectionInfoWidget::ShowDetails(const std::tuple & } // groupShareTxt->setPlainText(shareLinks.join(NEWLINE)); - groupSubsLinkTxt->setText(ConnectionManager->IsSubscription(groupId) ? std::get<0>(ConnectionManager->GetSubscriptionData(groupId)) : - tr("Not a subscription")); + const auto &groupMetaData = ConnectionManager->GetGroupMetaObject(groupId); + groupSubsLinkTxt->setText(groupMetaData.isSubscription ? groupMetaData.subscriptionSettings.address : tr("Not a subscription")); } } @@ -122,7 +122,7 @@ ConnectionInfoWidget::~ConnectionInfoWidget() void ConnectionInfoWidget::OnConnectionModified(const ConnectionId &id) { if (id == connectionId) - ShowDetails({ GetConnectionGroupId(id), id }); + ShowDetails({ id, groupId }); } void ConnectionInfoWidget::OnGroupRenamed(const GroupId &id, const QString &oldName, const QString &newName) @@ -137,13 +137,13 @@ void ConnectionInfoWidget::OnGroupRenamed(const GroupId &id, const QString &oldN void ConnectionInfoWidget::on_connectBtn_clicked() { - if (ConnectionManager->IsConnected(connectionId)) + if (ConnectionManager->IsConnected({ connectionId, groupId })) { ConnectionManager->StopConnection(); } else { - ConnectionManager->StartConnection(connectionId); + ConnectionManager->StartConnection(connectionId, groupId); } } @@ -163,7 +163,7 @@ void ConnectionInfoWidget::on_deleteBtn_clicked() { if (connectionId != NullConnectionId) { - ConnectionManager->DeleteConnection(connectionId); + ConnectionManager->RemoveConnectionFromGroup(connectionId, groupId); } else { @@ -194,17 +194,17 @@ bool ConnectionInfoWidget::eventFilter(QObject *object, QEvent *event) return QWidget::eventFilter(object, event); } -void ConnectionInfoWidget::OnConnected(const ConnectionId &id) +void ConnectionInfoWidget::OnConnected(const ConnectionGroupPair &id) { - if (connectionId == id) + if (id == ConnectionGroupPair{ connectionId, groupId }) { connectBtn->setIcon(QICON_R("stop.png")); } } -void ConnectionInfoWidget::OnDisConnected(const ConnectionId &id) +void ConnectionInfoWidget::OnDisConnected(const ConnectionGroupPair &id) { - if (connectionId == id) + if (id == ConnectionGroupPair{ connectionId, groupId }) { connectBtn->setIcon(QICON_R("connect.png")); } @@ -212,7 +212,7 @@ void ConnectionInfoWidget::OnDisConnected(const ConnectionId &id) void ConnectionInfoWidget::on_latencyBtn_clicked() { - if (connectionId != NullConnectionId) + if (connectionId == NullConnectionId) { ConnectionManager->StartLatencyTest(connectionId); } diff --git a/src/ui/widgets/ConnectionInfoWidget.hpp b/src/ui/widgets/ConnectionInfoWidget.hpp index 8b75a8af..2e481d4c 100644 --- a/src/ui/widgets/ConnectionInfoWidget.hpp +++ b/src/ui/widgets/ConnectionInfoWidget.hpp @@ -14,7 +14,7 @@ class ConnectionInfoWidget public: explicit ConnectionInfoWidget(QWidget *parent = nullptr); - void ShowDetails(const std::tuple &_identifier); + void ShowDetails(const ConnectionGroupPair &_identifier); ~ConnectionInfoWidget(); signals: @@ -32,8 +32,8 @@ class ConnectionInfoWidget void OnConnectionModified(const ConnectionId &id); void OnGroupRenamed(const GroupId &id, const QString &oldName, const QString &newName); - void OnConnected(const ConnectionId &id); - void OnDisConnected(const ConnectionId &id); + void OnConnected(const ConnectionGroupPair &id); + void OnDisConnected(const ConnectionGroupPair &id); void on_latencyBtn_clicked(); private: diff --git a/src/ui/widgets/ConnectionItemWidget.cpp b/src/ui/widgets/ConnectionItemWidget.cpp index ebfa22e4..a2e424e6 100644 --- a/src/ui/widgets/ConnectionItemWidget.cpp +++ b/src/ui/widgets/ConnectionItemWidget.cpp @@ -14,10 +14,10 @@ ConnectionItemWidget::ConnectionItemWidget(QWidget *parent) : QWidget(parent), c connect(ConnectionManager, &QvConfigHandler::OnLatencyTestFinished, this, &ConnectionItemWidget::OnLatencyTestFinished); } -ConnectionItemWidget::ConnectionItemWidget(const ConnectionId &id, QWidget *parent) : ConnectionItemWidget(parent) +ConnectionItemWidget::ConnectionItemWidget(const ConnectionId &id, const GroupId &gid, QWidget *parent) : ConnectionItemWidget(parent) { connectionId = id; - groupId = GetConnectionGroupId(id); + groupId = gid; originalItemName = GetDisplayName(id); itemType = NODE_ITEM; // @@ -34,7 +34,7 @@ ConnectionItemWidget::ConnectionItemWidget(const ConnectionId &id, QWidget *pare auto [uplink, downlink] = GetConnectionUsageAmount(connectionId); dataLabel->setText(FormatBytes(uplink) + " / " + FormatBytes(downlink)); // - if (ConnectionManager->IsConnected(id)) + if (ConnectionManager->IsConnected({ id, gid })) { emit RequestWidgetFocus(this); } @@ -80,7 +80,7 @@ void ConnectionItemWidget::BeginConnection() { if (itemType == NODE_ITEM) { - ConnectionManager->StartConnection(connectionId); + ConnectionManager->StartConnection(connectionId, groupId); } else { @@ -88,19 +88,19 @@ void ConnectionItemWidget::BeginConnection() } } -void ConnectionItemWidget::OnConnected(const ConnectionId &id) +void ConnectionItemWidget::OnConnected(const ConnectionGroupPair &id) { - if (id == connectionId) + if (id == ConnectionGroupPair{ connectionId, groupId }) { connNameLabel->setText("• " + originalItemName); - LOG(MODULE_UI, "OnConnected signal received for: " + id.toString()) + LOG(MODULE_UI, "OnConnected signal received for: " + id.connectionId.toString()) emit RequestWidgetFocus(this); } } -void ConnectionItemWidget::OnDisConnected(const ConnectionId &id) +void ConnectionItemWidget::OnDisConnected(const ConnectionGroupPair &id) { - if (id == connectionId) + if (id == ConnectionGroupPair{ connectionId, groupId }) { connNameLabel->setText(originalItemName); } @@ -178,7 +178,7 @@ void ConnectionItemWidget::OnConnectionItemRenamed(const ConnectionId &id, const { if (id == connectionId) { - connNameLabel->setText((ConnectionManager->IsConnected(id) ? "• " : "") + newName); + connNameLabel->setText((ConnectionManager->IsConnected({ connectionId, groupId }) ? "• " : "") + newName); originalItemName = newName; this->setToolTip(newName); } diff --git a/src/ui/widgets/ConnectionItemWidget.hpp b/src/ui/widgets/ConnectionItemWidget.hpp index c55bbc00..a2dcf82e 100644 --- a/src/ui/widgets/ConnectionItemWidget.hpp +++ b/src/ui/widgets/ConnectionItemWidget.hpp @@ -17,7 +17,7 @@ class ConnectionItemWidget { Q_OBJECT public: - explicit ConnectionItemWidget(const ConnectionId &connecionId, QWidget *parent = nullptr); + explicit ConnectionItemWidget(const ConnectionId &id, const GroupId &gid, QWidget *parent = nullptr); explicit ConnectionItemWidget(const GroupId &groupId, QWidget *parent = nullptr); // void BeginConnection(); @@ -39,9 +39,12 @@ class ConnectionItemWidget return headerMatched || GetDisplayName(connectionId).toLower().contains(searchString); } } - inline const std::tuple Identifier() const + inline const ConnectionGroupPair Identifier() const { - return { groupId, connectionId }; + ConnectionGroupPair i; + i.connectionId = this->connectionId; + i.groupId = this->groupId; + return i; } inline bool IsRenaming() const { @@ -55,8 +58,8 @@ class ConnectionItemWidget void RequestWidgetFocus(const ConnectionItemWidget *me); private slots: void OnConnectionStatsArrived(const ConnectionId &id, const quint64 upS, const quint64 downS, const quint64 upD, const quint64 downD); - void OnConnected(const ConnectionId &id); - void OnDisConnected(const ConnectionId &id); + void OnConnected(const ConnectionGroupPair &id); + void OnDisConnected(const ConnectionGroupPair &id); void OnLatencyTestStart(const ConnectionId &id); void OnConnectionModified(const ConnectionId &id); void OnLatencyTestFinished(const ConnectionId &id, const uint average); From 277c5065f6ff2464df693f5beb90b6f8785eab3e Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Mon, 4 May 2020 23:45:25 +0800 Subject: [PATCH 049/385] =?UTF-8?q?Have=20sleep,=20=F0=9F=A4=AC=20It=20jus?= =?UTF-8?q?t=20compiles,=20but=20not=20working,=20DO=20NOT=20RUN?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A QHash's key type has additional requirements other than being an assignable data type: it must provide operator==(), and there must also be a qHash() function in the type's namespace that returns a hash value for an argument of the key's type. --- makespec/BUILDVERSION | 2 +- src/base/models/QvConfigIdentifier.hpp | 30 +- src/core/connection/Serialization.hpp | 2 +- src/core/handler/ConfigHandler.cpp | 33 ++- src/core/handler/ConfigHandler.hpp | 16 +- src/core/handler/KernelInstanceHandler.cpp | 4 +- src/core/handler/KernelInstanceHandler.hpp | 4 +- src/ui/w_GroupManager.cpp | 6 +- src/ui/w_MainWindow.cpp | 323 +++++++++++---------- src/ui/w_MainWindow.hpp | 10 +- src/ui/widgets/ConnectionInfoWidget.cpp | 6 +- src/ui/widgets/ConnectionItemWidget.cpp | 21 +- src/ui/widgets/ConnectionItemWidget.hpp | 4 +- 13 files changed, 233 insertions(+), 228 deletions(-) diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 0117a108..52d88fca 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5369 \ No newline at end of file +5370 \ No newline at end of file diff --git a/src/base/models/QvConfigIdentifier.hpp b/src/base/models/QvConfigIdentifier.hpp index 0d49841a..25b609f4 100644 --- a/src/base/models/QvConfigIdentifier.hpp +++ b/src/base/models/QvConfigIdentifier.hpp @@ -26,10 +26,10 @@ namespace Qv2ray::base { return m_id; } - uint qHash(uint seed = 0) const - { - return ::qHash(m_id, seed); - } + // uint qHash(uint seed = 0) const + // { + // return ::qHash(m_id, seed); + // } void loadJson(const QJsonValue &d) { m_id = d.toString("null"); @@ -119,19 +119,17 @@ namespace Qv2ray::base Qv2rayConnectionObject() : lastConnected(), latency(QVTCPING_VALUE_NODATA), upLinkData(0), downLinkData(0){}; JSONSTRUCT_REGISTER(Qv2rayConnectionObject, F(lastConnected, latency, upLinkData, downLinkData), B(__Qv2rayConfigObjectBase)) }; + + template + inline uint qHash(IDType key) + { + return ::qHash(key.toString()); + } + inline uint qHash(const Qv2ray::base::ConnectionGroupPair &pair) + { + return ::qHash(pair.connectionId.toString() + pair.groupId.toString()); + } } // namespace Qv2ray::base using namespace Qv2ray::base; Q_DECLARE_METATYPE(ConnectionGroupPair); -inline uint qHash(const ConnectionId &key, uint seed = 0) -{ - return qHash(key.toString(), seed); -} -inline uint qHash(const GroupId &key, uint seed = 0) -{ - return qHash(key.toString(), seed); -} -uint qHash(const Qv2ray::base::ConnectionGroupPair &pair) -{ - return qHash(pair.connectionId.toString() + pair.groupId.toString()); -} diff --git a/src/core/connection/Serialization.hpp b/src/core/connection/Serialization.hpp index 6000b77d..883095be 100644 --- a/src/core/connection/Serialization.hpp +++ b/src/core/connection/Serialization.hpp @@ -19,7 +19,7 @@ namespace Qv2ray::core::connection 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 ConnectionGroupPair &id, bool isSip002 = false); const QString ConvertConfigToString(const QString &alias, const QString &groupName, const CONFIGROOT &server, bool isSip002); // // VMess URI Protocol diff --git a/src/core/handler/ConfigHandler.cpp b/src/core/handler/ConfigHandler.cpp index 4261f58c..8fb05d7b 100644 --- a/src/core/handler/ConfigHandler.cpp +++ b/src/core/handler/ConfigHandler.cpp @@ -187,16 +187,16 @@ namespace Qv2ray::core::handlers { for (const auto &conn : groups[id].connections) { - ClearConnectionUsage(conn); + ClearConnectionUsage({ conn, id }); } } - void QvConfigHandler::ClearConnectionUsage(const ConnectionId &id) + void QvConfigHandler::ClearConnectionUsage(const ConnectionGroupPair &id) { - CheckConnectionExistanceEx(id, nothing); - connections[id].upLinkData = 0; - connections[id].downLinkData = 0; + CheckConnectionExistanceEx(id.connectionId, nothing); + connections[id.connectionId].upLinkData = 0; + connections[id.connectionId].downLinkData = 0; emit OnStatsAvailable(id, 0, 0, 0, 0); - PluginHost->Send_ConnectionStatsEvent({ GetDisplayName(id), 0, 0, 0, 0 }); + PluginHost->Send_ConnectionStatsEvent({ GetDisplayName(id.connectionId), 0, 0, 0, 0 }); return; } @@ -573,15 +573,20 @@ namespace Qv2ray::core::handlers return hasErrorOccured; } - void QvConfigHandler::OnStatsDataArrived_p(const ConnectionId &id, const quint64 uploadSpeed, const quint64 downloadSpeed) + void QvConfigHandler::OnStatsDataArrived_p(const ConnectionGroupPair &id, const quint64 uploadSpeed, const quint64 downloadSpeed) { - if (id == NullConnectionId) + if (id.isEmpty()) return; - 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 auto &connectionId = id.connectionId; + connections[connectionId].upLinkData += uploadSpeed; + connections[connectionId].downLinkData += downloadSpeed; + emit OnStatsAvailable(id, uploadSpeed, downloadSpeed, // + connections[connectionId].upLinkData, // + connections[connectionId].downLinkData); + PluginHost->Send_ConnectionStatsEvent({ GetDisplayName(connectionId), // + uploadSpeed, downloadSpeed, // + connections[connectionId].upLinkData, // + connections[connectionId].downLinkData }); } const ConnectionId QvConfigHandler::CreateConnection(const QString &displayName, const GroupId &groupId, const CONFIGROOT &root, @@ -592,7 +597,7 @@ namespace Qv2ray::core::handlers groups[groupId].connections << newId; connections[newId].creationDate = system_clock::to_time_t(system_clock::now()); connections[newId].displayName = displayName; - emit OnConnectionCreated(newId, groupId, displayName); + emit OnConnectionCreated({ newId, groupId }, displayName); PluginHost->Send_ConnectionEvent({ displayName, "", Events::ConnectionEntry::ConnectionEvent_Created }); UpdateConnection(newId, root); if (!skipSaveConfig) diff --git a/src/core/handler/ConfigHandler.hpp b/src/core/handler/ConfigHandler.hpp index c6fe9d83..f240e208 100644 --- a/src/core/handler/ConfigHandler.hpp +++ b/src/core/handler/ConfigHandler.hpp @@ -90,7 +90,7 @@ namespace Qv2ray::core::handlers // Connection Operations. bool UpdateConnection(const ConnectionId &id, const CONFIGROOT &root, bool skipRestart = false); void ClearGroupUsage(const GroupId &id); - void ClearConnectionUsage(const ConnectionId &id); + void ClearConnectionUsage(const ConnectionGroupPair &id); const std::optional RemoveConnectionFromGroup(const ConnectionId &id, const GroupId &gid); const std::optional RenameConnection(const ConnectionId &id, const QString &newName); const std::optional MoveConnectionGroup(const ConnectionId &id, const GroupId &newGroupId); @@ -118,13 +118,13 @@ namespace Qv2ray::core::handlers // const std::tuple GetSubscriptionData(const GroupId &id) const; signals: - 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 OnKernelLogAvailable(const ConnectionGroupPair &id, const QString &log); + void OnStatsAvailable(const ConnectionGroupPair &id, const quint64 upS, const quint64 downS, const quint64 upD, const quint64 downD); // - void OnConnectionRenamed(const ConnectionId &id, const QString &originalName, const QString &newName); - void OnConnectionCreated(const ConnectionId &id, const GroupId &groupId, const QString &displayName); - void OnConnectionDeleted(const ConnectionId &id, const GroupId &groupId); - void OnConnectionRemovedFromGroup(const ConnectionId &id, const GroupId &groupId); + void OnConnectionRenamed(const ConnectionId &Id, const QString &originalName, const QString &newName); + void OnConnectionCreated(const ConnectionGroupPair &Id, const QString &displayName); + void OnConnectionDeleted(const ConnectionGroupPair &Id); + void OnConnectionRemovedFromGroup(const ConnectionGroupPair &groupId); void OnConnectionModified(const ConnectionId &id); void OnConnectionGroupChanged(const ConnectionId &id, const GroupId &originalGroup, const GroupId &newGroup); // @@ -143,7 +143,7 @@ namespace Qv2ray::core::handlers private slots: void OnKernelCrashed_p(const ConnectionGroupPair &id, const QString &errMessage); void OnLatencyDataArrived_p(const QvTCPingResultObject &data); - void OnStatsDataArrived_p(const ConnectionId &id, const quint64 uploadSpeed, const quint64 downloadSpeed); + void OnStatsDataArrived_p(const ConnectionGroupPair &id, const quint64 uploadSpeed, const quint64 downloadSpeed); protected: void timerEvent(QTimerEvent *event) override; diff --git a/src/core/handler/KernelInstanceHandler.cpp b/src/core/handler/KernelInstanceHandler.cpp index 7eebd4cd..191b754b 100644 --- a/src/core/handler/KernelInstanceHandler.cpp +++ b/src/core/handler/KernelInstanceHandler.cpp @@ -257,7 +257,7 @@ namespace Qv2ray::core::handlers void KernelInstanceHandler::OnKernelLogAvailable_p(const QString &log) { - emit OnKernelLogAvailable(currentId.connectionId, log); + emit OnKernelLogAvailable(currentId, log); } void KernelInstanceHandler::StopConnection() @@ -293,7 +293,7 @@ namespace Qv2ray::core::handlers { if (isConnected) { - emit OnStatsDataAvailable(currentId.connectionId, uploadSpeed, downloadSpeed); + emit OnStatsDataAvailable(currentId, uploadSpeed, downloadSpeed); } } } // namespace Qv2ray::core::handlers diff --git a/src/core/handler/KernelInstanceHandler.hpp b/src/core/handler/KernelInstanceHandler.hpp index f5b5f404..504edfbe 100644 --- a/src/core/handler/KernelInstanceHandler.hpp +++ b/src/core/handler/KernelInstanceHandler.hpp @@ -35,8 +35,8 @@ namespace Qv2ray::core::handlers void OnConnected(const ConnectionGroupPair &id); void OnDisconnected(const ConnectionGroupPair &id); void OnCrashed(const ConnectionGroupPair &id, const QString &errMessage); - void OnStatsDataAvailable(const ConnectionId &id, const quint64 uploadSpeed, const quint64 downloadSpeed); - void OnKernelLogAvailable(const ConnectionId &id, const QString &log); + void OnStatsDataAvailable(const ConnectionGroupPair &id, const quint64 uploadSpeed, const quint64 downloadSpeed); + void OnKernelLogAvailable(const ConnectionGroupPair &id, const QString &log); private slots: void OnKernelCrashed_p(const QString &msg); diff --git a/src/ui/w_GroupManager.cpp b/src/ui/w_GroupManager.cpp index 4f1e6e7e..ead2191f 100644 --- a/src/ui/w_GroupManager.cpp +++ b/src/ui/w_GroupManager.cpp @@ -36,9 +36,9 @@ GroupManager::GroupManager(QWidget *parent) : QDialog(parent) this->loadConnectionList(currentGroupId); // }); // - const auto reloadGroupLambda = [&](const ConnectionId &, const GroupId &groupId) { - if (groupId == currentGroupId) - this->loadConnectionList(groupId); + const auto reloadGroupLambda = [&](const ConnectionGroupPair &id) { + if (id.groupId == currentGroupId) + this->loadConnectionList(id.groupId); }; connect(ConnectionManager, &QvConfigHandler::OnConnectionCreated, reloadGroupLambda); connect(ConnectionManager, &QvConfigHandler::OnConnectionDeleted, reloadGroupLambda); diff --git a/src/ui/w_MainWindow.cpp b/src/ui/w_MainWindow.cpp index 867d840d..6b32578d 100644 --- a/src/ui/w_MainWindow.cpp +++ b/src/ui/w_MainWindow.cpp @@ -74,24 +74,24 @@ void MainWindow::UpdateColorScheme() sortBtn->setIcon(QICON_R("sort.png")); } -void MainWindow::MWAddConnectionItem_p(const ConnectionId &connection, const GroupId &groupId) +void MainWindow::MWAddConnectionItem_p(const ConnectionGroupPair &id) { - if (!groupNodes.contains(groupId)) + if (!groupNodes.contains(id.groupId)) { - MWAddGroupItem_p(groupId); + MWAddGroupItem_p(id.groupId); } - auto groupItem = groupNodes.value(groupId); + auto groupItem = groupNodes.value(id.groupId); auto connectionItem = std::make_shared(QStringList{ - "", // - GetDisplayName(connection), // - NumericString(GetConnectionLatency(connection)), // - "IMPORTTIME_NOT_SUPPORTED", // - "LAST_CONNECTED_NOT_SUPPORTED", // - NumericString(GetConnectionTotalData(connection)) // + "", // + GetDisplayName(id.connectionId), // + NumericString(GetConnectionLatency(id.connectionId)), // + "IMPORTTIME_NOT_SUPPORTED", // + "LAST_CONNECTED_NOT_SUPPORTED", // + NumericString(GetConnectionTotalData(id.connectionId)) // }); - connectionNodes.insert({ connection, groupId }, connectionItem); + connectionNodes.insert(id, connectionItem); groupItem->addChild(connectionItem.get()); - auto widget = new ConnectionItemWidget(connection, groupId, connectionListWidget); + auto widget = new ConnectionItemWidget(id, connectionListWidget); connect(widget, &ConnectionItemWidget::RequestWidgetFocus, this, &MainWindow::OnConnectionWidgetFocusRequested); connectionListWidget->setItemWidget(connectionItem.get(), 0, widget); } @@ -303,9 +303,9 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) MWAddGroupItem_p(group); auto connections = ConnectionManager->Connections(group); - for (auto connection : connections) + for (const auto &connection : connections) { - MWAddConnectionItem_p(connection, group); + MWAddConnectionItem_p({ connection, group }); } } // @@ -741,7 +741,7 @@ void MainWindow::on_connectionListWidget_itemClicked(QTreeWidgetItem *item, int infoWidget->ShowDetails(widget->Identifier()); } -void MainWindow::OnStatsAvailable(const ConnectionId &id, const quint64 upS, const quint64 downS, const quint64 upD, const quint64 downD) +void MainWindow::OnStatsAvailable(const ConnectionGroupPair &id, const quint64 upS, const quint64 downS, const quint64 upD, const quint64 downD) { if (!ConnectionManager->IsConnected(id)) return; @@ -757,259 +757,260 @@ void MainWindow::OnStatsAvailable(const ConnectionId &id, const quint64 upS, con netspeedLabel->setText(totalSpeedUp + NEWLINE + totalSpeedDown); dataamountLabel->setText(totalDataUp + NEWLINE + totalDataDown); // - hTray.setToolTip(TRAY_TOOLTIP_PREFIX NEWLINE + tr("Connected: ") + GetDisplayName(id) + // + hTray.setToolTip(TRAY_TOOLTIP_PREFIX NEWLINE + tr("Connected: ") + GetDisplayName(id.connectionId) + // NEWLINE "Up: " + totalSpeedUp + " Down: " + totalSpeedDown); // // Set data accordingly - if (connectionNodes.contains({id, )) - { - connectionNodes.value(id)->setText(MW_ITEM_COL_DATA, NumericString(GetConnectionTotalData(id))); - } + if (connectionNodes.contains(id)) + { + connectionNodes.value(id)->setText(MW_ITEM_COL_DATA, NumericString(GetConnectionTotalData(id.connectionId))); + } } -void MainWindow::OnVCoreLogAvailable(const ConnectionId &id, const QString &log) +void MainWindow::OnVCoreLogAvailable(const ConnectionGroupPair &id, const QString &log) { - Q_UNUSED(id); - FastAppendTextDocument(log.trimmed(), vCoreLogDocument); - // vCoreLogDocument->setPlainText(vCoreLogDocument->toPlainText() + log); - // From https://gist.github.com/jemyzhang/7130092 - auto maxLines = GlobalConfig.uiConfig.maximumLogLines; - auto block = vCoreLogDocument->begin(); + Q_UNUSED(id); + FastAppendTextDocument(log.trimmed(), vCoreLogDocument); + // vCoreLogDocument->setPlainText(vCoreLogDocument->toPlainText() + log); + // From https://gist.github.com/jemyzhang/7130092 + auto maxLines = GlobalConfig.uiConfig.maximumLogLines; + auto block = vCoreLogDocument->begin(); - while (block.isValid()) + while (block.isValid()) + { + if (vCoreLogDocument->blockCount() > maxLines) { - if (vCoreLogDocument->blockCount() > maxLines) - { - QTextCursor cursor(block); - block = block.next(); - cursor.select(QTextCursor::BlockUnderCursor); - cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor); - cursor.removeSelectedText(); - continue; - } - - break; + QTextCursor cursor(block); + block = block.next(); + cursor.select(QTextCursor::BlockUnderCursor); + cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor); + cursor.removeSelectedText(); + continue; } + + break; + } } void MainWindow::OnEditRequested(const ConnectionId &id) { - auto outBoundRoot = ConnectionManager->GetConnectionRoot(id); - CONFIGROOT root; - bool isChanged = false; + auto outBoundRoot = ConnectionManager->GetConnectionRoot(id); + CONFIGROOT root; + bool isChanged = false; - if (IsComplexConfig(outBoundRoot)) - { - LOG(MODULE_UI, "INFO: Opening route editor.") - RouteEditor routeWindow(outBoundRoot, this); - root = routeWindow.OpenEditor(); - isChanged = routeWindow.result() == QDialog::Accepted; - } - else - { - LOG(MODULE_UI, "INFO: Opening single connection edit window.") - auto out = OUTBOUND(outBoundRoot["outbounds"].toArray().first().toObject()); - OutboundEditor w(out, this); - auto outboundEntry = w.OpenEditor(); - isChanged = w.result() == QDialog::Accepted; - QJsonArray outboundsList; - outboundsList.push_back(outboundEntry); - root.insert("outbounds", outboundsList); - } + if (IsComplexConfig(outBoundRoot)) + { + LOG(MODULE_UI, "INFO: Opening route editor.") + RouteEditor routeWindow(outBoundRoot, this); + root = routeWindow.OpenEditor(); + isChanged = routeWindow.result() == QDialog::Accepted; + } + else + { + LOG(MODULE_UI, "INFO: Opening single connection edit window.") + auto out = OUTBOUND(outBoundRoot["outbounds"].toArray().first().toObject()); + OutboundEditor w(out, this); + auto outboundEntry = w.OpenEditor(); + isChanged = w.result() == QDialog::Accepted; + QJsonArray outboundsList; + outboundsList.push_back(outboundEntry); + root.insert("outbounds", outboundsList); + } - if (isChanged) - { - ConnectionManager->UpdateConnection(id, root); - } + if (isChanged) + { + ConnectionManager->UpdateConnection(id, root); + } } void MainWindow::OnEditJsonRequested(const ConnectionId &id) { - JsonEditor w(ConnectionManager->GetConnectionRoot(id), this); - auto root = CONFIGROOT(w.OpenEditor()); + JsonEditor w(ConnectionManager->GetConnectionRoot(id), this); + auto root = CONFIGROOT(w.OpenEditor()); - if (w.result() == QDialog::Accepted) - { - ConnectionManager->UpdateConnection(id, root); - } + if (w.result() == QDialog::Accepted) + { + ConnectionManager->UpdateConnection(id, root); + } } -void MainWindow::OnConnectionCreated(const ConnectionId &id, const GroupId &groupId, const QString &displayName) +void MainWindow::OnConnectionCreated(const ConnectionGroupPair &id, const QString &displayName) { - Q_UNUSED(displayName) - MWAddConnectionItem_p(id, groupId); + Q_UNUSED(displayName) + MWAddConnectionItem_p(id); } -void MainWindow::OnConnectionDeleted(const ConnectionId &id, const GroupId &groupId) +void MainWindow::OnConnectionDeleted(const ConnectionGroupPair &id) { - auto child = connectionNodes.take(id); - groupNodes.value(groupId)->removeChild(child.get()); + auto child = connectionNodes.take(id); + groupNodes.value(id.groupId)->removeChild(child.get()); } void MainWindow::OnConnectionGroupChanged(const ConnectionId &id, const GroupId &originalGroup, const GroupId &newGroup) { - delete GetItemWidget(connectionNodes.value(id).get()); - groupNodes.value(originalGroup)->removeChild(connectionNodes.value(id).get()); - connectionNodes.remove(id); - MWAddConnectionItem_p(id, newGroup); + const ConnectionGroupPair pair{ id, originalGroup }; + delete GetItemWidget(connectionNodes.value(pair).get()); + groupNodes.value(originalGroup)->removeChild(connectionNodes.value(pair).get()); + connectionNodes.remove(pair); + MWAddConnectionItem_p({ id, newGroup }); } void MainWindow::OnGroupCreated(const GroupId &id, const QString &displayName) { - Q_UNUSED(displayName) - MWAddGroupItem_p(id); + Q_UNUSED(displayName) + MWAddGroupItem_p(id); } void MainWindow::OnGroupDeleted(const GroupId &id, const QList &connections) { - for (const auto &conn : connections) - { - groupNodes.value(id)->removeChild(connectionNodes.value(conn).get()); - } - groupNodes.remove(id); + for (const auto &conn : connections) + { + groupNodes.value(id)->removeChild(connectionNodes.value({ conn, id }).get()); + } + groupNodes.remove(id); } void MainWindow::on_locateBtn_clicked() { - auto id = KernelInstance->CurrentConnection(); - if (!id.isEmpty()) - { - connectionListWidget->setCurrentItem(connectionNodes.value(id).get()); - connectionListWidget->scrollToItem(connectionNodes.value(id).get()); - on_connectionListWidget_itemClicked(connectionNodes.value(id).get(), 0); - } + auto id = KernelInstance->CurrentConnection(); + if (!id.isEmpty()) + { + connectionListWidget->setCurrentItem(connectionNodes.value(id).get()); + connectionListWidget->scrollToItem(connectionNodes.value(id).get()); + on_connectionListWidget_itemClicked(connectionNodes.value(id).get(), 0); + } } void MainWindow::on_action_RCM_RenameThis_triggered() { - CheckCurrentWidget; - widget->BeginRename(); + CheckCurrentWidget; + widget->BeginRename(); } void MainWindow::on_action_RCM_DuplicateThese_triggered() { - QList connlist; + QList connlist; - for (auto item : connectionListWidget->selectedItems()) + for (auto item : connectionListWidget->selectedItems()) + { + auto widget = GetItemWidget(item); + if (widget->IsConnection()) { - auto widget = GetItemWidget(item); - if (widget->IsConnection()) - { - connlist.append(widget->Identifier()); - } + connlist.append(widget->Identifier()); } + } - LOG(MODULE_UI, "Selected " + QSTRN(connlist.count()) + " items") + LOG(MODULE_UI, "Selected " + QSTRN(connlist.count()) + " items") - if (connlist.count() > 1 && QvMessageBoxAsk(this, tr("Duplicating Connection(s)"), // - tr("Are you sure to duplicate these connection(s)?")) != QMessageBox::Yes) - { - return; - } + if (connlist.count() > 1 && QvMessageBoxAsk(this, tr("Duplicating Connection(s)"), // + tr("Are you sure to duplicate these connection(s)?")) != QMessageBox::Yes) + { + return; + } - for (const auto &conn : connlist) - { - ConnectionManager->CreateConnection(GetDisplayName(conn.connectionId) + tr(" (Copy)"), conn.groupId, - ConnectionManager->GetConnectionRoot(conn.connectionId)); - } + for (const auto &conn : connlist) + { + ConnectionManager->CreateConnection(GetDisplayName(conn.connectionId) + tr(" (Copy)"), conn.groupId, + ConnectionManager->GetConnectionRoot(conn.connectionId)); + } } void MainWindow::on_action_RCM_EditThis_triggered() { - CheckCurrentWidget; - OnEditRequested(widget->Identifier().connectionId); + CheckCurrentWidget; + OnEditRequested(widget->Identifier().connectionId); } void MainWindow::on_action_RCM_EditAsJson_triggered() { - CheckCurrentWidget; - OnEditJsonRequested(widget->Identifier().connectionId); + CheckCurrentWidget; + OnEditJsonRequested(widget->Identifier().connectionId); } void MainWindow::on_chartVisibilityBtn_clicked() { - speedChartHolderWidget->setVisible(!speedChartWidget->isVisible()); + speedChartHolderWidget->setVisible(!speedChartWidget->isVisible()); } void MainWindow::on_logVisibilityBtn_clicked() { - masterLogBrowser->setVisible(!masterLogBrowser->isVisible()); + masterLogBrowser->setVisible(!masterLogBrowser->isVisible()); } void MainWindow::on_clearChartBtn_clicked() { - speedChartWidget->Clear(); + speedChartWidget->Clear(); } void MainWindow::on_connectionListWidget_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous) { - Q_UNUSED(previous) - if (current != nullptr && !isExiting) - { - on_connectionListWidget_itemClicked(current, 0); - } + Q_UNUSED(previous) + if (current != nullptr && !isExiting) + { + on_connectionListWidget_itemClicked(current, 0); + } } void MainWindow::on_action_RCM_tovCoreLog_triggered() { - masterLogBrowser->setDocument(vCoreLogDocument); + masterLogBrowser->setDocument(vCoreLogDocument); } void MainWindow::on_action_RCM_toQvLog_triggered() { - masterLogBrowser->setDocument(qvLogDocument); + masterLogBrowser->setDocument(qvLogDocument); } void MainWindow::on_masterLogBrowser_textChanged() { - auto bar = masterLogBrowser->verticalScrollBar(); - bar->setValue(bar->maximum()); + auto bar = masterLogBrowser->verticalScrollBar(); + bar->setValue(bar->maximum()); } void MainWindow::on_action_RCM_SetAutoConnection_triggered() { - auto current = connectionListWidget->currentItem(); - if (current != nullptr) + auto current = connectionListWidget->currentItem(); + if (current != nullptr) + { + auto widget = GetItemWidget(current); + const auto identifier = widget->Identifier(); + GlobalConfig.autoStartId = identifier; + if (!GlobalConfig.uiConfig.quietMode) { - auto widget = GetItemWidget(current); - const auto identifier = widget->Identifier(); - GlobalConfig.autoStartId = identifier; - if (!GlobalConfig.uiConfig.quietMode) - { - hTray.showMessage(tr("Set auto connection"), tr("Set %1 as auto connect.").arg(GetDisplayName(identifier.connectionId))); - } - SaveGlobalSettings(); + hTray.showMessage(tr("Set auto connection"), tr("Set %1 as auto connect.").arg(GetDisplayName(identifier.connectionId))); } + SaveGlobalSettings(); + } } void MainWindow::on_action_RCM_ClearUsage_triggered() { - auto current = connectionListWidget->currentItem(); - if (current != nullptr) + auto current = connectionListWidget->currentItem(); + if (current != nullptr) + { + auto widget = GetItemWidget(current); + if (widget) { - auto widget = GetItemWidget(current); - if (widget) - { - if (widget->IsConnection()) - ConnectionManager->ClearConnectionUsage(widget->Identifier().connectionId); - else - ConnectionManager->ClearGroupUsage(widget->Identifier().groupId); - } + if (widget->IsConnection()) + ConnectionManager->ClearConnectionUsage(widget->Identifier()); + else + ConnectionManager->ClearGroupUsage(widget->Identifier().groupId); } + } } void MainWindow::on_action_RCM_LatencyTest_triggered() { - auto current = connectionListWidget->currentItem(); - if (current != nullptr) + auto current = connectionListWidget->currentItem(); + if (current != nullptr) + { + auto widget = GetItemWidget(current); + if (widget) { - auto widget = GetItemWidget(current); - if (widget) - { - if (widget->IsConnection()) - ConnectionManager->StartLatencyTest(widget->Identifier().connectionId); - else - ConnectionManager->StartLatencyTest(widget->Identifier().groupId); - } + if (widget->IsConnection()) + ConnectionManager->StartLatencyTest(widget->Identifier().connectionId); + else + ConnectionManager->StartLatencyTest(widget->Identifier().groupId); } + } } void MainWindow::on_pluginsBtn_clicked() { - PluginManageWindow(this).exec(); + PluginManageWindow(this).exec(); } diff --git a/src/ui/w_MainWindow.hpp b/src/ui/w_MainWindow.hpp index c70e1d91..36345c1f 100644 --- a/src/ui/w_MainWindow.hpp +++ b/src/ui/w_MainWindow.hpp @@ -86,11 +86,11 @@ class MainWindow void OnConnected(const ConnectionGroupPair &id); void OnDisconnected(const ConnectionGroupPair &id); // - void OnStatsAvailable(const ConnectionId &id, const quint64 upS, const quint64 downS, const quint64 upD, const quint64 downD); - void OnVCoreLogAvailable(const ConnectionId &id, const QString &log); + void OnStatsAvailable(const ConnectionGroupPair &id, const quint64 upS, const quint64 downS, const quint64 upD, const quint64 downD); + void OnVCoreLogAvailable(const ConnectionGroupPair &id, const QString &log); // - void OnConnectionCreated(const ConnectionId &id, const GroupId &groupId, const QString &displayName); - void OnConnectionDeleted(const ConnectionId &id, const GroupId &groupId); + void OnConnectionCreated(const ConnectionGroupPair &Id, const QString &displayName); + void OnConnectionDeleted(const ConnectionGroupPair &Id); void OnConnectionGroupChanged(const ConnectionId &id, const GroupId &originalGroup, const GroupId &newGroup); // void OnGroupCreated(const GroupId &id, const QString &displayName); @@ -166,6 +166,6 @@ class MainWindow // void UpdateColorScheme(); // - void MWAddConnectionItem_p(const ConnectionId &connection, const GroupId &groupId); + void MWAddConnectionItem_p(const ConnectionGroupPair &id); void MWAddGroupItem_p(const GroupId &groupId); }; diff --git a/src/ui/widgets/ConnectionInfoWidget.cpp b/src/ui/widgets/ConnectionInfoWidget.cpp index 2753a828..b5d9b9d2 100644 --- a/src/ui/widgets/ConnectionInfoWidget.cpp +++ b/src/ui/widgets/ConnectionInfoWidget.cpp @@ -69,7 +69,7 @@ void ConnectionInfoWidget::ShowDetails(const ConnectionGroupPair &_identifier) stackedWidget->setCurrentIndex(isConnection ? INDEX_CONNECTION : INDEX_GROUP); if (isConnection) { - auto shareLink = ConvertConfigToString(connectionId); + auto shareLink = ConvertConfigToString(_identifier); // shareLinkTxt->setText(shareLink); protocolLabel->setText(GetConnectionProtocolString(connectionId)); @@ -97,9 +97,9 @@ void ConnectionInfoWidget::ShowDetails(const ConnectionGroupPair &_identifier) connectBtn->setIcon(QICON_R("connect.png")); groupNameLabel->setText(GetDisplayName(groupId)); QStringList shareLinks; - for (auto connection : ConnectionManager->Connections(groupId)) + for (const auto &connection : ConnectionManager->Connections(groupId)) { - shareLinks << ConvertConfigToString(connection, false); + shareLinks << ConvertConfigToString({ connection, groupId }, false); } // auto complexCount = shareLinks.removeAll(QV2RAY_SERIALIZATION_COMPLEX_CONFIG_PLACEHOLDER); diff --git a/src/ui/widgets/ConnectionItemWidget.cpp b/src/ui/widgets/ConnectionItemWidget.cpp index a2e424e6..4fbf237e 100644 --- a/src/ui/widgets/ConnectionItemWidget.cpp +++ b/src/ui/widgets/ConnectionItemWidget.cpp @@ -14,31 +14,32 @@ ConnectionItemWidget::ConnectionItemWidget(QWidget *parent) : QWidget(parent), c connect(ConnectionManager, &QvConfigHandler::OnLatencyTestFinished, this, &ConnectionItemWidget::OnLatencyTestFinished); } -ConnectionItemWidget::ConnectionItemWidget(const ConnectionId &id, const GroupId &gid, QWidget *parent) : ConnectionItemWidget(parent) +ConnectionItemWidget::ConnectionItemWidget(const ConnectionGroupPair &id, QWidget *parent) : ConnectionItemWidget(parent) { - connectionId = id; - groupId = gid; - originalItemName = GetDisplayName(id); + connectionId = id.connectionId; + groupId = id.groupId; + originalItemName = GetDisplayName(id.connectionId); itemType = NODE_ITEM; // indentSpacer->changeSize(10, indentSpacer->sizeHint().height()); // - auto latency = GetConnectionLatency(id); + auto latency = GetConnectionLatency(id.connectionId); latencyLabel->setText(latency == QVTCPING_VALUE_NODATA ? // tr("Not Tested") : // (latency == QVTCPING_VALUE_ERROR ? // tr("Error") : // (QSTRN(latency) + " ms"))); // // - connTypeLabel->setText(tr("Type: ") + GetConnectionProtocolString(id)); + connTypeLabel->setText(tr("Type: ") + GetConnectionProtocolString(id.connectionId)); auto [uplink, downlink] = GetConnectionUsageAmount(connectionId); dataLabel->setText(FormatBytes(uplink) + " / " + FormatBytes(downlink)); // - if (ConnectionManager->IsConnected({ id, gid })) + if (ConnectionManager->IsConnected(id)) { emit RequestWidgetFocus(this); } - OnConnectionItemRenamed(id, "", originalItemName); + // Fake trigger + OnConnectionItemRenamed(id.connectionId, "", originalItemName); connect(ConnectionManager, &QvConfigHandler::OnConnectionRenamed, this, &ConnectionItemWidget::OnConnectionItemRenamed); // // Rename events @@ -106,13 +107,13 @@ void ConnectionItemWidget::OnDisConnected(const ConnectionGroupPair &id) } } -void ConnectionItemWidget::OnConnectionStatsArrived(const ConnectionId &id, const quint64 upSpeed, const quint64 downSpeed, +void ConnectionItemWidget::OnConnectionStatsArrived(const ConnectionGroupPair &id, const quint64 upSpeed, const quint64 downSpeed, const quint64 totalUp, const quint64 totalDown) { Q_UNUSED(upSpeed) Q_UNUSED(downSpeed) - if (id == connectionId) + if (id.connectionId == connectionId) { dataLabel->setText(FormatBytes(totalUp) + " / " + FormatBytes(totalDown)); } diff --git a/src/ui/widgets/ConnectionItemWidget.hpp b/src/ui/widgets/ConnectionItemWidget.hpp index a2dcf82e..4683b0c7 100644 --- a/src/ui/widgets/ConnectionItemWidget.hpp +++ b/src/ui/widgets/ConnectionItemWidget.hpp @@ -17,7 +17,7 @@ class ConnectionItemWidget { Q_OBJECT public: - explicit ConnectionItemWidget(const ConnectionId &id, const GroupId &gid, QWidget *parent = nullptr); + explicit ConnectionItemWidget(const ConnectionGroupPair &id, QWidget *parent = nullptr); explicit ConnectionItemWidget(const GroupId &groupId, QWidget *parent = nullptr); // void BeginConnection(); @@ -57,9 +57,9 @@ class ConnectionItemWidget signals: void RequestWidgetFocus(const ConnectionItemWidget *me); private slots: - void OnConnectionStatsArrived(const ConnectionId &id, const quint64 upS, const quint64 downS, const quint64 upD, const quint64 downD); void OnConnected(const ConnectionGroupPair &id); void OnDisConnected(const ConnectionGroupPair &id); + void OnConnectionStatsArrived(const ConnectionGroupPair &id, const quint64 upS, const quint64 downS, const quint64 upD, const quint64 downD); void OnLatencyTestStart(const ConnectionId &id); void OnConnectionModified(const ConnectionId &id); void OnLatencyTestFinished(const ConnectionId &id, const uint average); From aed6baa6b3b97a286cdfe5e51ccfc87ca6f90eb5 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Tue, 5 May 2020 00:07:47 +0800 Subject: [PATCH 050/385] remove: stopped supporting upgrading from version < v2.0.0-RC4 --- src/core/settings/SettingsUpgrade.cpp | 77 --------------------------- 1 file changed, 77 deletions(-) diff --git a/src/core/settings/SettingsUpgrade.cpp b/src/core/settings/SettingsUpgrade.cpp index b2710fb5..fc142c96 100644 --- a/src/core/settings/SettingsUpgrade.cpp +++ b/src/core/settings/SettingsUpgrade.cpp @@ -16,83 +16,6 @@ namespace Qv2ray auto root = original; switch (fromVersion) { - // Cases 1, 2, and 3 are not supported anymore. - // -------------------------------------------------------------------------------------- - // Below is for Qv2ray version 2 - // case 4: - // { - // // We changed the "proxyCN" to "bypassCN" as it's easier to - // // understand.... - // auto v2_oldProxyCN = root["proxyCN"].toBool(); - // // - // // From 3 to 4, we changed 'runAsRoot' to 'tProxySupport' - // auto v3_oldrunAsRoot = root["runAsRoot"].toBool(); - // root.insert("tProxySupport", v3_oldrunAsRoot); - // UPGRADELOG("Upgrading runAsRoot to tProxySupport, the value is not changed: " + QSTRN(v3_oldrunAsRoot)) - // // - // QString path; - // path = QV2RAY_DEFAULT_VCORE_PATH; - // root["v2CorePath"] = path; - // UPGRADELOG("Added v2CorePath to the config file.") - // // - // QJsonObject uiSettings; - // uiSettings["language"] = root["language"].toString("en-US").replace("-", "_"); - // root["uiConfig"] = uiSettings; - // // - // root["inboundConfig"] = root["inBoundSettings"]; - // root.remove("inBoundSettings"); - // UPGRADELOG("Renamed inBoundSettings to inboundConfig.") - // // - // // connectionConfig - // QJsonObject o; - // o["dnsList"] = root["dnsList"]; - // o["withLocalDNS"] = root["withLocalDNS"]; - // o["enableProxy"] = root["enableProxy"]; - // o["bypassCN"] = !v2_oldProxyCN; - // o["enableStats"] = true; - // o["statsPort"] = 13459; - // UPGRADELOG("Default statistics enabled.") - // root["connectionConfig"] = o; - // UPGRADELOG("Renamed some connection configs to connectionConfig.") - // // - // // Do we need renaming here? - // // //auto inbound = root["inboundConfig"].toObject(); - // // //auto pacConfig = inbound["pacConfig"].toObject(); - // // //pacConfig["enablePAC"] = pacConfig["usePAC"].toBool(); - // // //inbound["pacConfig"] = pacConfig; - // // //root["inboundConfig"] = inbound; - // // //UPDATELOG("Renamed usePAC to enablePAC.") - // // - // QJsonObject i; - // i["connectionName"] = root["autoStartConfig"].toString(); - // root["autoStartConfig"] = i; - // UPGRADELOG("Added subscription feature to autoStartConfig.") - // break; - // } - - // // Qv2ray version 2, RC 2 - // case 5: - // { - // // Added subscription auto update - // auto subs = root["subscribes"].toObject(); - // root.remove("subscribes"); - // QJsonObject newSubscriptions; - - // for (auto item = subs.begin(); item != subs.end(); item++) - // { - // auto key = item.key(); - // Qv2rayGroupConfigObject _conf; - // _conf..address = item.value().toString(); - // _conf.lastUpdated = system_clock::to_time_t(system_clock::now()); - // _conf.updateInterval = 5; - // newSubscriptions[key] = _conf.toJson(); - // } - - // root["subscriptions"] = newSubscriptions; - // UPGRADELOG("Added subscription renewal options.") - // break; - // } - // Qv2ray version 2, RC 4 case 6: { From 357c11f5d75477af3a1a74600d2e5ef2fc217d91 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Tue, 5 May 2020 00:28:27 +0800 Subject: [PATCH 051/385] add: added config file version migration TODO list --- src/core/settings/SettingsUpgrade.cpp | 44 ++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/src/core/settings/SettingsUpgrade.cpp b/src/core/settings/SettingsUpgrade.cpp index fc142c96..91ae1f35 100644 --- a/src/core/settings/SettingsUpgrade.cpp +++ b/src/core/settings/SettingsUpgrade.cpp @@ -213,7 +213,50 @@ namespace Qv2ray // Splitted Qv2ray.conf, case 11: { + // Moved root["connections"] into separated file: $QV2RAY_CONFIG_PATH/connections.json + /// + /// Connection.json + /// { + /// "connections" : [ + /// {connectionObject 1}, + /// {connectionObject 2}, + /// {connectionObject 3}, + /// {connectionObject 4}, + /// ] + /// } + /// + // Merged groups and subscriptions. $QV2RAY_GROUPS_PATH/+ groupId.json + /// + /// Group.json + /// { + /// GROUP_OBJECT + /// } + /// + // Config file migrations: + // Connection Object: + // importDate --> creationDate + // lastUpdatedDate --> now // + // Susbcription Object + // Doesn't exist anymore, convert into normal group Object. + // + // Group Object + // connections ---> ConnectionID + // idSubscription ---> if the group is a subscription + // subscriptionSettings ---> Originally SubscriptionObject + // creationDate ---> Now + // lastUpdateDate ---> Now + // + // Main Object + // Drop recentConnections since it's ill-formed and not supported yet. + // convert autoStartId into ConnectionGroupPair / instead of QString + // Remove subscriptions item. + // + // FileSystem Migrations + // Move all files in GROUPS / SUBSCRIPTION subfolders into CONNECTIONS. + // Only Store (connections.json in CONFIG_PATH) and ($groupID.json in GROUP_PATH) + // + abort(); } default: { @@ -230,7 +273,6 @@ namespace Qv2ray qApp->exit(1); } } - abort(); root["config_version"] = root["config_version"].toInt() + 1; return root; } From ec17b27afa96893b9b13a0161e5d0d5c1e81e3e8 Mon Sep 17 00:00:00 2001 From: tleydxdy Date: Tue, 5 May 2020 02:35:29 -0400 Subject: [PATCH 052/385] make vmess link parsing more robust --- src/core/connection/Serialization_vmess.cpp | 30 +++++---------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/src/core/connection/Serialization_vmess.cpp b/src/core/connection/Serialization_vmess.cpp index ebcdded3..4f47cbf8 100644 --- a/src/core/connection/Serialization_vmess.cpp +++ b/src/core/connection/Serialization_vmess.cpp @@ -111,28 +111,10 @@ namespace Qv2ray::core::connection *errMessage = QObject::tr("seems like a v1 vmess, we don't support it"); return default; } - bool flag = true; - // C is a quick hack... -#define C(k) (flag = (vmessConf.contains(k) ? (errMessage->clear(), true) : (*errMessage += (k " does not exist"), false))) - // id, aid, port and add are mandatory fields of a vmess:// - // link. - flag = flag && C("id") && (C("aid") || C("alterId")) && C("port") && C("add"); - // Stream Settings - auto net = vmessConf["net"].toString(); - - if (net == "http" || net == "ws") - flag = flag && C("host") && C("path"); - else if (net == "domainsocket") - flag = flag && C("path"); - else if (net == "quic") - flag = flag && C("host") && C("type") && C("path"); - -#undef C - // return flag ? 0 : 1; // -------------------------------------------------------------------------------------- CONFIGROOT root; - QString ps, add, id, /*net,*/ type, host, path, tls; + QString ps, add, id, net, type, host, path, tls; int port, aid; // // __vmess_checker__func(key, values) @@ -194,9 +176,10 @@ namespace Qv2ray::core::connection << "domainsocket" // << "quic"); // // - __vmess_checker__func(path, << ""); // - __vmess_checker__func(host, << ""); // - __vmess_checker__func(tls, << ""); // + __vmess_checker__func(tls, << "none" // + << "tls"); // + path = vmessConf.contains("path") ? vmessConf["path"].toVariant().toString() : (net == "quic" ? "" : "/"); + host = vmessConf.contains("host") ? vmessConf["host"].toVariant().toString() : (net == "quic" ? "none" : "/"); } // Repect connection type rather than obfs type // if (QStringList{ "srtp", "utp", "wechat-video" }.contains(type)) // @@ -250,7 +233,8 @@ namespace Qv2ray::core::connection } else if (net == "ws") { - streaming.wsSettings.headers["Host"] = host; + if (!host.isEmpty()) + streaming.wsSettings.headers["Host"] = host; streaming.wsSettings.path = path; } else if (net == "kcp") From 48cf6ffef698d7c3a7aaf76fb6cdf8d89060ca86 Mon Sep 17 00:00:00 2001 From: tleydxdy Date: Tue, 5 May 2020 02:59:57 -0400 Subject: [PATCH 053/385] fix typo --- src/core/connection/Serialization_vmess.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/connection/Serialization_vmess.cpp b/src/core/connection/Serialization_vmess.cpp index 4f47cbf8..04f18661 100644 --- a/src/core/connection/Serialization_vmess.cpp +++ b/src/core/connection/Serialization_vmess.cpp @@ -179,7 +179,7 @@ namespace Qv2ray::core::connection __vmess_checker__func(tls, << "none" // << "tls"); // path = vmessConf.contains("path") ? vmessConf["path"].toVariant().toString() : (net == "quic" ? "" : "/"); - host = vmessConf.contains("host") ? vmessConf["host"].toVariant().toString() : (net == "quic" ? "none" : "/"); + host = vmessConf.contains("host") ? vmessConf["host"].toVariant().toString() : (net == "quic" ? "none" : ""); } // Repect connection type rather than obfs type // if (QStringList{ "srtp", "utp", "wechat-video" }.contains(type)) // From 8e63a11c57e2b830f7afce3c1222b9ab675b1c0d Mon Sep 17 00:00:00 2001 From: DuckSoft Date: Tue, 5 May 2020 20:14:04 +0800 Subject: [PATCH 054/385] updating translations --- translations/ja_JP.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/translations/ja_JP.ts b/translations/ja_JP.ts index 2bef7e70..78a855d2 100644 --- a/translations/ja_JP.ts +++ b/translations/ja_JP.ts @@ -3972,7 +3972,7 @@ Maybe you have downloaded the wrong core? This group is a subscription - このグループはサブスクリプションです。 + このグループはサブスクリプションです Connection Settings From 7cce0cd40ff49aa58e42fb746474ff9ff8ded66b Mon Sep 17 00:00:00 2001 From: DuckSoft Date: Tue, 5 May 2020 21:13:01 +0800 Subject: [PATCH 055/385] trying to resolve GNOME HTTPS Proxy Problem --- src/components/proxy/QvProxyConfigurator.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/proxy/QvProxyConfigurator.cpp b/src/components/proxy/QvProxyConfigurator.cpp index f6fad937..cea25886 100644 --- a/src/components/proxy/QvProxyConfigurator.cpp +++ b/src/components/proxy/QvProxyConfigurator.cpp @@ -242,9 +242,9 @@ namespace Qv2ray::components::proxy { 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); + // GNOME HTTPS is problematic. disabling. + // 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:// From 407436ffba0329f3d6a0f42ac52b30f5c6432bdf Mon Sep 17 00:00:00 2001 From: ymshenyu Date: Tue, 5 May 2020 22:09:46 +0800 Subject: [PATCH 056/385] Revert "trying to resolve GNOME HTTPS Proxy Problem" This reverts commit 7cce0cd40ff49aa58e42fb746474ff9ff8ded66b. --- src/components/proxy/QvProxyConfigurator.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/proxy/QvProxyConfigurator.cpp b/src/components/proxy/QvProxyConfigurator.cpp index cea25886..f6fad937 100644 --- a/src/components/proxy/QvProxyConfigurator.cpp +++ b/src/components/proxy/QvProxyConfigurator.cpp @@ -242,9 +242,9 @@ namespace Qv2ray::components::proxy { 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); - // GNOME HTTPS is problematic. disabling. - // 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); + // + 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:// From a3ce8015d6483d03ca5a3dcf6bb317678fe37af7 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Wed, 6 May 2020 17:27:14 +0800 Subject: [PATCH 057/385] add: added default vmess "type" field --- src/core/connection/Serialization_vmess.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/core/connection/Serialization_vmess.cpp b/src/core/connection/Serialization_vmess.cpp index 04f18661..42f59376 100644 --- a/src/core/connection/Serialization_vmess.cpp +++ b/src/core/connection/Serialization_vmess.cpp @@ -52,6 +52,11 @@ namespace Qv2ray::core::connection vmessUriRoot["host"] = transfer.httpSettings.host.join(","); vmessUriRoot["path"] = transfer.httpSettings.path; } + + if(!vmessUriRoot.contains("type") || vmessUriRoot["type"].toString().isEmpty()) + { + vmessUriRoot["type"] = "none"; + } // auto vmessPart = Base64Encode(JsonToString(vmessUriRoot, QJsonDocument::JsonFormat::Compact)); From b5ec43c0df349747b80da2bbeb3a6bf02a73df4c Mon Sep 17 00:00:00 2001 From: DuckSoft Date: Wed, 6 May 2020 22:03:52 +0800 Subject: [PATCH 058/385] refactor: system-proxy/linux/set --- src/components/proxy/QvProxyConfigurator.cpp | 69 ++++++++++++-------- 1 file changed, 43 insertions(+), 26 deletions(-) diff --git a/src/components/proxy/QvProxyConfigurator.cpp b/src/components/proxy/QvProxyConfigurator.cpp index f6fad937..c38839b1 100644 --- a/src/components/proxy/QvProxyConfigurator.cpp +++ b/src/components/proxy/QvProxyConfigurator.cpp @@ -233,46 +233,62 @@ namespace Qv2ray::components::proxy actions << QString("gsettings set org.gnome.system.proxy mode '%1'").arg("manual"); bool isKDE = qEnvironmentVariable("XDG_SESSION_DESKTOP") == "KDE"; const auto configPath = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation); - if (isKDE) + + // Setting Proxy Mode to Manual { - LOG(MODULE_PROXY, "KDE detected") - actions << QString("kwriteconfig5 --file " + configPath + "/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); + // for GNOME: + { + actions << "gsettings set org.gnome.system.proxy mode 'manual'"; + } + + // for KDE: if (isKDE) { - // FTP here should be scheme: ftp:// - for (auto protocol : { "http", "ftp", "https" }) + LOG(MODULE_PROXY, "KDE detected") + actions << QString("kwriteconfig5 --file %1/kioslaverc --group 'Proxy Settings' --key ProxyType 1").arg(configPath); + } + } + + // Configure HTTP Proxies for HTTP, FTP and HTTPS + if (hasHTTP) + { + // iterate over protocols... + for (const auto protocol : { "http", "ftp", "https" }) + { + // for GNOME: { - auto str = - QString("kwriteconfig5 --file " + configPath + "/kioslaverc --group \"Proxy Settings\" --key %1Proxy \"http://%2 %3\"") - .arg(protocol) - .arg(address) - .arg(QSTRN(httpPort)); - actions << str; + actions << QString("gsettings set org.gnome.system.proxy.%1 host '%2'").arg(protocol, address); + actions << QString("gsettings set org.gnome.system.proxy.%1 port %2").arg(protocol, QSTRN(httpPort)); + } + + // for KDE: + if (isKDE) + { + actions << QString("kwriteconfig5 --file %1/kioslaverc --group 'Proxy Settings' --key %2Proxy 'http://%3 %4'") + .arg(configPath, protocol, address, QSTRN(httpPort)); } } } + // Configure SOCKS5 Proxies 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); + // for GNOME: + { + 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); + } + + // for KDE: if (isKDE) { - actions << QString("kwriteconfig5 --file " + configPath + - "/kioslaverc --group \"Proxy Settings\" --key socksProxy \"socks://%1 %2\"") - .arg(address) - .arg(QSTRN(socksPort)); + actions << QString("kwriteconfig5 --file %1/kioslaverc --group 'Proxy Settings' --key socksProxy 'socks://%2 %3'") + .arg(configPath, address, QSTRN(socksPort)); } } + // Execute them all! + // // note: do not use std::all_of / any_of / none_of, // because those are short-circuit and cannot guarantee atomicity. auto result = std::count_if(actions.cbegin(), actions.cend(), [](const QString &action) { @@ -286,7 +302,8 @@ namespace Qv2ray::components::proxy LOG(MODULE_PROXY, "It may happen if you are using KDE with no gsettings support.") } - Q_UNUSED(result); + // TODO: Post-settings for DDE + #else for (auto service : macOSgetNetworkServices()) From 0db5ae89a4dd91189c380cd711ec9eaa5a5e2b37 Mon Sep 17 00:00:00 2001 From: DuckSoft Date: Wed, 6 May 2020 23:04:17 +0800 Subject: [PATCH 059/385] more system proxy tweaks --- src/components/proxy/QvProxyConfigurator.cpp | 76 ++++++++++++-------- 1 file changed, 46 insertions(+), 30 deletions(-) diff --git a/src/components/proxy/QvProxyConfigurator.cpp b/src/components/proxy/QvProxyConfigurator.cpp index c38839b1..23dfc7fc 100644 --- a/src/components/proxy/QvProxyConfigurator.cpp +++ b/src/components/proxy/QvProxyConfigurator.cpp @@ -234,21 +234,6 @@ namespace Qv2ray::components::proxy bool isKDE = qEnvironmentVariable("XDG_SESSION_DESKTOP") == "KDE"; const auto configPath = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation); - // Setting Proxy Mode to Manual - { - // for GNOME: - { - actions << "gsettings set org.gnome.system.proxy mode 'manual'"; - } - - // for KDE: - if (isKDE) - { - LOG(MODULE_PROXY, "KDE detected") - actions << QString("kwriteconfig5 --file %1/kioslaverc --group 'Proxy Settings' --key ProxyType 1").arg(configPath); - } - } - // Configure HTTP Proxies for HTTP, FTP and HTTPS if (hasHTTP) { @@ -287,19 +272,37 @@ namespace Qv2ray::components::proxy } } + // Setting Proxy Mode to Manual + { + // for GNOME: + { + actions << "gsettings set org.gnome.system.proxy mode 'manual'"; + } + + // for KDE: + if (isKDE) + { + LOG(MODULE_PROXY, "KDE detected") + actions << QString("kwriteconfig5 --file %1/kioslaverc --group 'Proxy Settings' --key ProxyType 1").arg(configPath); + } + } + // Execute them all! // // note: do not use std::all_of / any_of / none_of, // because those are short-circuit and cannot guarantee atomicity. auto result = std::count_if(actions.cbegin(), actions.cend(), [](const QString &action) { - DEBUG(MODULE_PROXY, action) - return QProcess::execute(action) == QProcess::NormalExit; + // execute and get the code + const auto returnCode = QProcess::execute(action); + // print out the commands and result codes + DEBUG(MODULE_PROXY, QString("[%1] %2").arg(QSTRN(returnCode), action)) + // give the code back + return returnCode == QProcess::NormalExit; }) == actions.size(); if (!result) { - LOG(MODULE_PROXY, "There was something wrong when setting proxies.") - LOG(MODULE_PROXY, "It may happen if you are using KDE with no gsettings support.") + LOG(MODULE_PROXY, "Something wrong when setting proxies.") } // TODO: Post-settings for DDE @@ -340,6 +343,7 @@ namespace Qv2ray::components::proxy void ClearSystemProxy() { LOG(MODULE_PROXY, "Clearing System Proxy") + #ifdef Q_OS_WIN LOG(MODULE_PROXY, "Cleaning system proxy settings.") INTERNET_PER_CONN_OPTION_LIST list; @@ -370,21 +374,33 @@ namespace Qv2ray::components::proxy InternetSetOption(nullptr, INTERNET_OPTION_SETTINGS_CHANGED, nullptr, 0); InternetSetOption(nullptr, INTERNET_OPTION_REFRESH, nullptr, 0); #elif defined(Q_OS_LINUX) - if (qEnvironmentVariable("XDG_SESSION_DESKTOP") == "KDE") + QStringList actions; + const bool isKDE = qEnvironmentVariable("XDG_SESSION_DESKTOP") == "KDE"; + const auto configRoot = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation); + + // Setting System Proxy Mode to: None { - QProcess::execute("kwriteconfig5 --file " + QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + - "/kioslaverc --group \"Proxy Settings\" --key ProxyType 0"); - for (auto protocol : { "http", "ftp", "https" }) + // for GNOME: { - auto str = QString("kwriteconfig5 --file " + QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + - "/kioslaverc --group \"Proxy Settings\" --key %1Proxy ''") - .arg(protocol); - QProcess::execute(str); + actions << QString("gsettings set org.gnome.system.proxy mode 'none'"); + } + + // for KDE: + if (isKDE) + { + actions << QString("kwriteconfig5 --file %1/kioslaverc --group 'Proxy Settings' --key ProxyType 0").arg(configRoot); } - QProcess::execute("kwriteconfig5 --file " + QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + - "/kioslaverc --group \"Proxy Settings\" --key socksProxy ''"); } - QProcess::execute("gsettings set org.gnome.system.proxy mode 'none'"); + + // Execute the Actions + for (const auto &action : actions) + { + // execute and get the code + const auto returnCode = QProcess::execute(action); + // print out the commands and result codes + DEBUG(MODULE_PROXY, QString("[%1] %2").arg(QSTRN(returnCode), action)) + } + #else for (auto service : macOSgetNetworkServices()) { From a224bc98c50bafe4b8aa2304ac463b84993d1b9b Mon Sep 17 00:00:00 2001 From: DuckSoft Date: Thu, 7 May 2020 00:07:22 +0800 Subject: [PATCH 060/385] working around poisonous deepin proxy settings --- src/base/models/QvRuntimeConfig.hpp | 1 + src/components/proxy/QvProxyConfigurator.cpp | 22 +++++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/base/models/QvRuntimeConfig.hpp b/src/base/models/QvRuntimeConfig.hpp index d1dd033c..f572f88b 100644 --- a/src/base/models/QvRuntimeConfig.hpp +++ b/src/base/models/QvRuntimeConfig.hpp @@ -8,6 +8,7 @@ namespace Qv2ray::base struct Qv2rayRuntimeConfig { bool screenShotHideQv2ray = false; + bool deepinHorribleProxyHint = false; }; inline base::Qv2rayRuntimeConfig RuntimeConfig = base::Qv2rayRuntimeConfig(); } // namespace Qv2ray::base diff --git a/src/components/proxy/QvProxyConfigurator.cpp b/src/components/proxy/QvProxyConfigurator.cpp index 23dfc7fc..514b816d 100644 --- a/src/components/proxy/QvProxyConfigurator.cpp +++ b/src/components/proxy/QvProxyConfigurator.cpp @@ -232,6 +232,7 @@ namespace Qv2ray::components::proxy QStringList actions; actions << QString("gsettings set org.gnome.system.proxy mode '%1'").arg("manual"); bool isKDE = qEnvironmentVariable("XDG_SESSION_DESKTOP") == "KDE"; + bool isDDE = isKDE ? false : qEnvironmentVariable("XDG_SESSION_DESKTOP") == "DDE"; const auto configPath = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation); // Configure HTTP Proxies for HTTP, FTP and HTTPS @@ -305,7 +306,26 @@ namespace Qv2ray::components::proxy LOG(MODULE_PROXY, "Something wrong when setting proxies.") } - // TODO: Post-settings for DDE + // Post-Actions for HTTP on Deepin Desktop Environment. + if (isDDE && hasHTTP) + { + if (!RuntimeConfig.deepinHorribleProxyHint) + { + RuntimeConfig.deepinHorribleProxyHint = true; + + const auto deepinWarnTitle = QObject::tr("Deepin Detected"); + const auto deepinWarnMessage = + QObject::tr("Deepin plays smart and sets you the wrong HTTPS_PROXY environment variable. ") + NEWLINE + // + QObject::tr("The original scheme should be http://, but he will replace with https://, causing the problem. ") + NEWLINE + // + QObject::tr("Qv2ray will help you change it back and make things work again. "); + QvMessageBoxWarn(nullptr, deepinWarnTitle, deepinWarnMessage); + } + + // set them back! + const auto httpProxyURL = QString("http://%1:%2").arg(address, QSTRN(httpPort)).toStdString(); + setenv("https_proxy", httpProxyURL.c_str(), true); + setenv("ftp_proxy", httpProxyURL.c_str(), true); + } #else From 57ffa703a0c1c2a37335fb6c963c109cc82a1318 Mon Sep 17 00:00:00 2001 From: DuckSoft Date: Thu, 7 May 2020 00:44:08 +0800 Subject: [PATCH 061/385] updating translations for the deepin hint --- translations/en_US.ts | 16 ++++++++++++++++ translations/ja_JP.ts | 16 ++++++++++++++++ translations/ru_RU.ts | 16 ++++++++++++++++ translations/zh_CN.ts | 16 ++++++++++++++++ 4 files changed, 64 insertions(+) diff --git a/translations/en_US.ts b/translations/en_US.ts index 50c7fdb9..7bcc9616 100644 --- a/translations/en_US.ts +++ b/translations/en_US.ts @@ -2109,6 +2109,22 @@ For example, for updating subscriptions. Unknown + + Deepin Detected + + + + Deepin plays smart and sets you the wrong HTTPS_PROXY environment variable. + + + + The original scheme should be http://, but he will replace with https://, causing the problem. + + + + Qv2ray will help you change it back and make things work again. + + Qv2ray::common::QvCommandArgParser diff --git a/translations/ja_JP.ts b/translations/ja_JP.ts index 2bef7e70..a0ec5922 100644 --- a/translations/ja_JP.ts +++ b/translations/ja_JP.ts @@ -2913,6 +2913,22 @@ For example, for updating subscriptions. Unknown 不明 + + Deepin Detected + Deepin検出 + + + Deepin plays smart and sets you the wrong HTTPS_PROXY environment variable. + Deepinのバカ、間違ったHTTPS_PROXY環境変数を設定します。 + + + The original scheme should be http://, but he will replace with https://, causing the problem. + 本来のスキームは http:// であるべきなのですが、彼は https:// に置き換えて問題を引き起こします。 + + + Qv2ray will help you change it back and make things work again. + Qv2rayはそれを元に戻して、またうまくいくようにしてくれます。 + Qv2ray::common::QvCommandArgParser diff --git a/translations/ru_RU.ts b/translations/ru_RU.ts index 0a979c06..1ccbdfcf 100644 --- a/translations/ru_RU.ts +++ b/translations/ru_RU.ts @@ -2796,6 +2796,22 @@ For example, for updating subscriptions. Unknown + + Deepin Detected + + + + Deepin plays smart and sets you the wrong HTTPS_PROXY environment variable. + + + + The original scheme should be http://, but he will replace with https://, causing the problem. + + + + Qv2ray will help you change it back and make things work again. + + Qv2ray::common::QvCommandArgParser diff --git a/translations/zh_CN.ts b/translations/zh_CN.ts index 105023ed..ed8cbf39 100644 --- a/translations/zh_CN.ts +++ b/translations/zh_CN.ts @@ -2491,6 +2491,22 @@ For example, for updating subscriptions. Unknown 未知 + + Deepin Detected + 检测到 Deepin + + + Deepin plays smart and sets you the wrong HTTPS_PROXY environment variable. + 默认情况下,Deepin 自作聪明,会给你设置错误的 HTTPS_PROXY 环境变量。 + + + The original scheme should be http://, but he will replace with https://, causing the problem. + 原 scheme 应为 http://,但 Deepin 将其替换成了 https://,导致 HTTPS 代理无法正常使用。 + + + Qv2ray will help you change it back and make things work again. + Qv2ray 会尝试帮你改回去,以期解决此问题。 + Qv2ray::common::QvCommandArgParser From 2622664c617b0c9572c44afebf4bcc51935f8f2f Mon Sep 17 00:00:00 2001 From: DuckSoft Date: Thu, 7 May 2020 10:08:20 +0800 Subject: [PATCH 062/385] D E E P I N --- 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 514b816d..8c496f0a 100644 --- a/src/components/proxy/QvProxyConfigurator.cpp +++ b/src/components/proxy/QvProxyConfigurator.cpp @@ -232,7 +232,7 @@ namespace Qv2ray::components::proxy QStringList actions; actions << QString("gsettings set org.gnome.system.proxy mode '%1'").arg("manual"); bool isKDE = qEnvironmentVariable("XDG_SESSION_DESKTOP") == "KDE"; - bool isDDE = isKDE ? false : qEnvironmentVariable("XDG_SESSION_DESKTOP") == "DDE"; + bool isDDE = isKDE ? false : qEnvironmentVariable("XDG_SESSION_DESKTOP") == "deepin"; const auto configPath = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation); // Configure HTTP Proxies for HTTP, FTP and HTTPS From 5f46014b2317b970fe7247978bee033ace974118 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Fri, 8 May 2020 00:12:23 +0800 Subject: [PATCH 063/385] "I haven't implement the Connections Saving Feature" --- libs/QJsonStruct | 2 +- makespec/BUILDVERSION | 2 +- src/base/models/QvConfigIdentifier.hpp | 18 +- src/base/models/QvSettingsObject.hpp | 56 +++--- src/core/connection/Generation.cpp | 2 +- src/core/handler/ConfigHandler.cpp | 115 +++++------- src/core/handler/ConfigHandler.hpp | 8 +- src/core/kernel/APIBackend.cpp | 2 +- src/core/kernel/V2rayKernelInteractions.cpp | 2 +- src/core/settings/SettingsUpgrade.cpp | 184 +++++++++++++++----- src/ui/editors/w_JsonEditor.cpp | 2 +- src/ui/editors/w_JsonEditor.hpp | 2 +- src/ui/w_GroupManager.cpp | 4 +- src/ui/w_MainWindow_extra.cpp | 14 +- src/ui/w_PreferencesWindow.cpp | 10 +- src/ui/widgets/ConnectionInfoWidget.cpp | 2 +- 16 files changed, 246 insertions(+), 179 deletions(-) diff --git a/libs/QJsonStruct b/libs/QJsonStruct index 7f71fcde..09e5706d 160000 --- a/libs/QJsonStruct +++ b/libs/QJsonStruct @@ -1 +1 @@ -Subproject commit 7f71fcded7054888cd74fb205d2d718bbba0e5e7 +Subproject commit 09e5706dab5c7f221a5080ac430922857c25c94a diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 52d88fca..56457bc3 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5370 \ No newline at end of file +5377 \ No newline at end of file diff --git a/src/base/models/QvConfigIdentifier.hpp b/src/base/models/QvConfigIdentifier.hpp index 25b609f4..5383216b 100644 --- a/src/base/models/QvConfigIdentifier.hpp +++ b/src/base/models/QvConfigIdentifier.hpp @@ -26,10 +26,6 @@ namespace Qv2ray::base { return m_id; } - // uint qHash(uint seed = 0) const - // { - // return ::qHash(m_id, seed); - // } void loadJson(const QJsonValue &d) { m_id = d.toString("null"); @@ -98,16 +94,16 @@ namespace Qv2ray::base JSONSTRUCT_REGISTER(SubscriptionConfigObject, F(updateInterval, address)) }; - struct Qv2rayGroupConfigObject : __Qv2rayConfigObjectBase + struct GroupObject : __Qv2rayConfigObjectBase { QList connections; bool isSubscription; - SubscriptionConfigObject subscriptionSettings; - Qv2rayGroupConfigObject() : __Qv2rayConfigObjectBase(), connections(), isSubscription(false), subscriptionSettings(){}; - JSONSTRUCT_REGISTER(Qv2rayGroupConfigObject, F(connections, isSubscription, subscriptionSettings), B(__Qv2rayConfigObjectBase)) + SubscriptionConfigObject subscriptionOption; + GroupObject() : __Qv2rayConfigObjectBase(), connections(), isSubscription(false), subscriptionOption(){}; + JSONSTRUCT_REGISTER(GroupObject, F(connections, isSubscription, subscriptionOption), B(__Qv2rayConfigObjectBase)) }; - struct Qv2rayConnectionObject : __Qv2rayConfigObjectBase + struct ConnectionObject : __Qv2rayConfigObjectBase { qint64 lastConnected; qint64 latency; @@ -116,8 +112,8 @@ namespace Qv2ray::base // int __qvConnectionRefCount; // - Qv2rayConnectionObject() : lastConnected(), latency(QVTCPING_VALUE_NODATA), upLinkData(0), downLinkData(0){}; - JSONSTRUCT_REGISTER(Qv2rayConnectionObject, F(lastConnected, latency, upLinkData, downLinkData), B(__Qv2rayConfigObjectBase)) + ConnectionObject() : lastConnected(), latency(QVTCPING_VALUE_NODATA), upLinkData(0), downLinkData(0){}; + JSONSTRUCT_REGISTER(ConnectionObject, F(lastConnected, latency, upLinkData, downLinkData), B(__Qv2rayConfigObjectBase)) }; template diff --git a/src/base/models/QvSettingsObject.hpp b/src/base/models/QvSettingsObject.hpp index c2764ac7..ab3e1255 100644 --- a/src/base/models/QvSettingsObject.hpp +++ b/src/base/models/QvSettingsObject.hpp @@ -171,17 +171,11 @@ namespace Qv2ray::base::config JSONSTRUCT_REGISTER(Qv2rayConfig_Connection, F(bypassCN, bypassBT, enableProxy, v2rayFreedomDNS, withLocalDNS, dnsList, forwardProxyConfig, routeConfig)) }; - - struct Qv2rayConfig_API + struct Qv2rayConfig_Kernel { bool enableAPI; int statsPort; - Qv2rayConfig_API() : enableAPI(true), statsPort(15490){}; - JSONSTRUCT_REGISTER(Qv2rayConfig_API, F(enableAPI, statsPort)) - }; - - struct Qv2rayConfig_Kernel - { + // QString v2CorePath_linux; QString v2AssetsPath_linux; QString v2CorePath_macx; @@ -217,8 +211,11 @@ namespace Qv2ray::base::config #undef _VARNAME_VCOREPATH_ #undef _VARNAME_VASSETSPATH_ - JSONSTRUCT_REGISTER(Qv2rayConfig_Kernel, - F(v2CorePath_linux, v2AssetsPath_linux, v2CorePath_macx, v2AssetsPath_macx, v2CorePath_win, v2AssetsPath_win)) + JSONSTRUCT_REGISTER(Qv2rayConfig_Kernel, // + F(enableAPI, statsPort), // + F(v2CorePath_linux, v2AssetsPath_linux), // + F(v2CorePath_macx, v2AssetsPath_macx), // + F(v2CorePath_win, v2AssetsPath_win)) }; struct Qv2rayConfig_Update @@ -262,6 +259,13 @@ namespace Qv2ray::base::config JSONSTRUCT_REGISTER(Qv2rayConfig_Network, F(proxyType, type, address, port, userAgent)) }; + enum Qv2rayAutoConnectionBehavior + { + AUTO_CONNECTION_NONE = 0, + AUTO_CONNECTION_FIXED = 1, + AUTO_CONNECTION_LAST_CONNECTED = 2 + }; + struct Qv2rayConfigObject { int config_version; @@ -269,13 +273,13 @@ namespace Qv2ray::base::config int logLevel; // ConnectionGroupPair autoStartId; + Qv2rayAutoConnectionBehavior autoStartBehavior; // // Key = groupId, connectionId - QList groups; - QList connections; + // QList groups; + // QList connections; // Qv2rayConfig_UI uiConfig; - Qv2rayConfig_API apiConfig; Qv2rayConfig_Plugin pluginConfig; Qv2rayConfig_Kernel kernelConfig; Qv2rayConfig_Update updateConfig; @@ -291,9 +295,8 @@ namespace Qv2ray::base::config tProxySupport(false), // logLevel(), // autoStartId(), // - groups(), // + autoStartBehavior(), // uiConfig(), // - apiConfig(), // pluginConfig(), // kernelConfig(), // updateConfig(), // @@ -304,23 +307,10 @@ namespace Qv2ray::base::config advancedConfig(), // connectionConfig(){}; - JSONSTRUCT_REGISTER(Qv2rayConfigObject, - F(config_version, // - tProxySupport, // - logLevel, // - uiConfig, // - pluginConfig, // - updateConfig, // - kernelConfig, // - networkConfig, // - groups, // - autoStartId, // - inboundConfig, // - outboundConfig, // - connectionConfig, // - toolBarConfig, // - advancedConfig, // - apiConfig // - )) + JSONSTRUCT_REGISTER(Qv2rayConfigObject, // + F(config_version, tProxySupport, autoStartId, autoStartBehavior, logLevel), // + F(uiConfig, advancedConfig, pluginConfig, updateConfig, kernelConfig, networkConfig), // + F(inboundConfig, outboundConfig, connectionConfig), // + F(toolBarConfig)) }; } // namespace Qv2ray::base::config diff --git a/src/core/connection/Generation.cpp b/src/core/connection/Generation.cpp index 3406439c..a9755ad4 100644 --- a/src/core/connection/Generation.cpp +++ b/src/core/connection/Generation.cpp @@ -579,7 +579,7 @@ namespace Qv2ray::core::connection INBOUNDSETTING fakeDocodemoDoor; fakeDocodemoDoor["address"] = "127.0.0.1"; QJsonObject apiInboundsRoot = - GenerateInboundEntry("127.0.0.1", GlobalConfig.apiConfig.statsPort, "dokodemo-door", fakeDocodemoDoor, API_TAG_INBOUND); + GenerateInboundEntry("127.0.0.1", GlobalConfig.kernelConfig.statsPort, "dokodemo-door", fakeDocodemoDoor, API_TAG_INBOUND); inbounds.push_front(apiInboundsRoot); root["inbounds"] = inbounds; // diff --git a/src/core/handler/ConfigHandler.cpp b/src/core/handler/ConfigHandler.cpp index 8fb05d7b..94236576 100644 --- a/src/core/handler/ConfigHandler.cpp +++ b/src/core/handler/ConfigHandler.cpp @@ -7,74 +7,51 @@ namespace Qv2ray::core::handlers { - QvConfigHandler::QvConfigHandler() { DEBUG(MODULE_CORE_HANDLER, "ConnectionHandler Constructor.") + const auto connectionJson = JsonFromString(StringFromFile(QV2RAY_CONFIG_DIR + "connections.json")); + const auto groupJson = JsonFromString(StringFromFile(QV2RAY_CONFIG_DIR + "groups.json")); + // + for (const auto &connectionId : connectionJson.keys()) + { + connections.insert(ConnectionId{ connectionId }, ConnectionObject::fromJson(connectionJson.value(connectionId).toObject())); + } + // + for (const auto &groupId : groupJson.keys()) + { + const auto groupObject = GroupObject::fromJson(groupJson.value(groupId).toObject()); + groups.insert(GroupId{ groupId }, groupObject); + // + for (const auto &connId : groupObject.connections) + { + connections[connId].__qvConnectionRefCount++; + } + } + // + for (const auto &id : connections.keys()) + { + auto const &connectionObject = connections.value(id); + if (connectionObject.__qvConnectionRefCount == 0) + { + connections.remove(id); + LOG(MODULE_CORE_HANDLER, "Dropped connection id: " + id.toString() + " since it's not in a group") + } + else + { + const auto connectionFilePath = QV2RAY_CONNECTIONS_DIR + id.toString() + QV2RAY_CONFIG_FILE_EXTENSION; + connectionRootCache[id] = CONFIGROOT(JsonFromString(StringFromFile(connectionFilePath))); + DEBUG(MODULE_CORE_HANDLER, "Loaded connection id: " + id.toString() + " into cache.") + } + } - // // Do we need to check how many of them are loaded? - // // Do not use: for (const auto &key : connections), why? - // for (auto i = 0; i < GlobalConfig.connectionConfig.count(); i++) - // { - // auto const &id = ConnectionId(GlobalConfig.connections.keys().at(i)); - // connections[id] = GlobalConfig.connections.values().at(i); - // } - // - // for (const auto &key : GlobalConfig.subscriptions.keys()) - // { - // GroupId gkey(key); - // if (gkey == NullGroupId) - // { - // LOG(MODULE_CORE_HANDLER, "Removed a null subscription id") - // continue; - // } - // auto const &val = GlobalConfig.subscriptions[key]; - // groups[gkey] = val; - // - // for (auto conn : val.connections) - // { - // connections[ConnectionId(conn)].groupId = GroupId(key); - // } - // } - // - // for (const auto &key : GlobalConfig.groups ) - // { - // GroupId gkey(key); - // if (gkey == NullGroupId) - // { - // LOG(MODULE_CORE_HANDLER, "Removed a null group id") - // continue; - // } - // auto const &val = GlobalConfig.groups.value(key); - // groups[gkey] = val; - // - // for (auto conn : val.connections) - // { - // connections[ConnectionId(conn)].groupId = GroupId(key); - // } - // } - // - // for (const auto &id : connections.keys()) - // { - // DEBUG(MODULE_CORE_HANDLER, "Loading connection: " + connections.value(id).displayName + " to cache.") - // auto const &group = connections.value(id).groupId; - // if (group != NullGroupId) - // { - // auto path = group.toString() + "/" + id.toString() + QV2RAY_CONFIG_FILE_EXTENSION; - // path.prepend(groups[group].isSubscription ? QV2RAY_SUBSCRIPTION_DIR : QV2RAY_CONNECTIONS_DIR); - // // - // connectionRootCache[id] = CONFIGROOT(JsonFromString(StringFromFile(path))); - // } - // else - // { - // connections.remove(id); - // LOG(MODULE_CORE_HANDLER, "Dropped connection id: " + id.toString() + " since it's not in a group") - // } - // } - // // Force default group name. - groups[DefaultGroupId].displayName = tr("Default Group"); - groups[DefaultGroupId].isSubscription = false; + if (!groups.contains(DefaultGroupId)) + { + groups.insert(DefaultGroupId, {}); + groups[DefaultGroupId].displayName = tr("Default Group"); + groups[DefaultGroupId].isSubscription = false; + } // kernelHandler = new KernelInstanceHandler(this); connect(kernelHandler, &KernelInstanceHandler::OnCrashed, this, &QvConfigHandler::OnKernelCrashed_p); @@ -96,8 +73,10 @@ namespace Qv2ray::core::handlers void QvConfigHandler::CHSaveConfigData() { // Do not copy construct. - GlobalConfig.connections = connections.keys(); - GlobalConfig.groups = groups.keys(); + // GlobalConfig.connections = connections.keys(); + // GlobalConfig.groups = groups.keys(); + // +#error I haven't implement the Connections Saving Feature!! SaveGlobalSettings(); } @@ -440,11 +419,11 @@ namespace Qv2ray::core::handlers groups[id].isSubscription = isSubscription; if (!address.isEmpty()) { - groups[id].subscriptionSettings.address = address; + groups[id].subscriptionOption.address = address; } if (updateInterval != -1) { - groups[id].subscriptionSettings.updateInterval = updateInterval; + groups[id].subscriptionOption.updateInterval = updateInterval; } return true; } @@ -457,7 +436,7 @@ namespace Qv2ray::core::handlers return false; } isHttpRequestInProgress = true; - auto data = httpHelper->Get(groups[id].subscriptionSettings.address); + auto data = httpHelper->Get(groups[id].subscriptionOption.address); isHttpRequestInProgress = false; return CHUpdateSubscription_p(id, data); } diff --git a/src/core/handler/ConfigHandler.hpp b/src/core/handler/ConfigHandler.hpp index f240e208..8327d9e5 100644 --- a/src/core/handler/ConfigHandler.hpp +++ b/src/core/handler/ConfigHandler.hpp @@ -50,12 +50,12 @@ namespace Qv2ray::core::handlers { return groups.keys(); } - inline const Qv2rayConnectionObject GetConnectionMetaObject(const ConnectionId &id) const + inline const ConnectionObject GetConnectionMetaObject(const ConnectionId &id) const { CheckConnectionExistanceEx(id, {}); return connections[id]; } - inline Qv2rayGroupConfigObject GetGroupMetaObject(const GroupId &id) const + inline GroupObject GetGroupMetaObject(const GroupId &id) const { CheckGroupExistanceEx(id, {}); return groups[id]; @@ -155,8 +155,8 @@ namespace Qv2ray::core::handlers int saveTimerId; int pingAllTimerId; int pingConnectionTimerId; - QHash groups; - QHash connections; + QHash groups; + QHash connections; QHash connectionRootCache; private: diff --git a/src/core/kernel/APIBackend.cpp b/src/core/kernel/APIBackend.cpp index 2a86b7e6..86b158ab 100644 --- a/src/core/kernel/APIBackend.cpp +++ b/src/core/kernel/APIBackend.cpp @@ -73,7 +73,7 @@ namespace Qv2ray::core::kernel { if (!dialed) { - auto channelAddress = "127.0.0.1:" + QString::number(GlobalConfig.apiConfig.statsPort); + auto channelAddress = "127.0.0.1:" + QString::number(GlobalConfig.kernelConfig.statsPort); #ifndef BACKEND_LIBQVB LOG(MODULE_VCORE, "gRPC Version: " + QString::fromStdString(grpc::Version())) Channel = grpc::CreateChannel(channelAddress.toStdString(), grpc::InsecureChannelCredentials()); diff --git a/src/core/kernel/V2rayKernelInteractions.cpp b/src/core/kernel/V2rayKernelInteractions.cpp index f2dcfc7c..b95c5cf1 100644 --- a/src/core/kernel/V2rayKernelInteractions.cpp +++ b/src/core/kernel/V2rayKernelInteractions.cpp @@ -239,7 +239,7 @@ namespace Qv2ray::core::kernel { LOG(MODULE_VCORE, "API has been disabled by the command line argument \"-noAPI\"") } - else if (!GlobalConfig.apiConfig.enableAPI) + else if (!GlobalConfig.kernelConfig.enableAPI) { LOG(MODULE_VCORE, "API has been disabled by the global config option") } diff --git a/src/core/settings/SettingsUpgrade.cpp b/src/core/settings/SettingsUpgrade.cpp index 91ae1f35..c8e0059f 100644 --- a/src/core/settings/SettingsUpgrade.cpp +++ b/src/core/settings/SettingsUpgrade.cpp @@ -1,4 +1,4 @@ -// +// // This file handles some important migration // from old to newer versions of Qv2ray. // @@ -100,7 +100,7 @@ namespace Qv2ray for (auto i = 0; i < rootSubscriptions.count(); i++) { - auto key = rootSubscriptions.keys()[i]; + auto key = rootSubscriptions.keys().at(i); auto value = rootSubscriptions.value(key); // UPGRADELOG("Upgrading subscription: " + key) @@ -125,7 +125,7 @@ namespace Qv2ray auto fileList = GetFileList(baseDirPath); // Copy every file within a subscription. - for (auto fileName : fileList) + for (const auto &fileName : fileList) { auto subsConnectionId = GenerateUuid(); auto baseFilePath = baseDirPath + "/" + fileName; @@ -213,50 +213,152 @@ namespace Qv2ray // Splitted Qv2ray.conf, case 11: { - // Moved root["connections"] into separated file: $QV2RAY_CONFIG_PATH/connections.json - /// - /// Connection.json - /// { - /// "connections" : [ - /// {connectionObject 1}, - /// {connectionObject 2}, - /// {connectionObject 3}, - /// {connectionObject 4}, - /// ] - /// } - /// - // Merged groups and subscriptions. $QV2RAY_GROUPS_PATH/+ groupId.json - /// - /// Group.json - /// { - /// GROUP_OBJECT - /// } - /// - // Config file migrations: - // Connection Object: - // importDate --> creationDate - // lastUpdatedDate --> now + // Process AutoStartSettings + ConnectionGroupPair autoStartIdPair{ ConnectionId{ root["autoStartId"].toString() }, NullGroupId }; + + // Process connection entries. // - // Susbcription Object - // Doesn't exist anymore, convert into normal group Object. - // - // Group Object - // connections ---> ConnectionID - // idSubscription ---> if the group is a subscription - // subscriptionSettings ---> Originally SubscriptionObject - // creationDate ---> Now - // lastUpdateDate ---> Now + { + // Moved root["connections"] into separated file: $QV2RAY_CONFIG_PATH/connections.json + QDir connectionsDir(QV2RAY_CONNECTIONS_DIR); + if (!connectionsDir.exists()) + { + connectionsDir.mkpath(QV2RAY_CONNECTIONS_DIR); + } + const auto connectionsArray = root["connections"].toObject().keys(); + QJsonObject newConnectionsArray; + /// + /// Connection.json + /// { + /// "connections" : [ + /// {ID1, connectionObject 1}, + /// {ID2, connectionObject 2}, + /// {ID3, connectionObject 3}, + /// {ID4, connectionObject 4}, + /// ] + /// } + /// + for (const auto &connectionVal : connectionsArray) + { + // Config file migrations: + // Connection Object: + // importDate --> creationDate + // lastUpdatedDate --> now + // + auto connection = root["connections"].toObject()[connectionVal].toObject(); + connection["creationDate"] = connection.take("importDate"); + connection["lastUpdatedDate"] = (qint64) system_clock::to_time_t(system_clock::now()); + UPGRADELOG("Migrating connection: " + connectionVal + " -- " + connection["displayName"].toString()) + newConnectionsArray[connectionVal] = connection; + } + QJsonObject ConnectionJsonObject; + root["connections"] = QJsonArray::fromStringList(connectionsArray); + // + // Store Connection.json + StringToFile(JsonToString(newConnectionsArray), QV2RAY_CONFIG_DIR + "connections.json"); + } + // Merged groups and subscriptions. $QV2RAY_GROUPS_PATH + groupId.json + { + // Susbcription Object + // Doesn't exist anymore, convert into normal group Object. + // + QMap ConnectionsCache; + QJsonObject allGroupsObject; + const auto subscriptionKeys = root["subscriptions"].toObject().keys(); + for (const auto &key : subscriptionKeys) + { + auto aSubscription = root["subscriptions"].toObject()[key].toObject(); + QJsonObject subscriptionSettings; + subscriptionSettings["address"] = aSubscription.take("address"); + subscriptionSettings["updateInterval"] = aSubscription.take("updateInterval"); + aSubscription["lastUpdatedDate"] = (qint64) system_clock::to_time_t(system_clock::now()); + aSubscription["creationDate"] = (qint64) system_clock::to_time_t(system_clock::now()); + aSubscription["subscriptionOption"] = subscriptionSettings; + UPGRADELOG("Migrating subscription: " + key + " -- " + aSubscription["displayName"].toString()) + // + if (autoStartIdPair.groupId != NullGroupId && + aSubscription["connections"].toArray().contains(autoStartIdPair.connectionId.toString())) + { + autoStartIdPair.groupId = GroupId{ key }; + } + // + for (const auto &cid : aSubscription["connections"].toArray()) + { + ConnectionsCache[cid.toString()] = JsonFromString(StringFromFile(QV2RAY_CONFIG_DIR + "subscriptions/" + key + "/" + + cid.toString() + QV2RAY_CONFIG_FILE_EXTENSION)); + } + // + allGroupsObject[key] = aSubscription; + } + // + root.remove("subscriptions"); + // + const auto groupKeys = root["groups"].toObject().keys(); + for (const auto &key : groupKeys) + { + // Group Object + // connections ---> ConnectionID + // idSubscription ---> if the group is a subscription + // subscriptionSettings ---> Originally SubscriptionObject + // creationDate ---> Now + // lastUpdateDate ---> Now + auto aGroup = root["groups"].toObject()[key].toObject(); + aGroup["lastUpdatedDate"] = (qint64) system_clock::to_time_t(system_clock::now()); + aGroup["creationDate"] = (qint64) system_clock::to_time_t(system_clock::now()); + UPGRADELOG("Migrating group: " + key + " -- " + aGroup["displayName"].toString()) + // + if (autoStartIdPair.groupId != NullGroupId && + aGroup["connections"].toArray().contains(autoStartIdPair.connectionId.toString())) + { + autoStartIdPair.groupId = GroupId{ key }; + } + for (const auto &cid : aGroup["connections"].toArray()) + { + ConnectionsCache[cid.toString()] = JsonFromString( + StringFromFile(QV2RAY_CONFIG_DIR + "connections/" + key + "/" + cid.toString() + QV2RAY_CONFIG_FILE_EXTENSION)); + } + // + allGroupsObject[key] = aGroup; + } + // + StringToFile(JsonToString(allGroupsObject), QV2RAY_CONFIG_DIR + "groups.json"); + // + root.remove("groups"); // + UPGRADELOG("Removing unused directory") + QDir(QV2RAY_CONFIG_DIR + "subscriptions/").removeRecursively(); + QDir(QV2RAY_CONFIG_DIR + "connections/").removeRecursively(); + // + QDir().mkpath(QV2RAY_CONFIG_DIR + "connections/"); + // + // + // FileSystem Migrations + // Move all files in GROUPS / SUBSCRIPTION subfolders into CONNECTIONS. + // Only Store (connections.json in CONFIG_PATH) and ($groupID.json in GROUP_PATH) + for (const auto &cid : ConnectionsCache.keys()) + { + StringToFile(JsonToString(ConnectionsCache[cid]), + QV2RAY_CONFIG_DIR + "connections/" + cid + QV2RAY_CONFIG_FILE_EXTENSION); + } + // + } + // // Main Object // Drop recentConnections since it's ill-formed and not supported yet. // convert autoStartId into ConnectionGroupPair / instead of QString // Remove subscriptions item. - // - // FileSystem Migrations - // Move all files in GROUPS / SUBSCRIPTION subfolders into CONNECTIONS. - // Only Store (connections.json in CONFIG_PATH) and ($groupID.json in GROUP_PATH) - // - abort(); + root.remove("recentConnections"); + root["autoStartId"] = autoStartIdPair.toJson(); + // 1 here means FIXED + root["autoStartBehavior"] = 1; + + // Moved apiConfig into kernelConfig + auto kernelConfig = root["kernelConfig"].toObject(); + kernelConfig["enableAPI"] = root["enableAPI"]; + kernelConfig["statsPort"] = root["statsPort"]; + UPGRADELOG("Finished upgrading config file for Qv2ray Group Routing update.") + + break; } default: { diff --git a/src/ui/editors/w_JsonEditor.cpp b/src/ui/editors/w_JsonEditor.cpp index 715f56c1..6d3c7b1b 100644 --- a/src/ui/editors/w_JsonEditor.cpp +++ b/src/ui/editors/w_JsonEditor.cpp @@ -1,4 +1,4 @@ -#include "w_JsonEditor.hpp" +#include "w_JsonEditor.hpp" #include "common/QvHelpers.hpp" diff --git a/src/ui/editors/w_JsonEditor.hpp b/src/ui/editors/w_JsonEditor.hpp index a51940d3..9d314380 100644 --- a/src/ui/editors/w_JsonEditor.hpp +++ b/src/ui/editors/w_JsonEditor.hpp @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include "base/Qv2rayBase.hpp" #include "common/JsonHighlighter.hpp" diff --git a/src/ui/w_GroupManager.cpp b/src/ui/w_GroupManager.cpp index ead2191f..5628ef21 100644 --- a/src/ui/w_GroupManager.cpp +++ b/src/ui/w_GroupManager.cpp @@ -263,10 +263,10 @@ void GroupManager::on_groupList_itemClicked(QListWidgetItem *item) groupNameTxt->setText(GetDisplayName(currentGroupId)); const auto &groupMetaObject = ConnectionManager->GetGroupMetaObject(currentGroupId); groupIsSubscriptionGroup->setChecked(groupMetaObject.isSubscription); - subAddrTxt->setText(groupMetaObject.subscriptionSettings.address); + subAddrTxt->setText(groupMetaObject.subscriptionOption.address); lastUpdatedLabel->setText(timeToString(groupMetaObject.lastUpdatedDate)); createdAtLabel->setText(timeToString(groupMetaObject.creationDate)); - updateIntervalSB->setValue(groupMetaObject.subscriptionSettings.updateInterval); + updateIntervalSB->setValue(groupMetaObject.subscriptionOption.updateInterval); // connectionsList->clear(); loadConnectionList(currentGroupId); diff --git a/src/ui/w_MainWindow_extra.cpp b/src/ui/w_MainWindow_extra.cpp index 5cfb2d6d..844cacb2 100644 --- a/src/ui/w_MainWindow_extra.cpp +++ b/src/ui/w_MainWindow_extra.cpp @@ -49,16 +49,16 @@ void MainWindow::CheckSubscriptionsUpdate() const auto info = ConnectionManager->GetGroupMetaObject(entry); // // The update is ignored. - if (info.subscriptionSettings.updateInterval == 0) + if (info.subscriptionOption.updateInterval == 0) continue; // const auto &lastRenewDate = QDateTime::fromTime_t(info.lastUpdatedDate); - const auto &renewTime = lastRenewDate.addSecs(info.subscriptionSettings.updateInterval * 86400); - LOG(MODULE_SUBSCRIPTION, // - "Subscription \"" + info.displayName + "\": " + // - NEWLINE + " --> Last renewal time: " + lastRenewDate.toString() + // - NEWLINE + " --> Renew interval: " + QSTRN(info.subscriptionSettings.updateInterval) + // - NEWLINE + " --> Ideal renew time: " + renewTime.toString()) // + const auto &renewTime = lastRenewDate.addSecs(info.subscriptionOption.updateInterval * 86400); + LOG(MODULE_SUBSCRIPTION, // + "Subscription \"" + info.displayName + "\": " + // + NEWLINE + " --> Last renewal time: " + lastRenewDate.toString() + // + NEWLINE + " --> Renew interval: " + QSTRN(info.subscriptionOption.updateInterval) + // + NEWLINE + " --> Ideal renew time: " + renewTime.toString()) // if (renewTime <= QDateTime::currentDateTime()) { diff --git a/src/ui/w_PreferencesWindow.cpp b/src/ui/w_PreferencesWindow.cpp index 190e729f..8902a1d8 100644 --- a/src/ui/w_PreferencesWindow.cpp +++ b/src/ui/w_PreferencesWindow.cpp @@ -136,8 +136,8 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent), Current // vCorePathTxt->setText(CurrentConfig.kernelConfig.KernelPath()); vCoreAssetsPathTxt->setText(CurrentConfig.kernelConfig.AssetsPath()); - enableAPI->setChecked(CurrentConfig.apiConfig.enableAPI); - statsPortBox->setValue(CurrentConfig.apiConfig.statsPort); + enableAPI->setChecked(CurrentConfig.kernelConfig.enableAPI); + statsPortBox->setValue(CurrentConfig.kernelConfig.statsPort); // // bypassCNCb->setChecked(CurrentConfig.connectionConfig.bypassCN); @@ -307,7 +307,7 @@ void PreferencesWindow::on_buttonBox_accepted() if (!StartupOption.noAPI) { size++; - ports << CurrentConfig.apiConfig.statsPort; + ports << CurrentConfig.kernelConfig.statsPort; } if (ports.size() != size) @@ -653,7 +653,7 @@ void PreferencesWindow::on_bypassBTCb_stateChanged(int arg1) void PreferencesWindow::on_statsPortBox_valueChanged(int arg1) { NEEDRESTART - CurrentConfig.apiConfig.statsPort = arg1; + CurrentConfig.kernelConfig.statsPort = arg1; } void PreferencesWindow::on_socksPortLE_valueChanged(int arg1) @@ -1153,7 +1153,7 @@ void PreferencesWindow::on_enableAPI_stateChanged(int arg1) { LOADINGCHECK NEEDRESTART - CurrentConfig.apiConfig.enableAPI = arg1 == Qt::Checked; + CurrentConfig.kernelConfig.enableAPI = arg1 == Qt::Checked; } void PreferencesWindow::on_updateChannelCombo_currentIndexChanged(int index) diff --git a/src/ui/widgets/ConnectionInfoWidget.cpp b/src/ui/widgets/ConnectionInfoWidget.cpp index b5d9b9d2..cc848b8f 100644 --- a/src/ui/widgets/ConnectionInfoWidget.cpp +++ b/src/ui/widgets/ConnectionInfoWidget.cpp @@ -111,7 +111,7 @@ void ConnectionInfoWidget::ShowDetails(const ConnectionGroupPair &_identifier) // groupShareTxt->setPlainText(shareLinks.join(NEWLINE)); const auto &groupMetaData = ConnectionManager->GetGroupMetaObject(groupId); - groupSubsLinkTxt->setText(groupMetaData.isSubscription ? groupMetaData.subscriptionSettings.address : tr("Not a subscription")); + groupSubsLinkTxt->setText(groupMetaData.isSubscription ? groupMetaData.subscriptionOption.address : tr("Not a subscription")); } } From a086db44323172cffcb0c5acf05fcfbcc3c37f15 Mon Sep 17 00:00:00 2001 From: DuckSoft Date: Fri, 8 May 2020 11:00:03 +0800 Subject: [PATCH 064/385] =?UTF-8?q?=E6=B2=A1=E6=95=91=E4=BA=86=EF=BC=8C?= =?UTF-8?q?=E7=AD=89=E6=AD=BB=E5=90=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/proxy/QvProxyConfigurator.cpp | 14 +++++++------- translations/en_US.ts | 6 +++--- translations/ja_JP.ts | 18 +++++++++++++++--- translations/ru_RU.ts | 6 +++--- translations/zh_CN.ts | 18 +++++++++++++++--- 5 files changed, 43 insertions(+), 19 deletions(-) diff --git a/src/components/proxy/QvProxyConfigurator.cpp b/src/components/proxy/QvProxyConfigurator.cpp index 8c496f0a..9c0b4895 100644 --- a/src/components/proxy/QvProxyConfigurator.cpp +++ b/src/components/proxy/QvProxyConfigurator.cpp @@ -315,16 +315,16 @@ namespace Qv2ray::components::proxy const auto deepinWarnTitle = QObject::tr("Deepin Detected"); const auto deepinWarnMessage = - QObject::tr("Deepin plays smart and sets you the wrong HTTPS_PROXY environment variable. ") + NEWLINE + // - QObject::tr("The original scheme should be http://, but he will replace with https://, causing the problem. ") + NEWLINE + // - QObject::tr("Qv2ray will help you change it back and make things work again. "); + QObject::tr("Deepin plays smart and sets you the wrong HTTPS_PROXY, FTP_PROXY environment variable.") + NEWLINE + // + QObject::tr("The origin scheme http is wrongly replaced by https and ftp, causing the problem.") + NEWLINE + // + QObject::tr("Qv2ray cannot help you change them back. Please don't blame us if things go wrong."); // QvMessageBoxWarn(nullptr, deepinWarnTitle, deepinWarnMessage); } - // set them back! - const auto httpProxyURL = QString("http://%1:%2").arg(address, QSTRN(httpPort)).toStdString(); - setenv("https_proxy", httpProxyURL.c_str(), true); - setenv("ftp_proxy", httpProxyURL.c_str(), true); + // set them back! - NOPE. setenv only works within your little program. + // const auto httpProxyURL = QString("http://%1:%2").arg(address, QSTRN(httpPort)).toStdString(); + // setenv("https_proxy", httpProxyURL.c_str(), true); + // setenv("ftp_proxy", httpProxyURL.c_str(), true); } #else diff --git a/translations/en_US.ts b/translations/en_US.ts index 7bcc9616..ebfea120 100644 --- a/translations/en_US.ts +++ b/translations/en_US.ts @@ -2114,15 +2114,15 @@ For example, for updating subscriptions. - Deepin plays smart and sets you the wrong HTTPS_PROXY environment variable. + Deepin plays smart and sets you the wrong HTTPS_PROXY, FTP_PROXY environment variable. - The original scheme should be http://, but he will replace with https://, causing the problem. + The origin scheme http is wrongly replaced by https and ftp, causing the problem. - Qv2ray will help you change it back and make things work again. + Qv2ray cannot help you change them back. Please don't blame us if things go wrong. diff --git a/translations/ja_JP.ts b/translations/ja_JP.ts index a0ec5922..a55747b8 100644 --- a/translations/ja_JP.ts +++ b/translations/ja_JP.ts @@ -2919,15 +2919,27 @@ For example, for updating subscriptions. Deepin plays smart and sets you the wrong HTTPS_PROXY environment variable. - Deepinのバカ、間違ったHTTPS_PROXY環境変数を設定します。 + Deepinのバカ、間違ったHTTPS_PROXY環境変数を設定します。 The original scheme should be http://, but he will replace with https://, causing the problem. - 本来のスキームは http:// であるべきなのですが、彼は https:// に置き換えて問題を引き起こします。 + 本来のスキームは http:// であるべきなのですが、彼は https:// に置き換えて問題を引き起こします。 Qv2ray will help you change it back and make things work again. - Qv2rayはそれを元に戻して、またうまくいくようにしてくれます。 + Qv2rayはそれを元に戻して、またうまくいくようにしてくれます。 + + + Deepin plays smart and sets you the wrong HTTPS_PROXY, FTP_PROXY environment variable. + Deepinのバカ、間違ったHTTPS_PROXYとFTP_PROXYの環境変数を設定します。 + + + The origin scheme http is wrongly replaced by https and ftp, causing the problem. + 元のスキームのhttpがhttpsとftpに間違って置き換えられてしまい、問題が発生しています。 + + + Qv2ray cannot help you change them back. Please don't blame us if things go wrong. + Qv2rayでは元に戻すことはできません。何かあっても私たちのせいにしないでください。 diff --git a/translations/ru_RU.ts b/translations/ru_RU.ts index 1ccbdfcf..b82073f9 100644 --- a/translations/ru_RU.ts +++ b/translations/ru_RU.ts @@ -2801,15 +2801,15 @@ For example, for updating subscriptions. - Deepin plays smart and sets you the wrong HTTPS_PROXY environment variable. + Deepin plays smart and sets you the wrong HTTPS_PROXY, FTP_PROXY environment variable. - The original scheme should be http://, but he will replace with https://, causing the problem. + The origin scheme http is wrongly replaced by https and ftp, causing the problem. - Qv2ray will help you change it back and make things work again. + Qv2ray cannot help you change them back. Please don't blame us if things go wrong. diff --git a/translations/zh_CN.ts b/translations/zh_CN.ts index ed8cbf39..c2b03b9c 100644 --- a/translations/zh_CN.ts +++ b/translations/zh_CN.ts @@ -2497,15 +2497,27 @@ For example, for updating subscriptions. Deepin plays smart and sets you the wrong HTTPS_PROXY environment variable. - 默认情况下,Deepin 自作聪明,会给你设置错误的 HTTPS_PROXY 环境变量。 + 默认情况下,Deepin 自作聪明,会给你设置错误的 HTTPS_PROXY 环境变量。 The original scheme should be http://, but he will replace with https://, causing the problem. - 原 scheme 应为 http://,但 Deepin 将其替换成了 https://,导致 HTTPS 代理无法正常使用。 + 原 scheme 应为 http://,但 Deepin 将其替换成了 https://,导致 HTTPS 代理无法正常使用。 Qv2ray will help you change it back and make things work again. - Qv2ray 会尝试帮你改回去,以期解决此问题。 + Qv2ray 会尝试帮你改回去,以期解决此问题。 + + + Deepin plays smart and sets you the wrong HTTPS_PROXY, FTP_PROXY environment variable. + Deepin可能自作聪明,为你设置了错误的 HTTPS_PROXY 和 FTP_PROXY 环境变量。 + + + The origin scheme http is wrongly replaced by https and ftp, causing the problem. + 原来正确的 http:// 可能被错误地替换为 https:// 和 ftp://,导致这个问题。 + + + Qv2ray cannot help you change them back. Please don't blame us if things go wrong. + Qv2ray 很遗憾无法帮你改回来。若因此遇到问题,请勿指责吾等。 From 4ed028d77aa4275d0186343305b2073aab9c1610 Mon Sep 17 00:00:00 2001 From: DuckSoft Date: Fri, 8 May 2020 11:11:49 +0800 Subject: [PATCH 065/385] Deepin you win --- 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 9c0b4895..7ba75e93 100644 --- a/src/components/proxy/QvProxyConfigurator.cpp +++ b/src/components/proxy/QvProxyConfigurator.cpp @@ -232,7 +232,7 @@ namespace Qv2ray::components::proxy QStringList actions; actions << QString("gsettings set org.gnome.system.proxy mode '%1'").arg("manual"); bool isKDE = qEnvironmentVariable("XDG_SESSION_DESKTOP") == "KDE"; - bool isDDE = isKDE ? false : qEnvironmentVariable("XDG_SESSION_DESKTOP") == "deepin"; + bool isDDE = isKDE ? false : qEnvironmentVariable("XDG_CURRENT_DESKTOP").toLower() == "deepin"; const auto configPath = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation); // Configure HTTP Proxies for HTTP, FTP and HTTPS From 39e2adfabd87cf28375e4161c9fd1e3f1ae3d563 Mon Sep 17 00:00:00 2001 From: ymshenyu Date: Fri, 8 May 2020 13:29:07 +0800 Subject: [PATCH 066/385] Update CMakeLists.txt --- CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e07cb39a..a3789ce7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,10 +29,12 @@ add_definitions(-DQV2RAY_VERSION_BUGFIX=${CPACK_PACKAGE_VERSION_PATCH}) add_definitions(-DQV2RAY_VERSION_BUILD=${QV2RAY_BUILD_VERSION}) add_definitions(-DQV2RAY_VERSION_STRING="${QV2RAY_VERSION_STRING}") -add_definitions(-DXTOSTRUCT_QT) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) +if(MSVC) + set(CMAKE_CXX_EXTENSIONS OFF) +endif() find_package(Qt5 5.11 COMPONENTS Core Gui Widgets Network REQUIRED) From d4d5220547216026127ea93ac1134a66667a04db Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Fri, 8 May 2020 14:11:48 +0800 Subject: [PATCH 067/385] add: added connection / group saving --- makespec/BUILDVERSION | 2 +- src/core/handler/ConfigHandler.cpp | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 56457bc3..9eb6d374 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5377 \ No newline at end of file +5378 \ No newline at end of file diff --git a/src/core/handler/ConfigHandler.cpp b/src/core/handler/ConfigHandler.cpp index 94236576..e6bf4cf5 100644 --- a/src/core/handler/ConfigHandler.cpp +++ b/src/core/handler/ConfigHandler.cpp @@ -76,7 +76,19 @@ namespace Qv2ray::core::handlers // GlobalConfig.connections = connections.keys(); // GlobalConfig.groups = groups.keys(); // -#error I haven't implement the Connections Saving Feature!! + QJsonObject connectionsObject; + for (const auto &key : connections.keys()) + { + connectionsObject[key.toString()] = connections[key].toJson(); + } + StringToFile(JsonToString(connectionsObject), QV2RAY_CONFIG_DIR + "connections.json"); + // + QJsonObject groupObject; + for (const auto &key : groups.keys()) + { + groupObject[key.toString()] = groups[key].toJson(); + } + StringToFile(JsonToString(groupObject), QV2RAY_CONFIG_DIR + "groups.json"); SaveGlobalSettings(); } From ffae054fe96e30e89cef168ffc1ee0258da0a684 Mon Sep 17 00:00:00 2001 From: DuckSoft Date: Sat, 9 May 2020 14:14:34 +0800 Subject: [PATCH 068/385] fixing KDE system proxy settings damn kwriteconfig5 needs you to use double quote --- src/components/proxy/QvProxyConfigurator.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/proxy/QvProxyConfigurator.cpp b/src/components/proxy/QvProxyConfigurator.cpp index 7ba75e93..cb301468 100644 --- a/src/components/proxy/QvProxyConfigurator.cpp +++ b/src/components/proxy/QvProxyConfigurator.cpp @@ -250,7 +250,7 @@ namespace Qv2ray::components::proxy // for KDE: if (isKDE) { - actions << QString("kwriteconfig5 --file %1/kioslaverc --group 'Proxy Settings' --key %2Proxy 'http://%3 %4'") + actions << QString(R"(kwriteconfig5 --file "%1/kioslaverc" --group "Proxy Settings" --key %2Proxy "http://%3 %4")") .arg(configPath, protocol, address, QSTRN(httpPort)); } } @@ -268,7 +268,7 @@ namespace Qv2ray::components::proxy // for KDE: if (isKDE) { - actions << QString("kwriteconfig5 --file %1/kioslaverc --group 'Proxy Settings' --key socksProxy 'socks://%2 %3'") + actions << QString(R"(kwriteconfig5 --file "%1/kioslaverc" --group "Proxy Settings" --key socksProxy "socks://%2 %3")") .arg(configPath, address, QSTRN(socksPort)); } } @@ -284,7 +284,7 @@ namespace Qv2ray::components::proxy if (isKDE) { LOG(MODULE_PROXY, "KDE detected") - actions << QString("kwriteconfig5 --file %1/kioslaverc --group 'Proxy Settings' --key ProxyType 1").arg(configPath); + actions << QString(R"(kwriteconfig5 --file "%1/kioslaverc" --group "Proxy Settings" --key ProxyType 1)").arg(configPath); } } @@ -408,7 +408,7 @@ namespace Qv2ray::components::proxy // for KDE: if (isKDE) { - actions << QString("kwriteconfig5 --file %1/kioslaverc --group 'Proxy Settings' --key ProxyType 0").arg(configRoot); + actions << QString(R"(kwriteconfig5 --file "%1/kioslaverc" --group "Proxy Settings" --key ProxyType 0)").arg(configRoot); } } From 79d61d3309f530032ee0dc39882fe806cf012271 Mon Sep 17 00:00:00 2001 From: DuckSoft Date: Sat, 9 May 2020 14:14:34 +0800 Subject: [PATCH 069/385] fixing KDE system proxy settings damn kwriteconfig5 needs you to use double quote --- src/components/proxy/QvProxyConfigurator.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/proxy/QvProxyConfigurator.cpp b/src/components/proxy/QvProxyConfigurator.cpp index 7ba75e93..cb301468 100644 --- a/src/components/proxy/QvProxyConfigurator.cpp +++ b/src/components/proxy/QvProxyConfigurator.cpp @@ -250,7 +250,7 @@ namespace Qv2ray::components::proxy // for KDE: if (isKDE) { - actions << QString("kwriteconfig5 --file %1/kioslaverc --group 'Proxy Settings' --key %2Proxy 'http://%3 %4'") + actions << QString(R"(kwriteconfig5 --file "%1/kioslaverc" --group "Proxy Settings" --key %2Proxy "http://%3 %4")") .arg(configPath, protocol, address, QSTRN(httpPort)); } } @@ -268,7 +268,7 @@ namespace Qv2ray::components::proxy // for KDE: if (isKDE) { - actions << QString("kwriteconfig5 --file %1/kioslaverc --group 'Proxy Settings' --key socksProxy 'socks://%2 %3'") + actions << QString(R"(kwriteconfig5 --file "%1/kioslaverc" --group "Proxy Settings" --key socksProxy "socks://%2 %3")") .arg(configPath, address, QSTRN(socksPort)); } } @@ -284,7 +284,7 @@ namespace Qv2ray::components::proxy if (isKDE) { LOG(MODULE_PROXY, "KDE detected") - actions << QString("kwriteconfig5 --file %1/kioslaverc --group 'Proxy Settings' --key ProxyType 1").arg(configPath); + actions << QString(R"(kwriteconfig5 --file "%1/kioslaverc" --group "Proxy Settings" --key ProxyType 1)").arg(configPath); } } @@ -408,7 +408,7 @@ namespace Qv2ray::components::proxy // for KDE: if (isKDE) { - actions << QString("kwriteconfig5 --file %1/kioslaverc --group 'Proxy Settings' --key ProxyType 0").arg(configRoot); + actions << QString(R"(kwriteconfig5 --file "%1/kioslaverc" --group "Proxy Settings" --key ProxyType 0)").arg(configRoot); } } From 74cbf83219838dc47221823c7626c37c4a1965ea Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Sun, 10 May 2020 23:55:21 +0800 Subject: [PATCH 070/385] add: experimental connection-group linking creation --- makespec/BUILDVERSION | 2 +- src/common/QvHelpers.cpp | 2 +- src/common/QvHelpers.hpp | 2 +- src/components/plugins/interface | 2 +- src/components/proxy/QvProxyConfigurator.cpp | 4 +- src/core/handler/ConfigHandler.cpp | 57 ++++-------- src/core/handler/ConfigHandler.hpp | 6 +- src/core/handler/KernelInstanceHandler.cpp | 16 ++-- src/ui/w_GroupManager.cpp | 93 +++++++++++++------- src/ui/w_GroupManager.hpp | 11 +-- src/ui/w_GroupManager.ui | 46 ++++++---- src/ui/w_MainWindow.cpp | 10 +-- src/ui/w_MainWindow.hpp | 4 +- src/ui/widgets/ConnectionInfoWidget.cpp | 6 +- src/ui/widgets/ConnectionItemWidget.cpp | 2 +- 15 files changed, 141 insertions(+), 122 deletions(-) diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 9eb6d374..4d1d260e 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5378 \ No newline at end of file +5379 \ No newline at end of file diff --git a/src/common/QvHelpers.cpp b/src/common/QvHelpers.cpp index f951357e..71043373 100644 --- a/src/common/QvHelpers.cpp +++ b/src/common/QvHelpers.cpp @@ -125,7 +125,7 @@ namespace Qv2ray::common QStringList SplitLines(const QString &_string) { - return _string.split(QRegExp("[\r\n]"), QString::SkipEmptyParts); + return _string.split(QRegExp("[\r\n]"), Qt::SkipEmptyParts); } // list SplitLines_std(const QString &_string) diff --git a/src/common/QvHelpers.hpp b/src/common/QvHelpers.hpp index c4214d22..a121a1be 100644 --- a/src/common/QvHelpers.hpp +++ b/src/common/QvHelpers.hpp @@ -87,7 +87,7 @@ namespace Qv2ray::common { QDateTime timestamp; timestamp.setSecsSinceEpoch(t); - return timestamp.toString(Qt::SystemLocaleShortDate); + return timestamp.toString(); } inline void FastAppendTextDocument(const QString &message, QTextDocument *doc) diff --git a/src/components/plugins/interface b/src/components/plugins/interface index d37c7ea9..faaeb651 160000 --- a/src/components/plugins/interface +++ b/src/components/plugins/interface @@ -1 +1 @@ -Subproject commit d37c7ea9459956dc459610e98b821d4a790cb6e8 +Subproject commit faaeb651e49af5229852a28aedebaa0258c9b19d diff --git a/src/components/proxy/QvProxyConfigurator.cpp b/src/components/proxy/QvProxyConfigurator.cpp index f6fad937..a0327132 100644 --- a/src/components/proxy/QvProxyConfigurator.cpp +++ b/src/components/proxy/QvProxyConfigurator.cpp @@ -317,7 +317,7 @@ namespace Qv2ray::components::proxy if (hasSOCKS) portSettings.insert(Events::SystemProxy::SystemProxyType::SystemProxy_SOCKS, socksPort); PluginHost->Send_SystemProxyEvent( - Events::SystemProxy::EventObject{ portSettings, Events::SystemProxy::SystemProxyStateType::SystemProxyState_SetProxy }); + Events::SystemProxy::EventObject{ portSettings, Events::SystemProxy::SystemProxyStateType::SetProxy }); } void ClearSystemProxy() @@ -382,6 +382,6 @@ namespace Qv2ray::components::proxy // // Trigger plugin events PluginHost->Send_SystemProxyEvent( - Events::SystemProxy::EventObject{ {}, Events::SystemProxy::SystemProxyStateType::SystemProxyState_ClearProxy }); + Events::SystemProxy::EventObject{ {}, Events::SystemProxy::SystemProxyStateType::ClearProxy }); } } // namespace Qv2ray::components::proxy diff --git a/src/core/handler/ConfigHandler.cpp b/src/core/handler/ConfigHandler.cpp index e6bf4cf5..b3a8074d 100644 --- a/src/core/handler/ConfigHandler.cpp +++ b/src/core/handler/ConfigHandler.cpp @@ -210,20 +210,20 @@ namespace Qv2ray::core::handlers { CheckConnectionExistance(id); OnConnectionRenamed(id, connections[id].displayName, newName); - PluginHost->Send_ConnectionEvent({ newName, connections[id].displayName, Events::ConnectionEntry::ConnectionEvent_Renamed }); + PluginHost->Send_ConnectionEvent({ Events::ConnectionEntry::Renamed, newName, connections[id].displayName }); connections[id].displayName = newName; CHSaveConfigData(); return {}; } - const std::optional QvConfigHandler::RemoveConnectionFromGroup(const ConnectionId &id, const GroupId &gid) + const std::optional QvConfigHandler::DeleteConnectionFromGroup(const ConnectionId &id, const GroupId &gid) { CheckConnectionExistance(id); // auto groupId = connections[id].groupId; // 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, "", Events::ConnectionEntry::ConnectionEvent_Deleted }); + // PluginHost->Send_ConnectionEvent({ connections[id].displayName, "", Events::ConnectionEntry::Deleted }); // connections.remove(id); // groups[groupId].connections.removeAll(id); //// @@ -247,34 +247,15 @@ namespace Qv2ray::core::handlers return tr("File does not exist."); } - const std::optional QvConfigHandler::MoveConnectionGroup(const ConnectionId &id, const GroupId &newGroupId) + const std::optional QvConfigHandler::LinkConnectionWithGroup(const ConnectionId &id, const GroupId &newGroupId) { CheckConnectionExistance(id); - // auto const oldgid = connections[id].groupId; - //// - // QString oldPath = (groups[oldgid].isSubscription ? QV2RAY_SUBSCRIPTION_DIR : QV2RAY_CONNECTIONS_DIR) + oldgid.toString() + "/" + - // id.toString() + QV2RAY_CONFIG_FILE_EXTENSION; - //// - // auto newDir = (groups[newGroupId].isSubscription ? QV2RAY_SUBSCRIPTION_DIR : QV2RAY_CONNECTIONS_DIR) + newGroupId.toString() + "/"; - // QString newPath = newDir + id.toString() + QV2RAY_CONFIG_FILE_EXTENSION; - //// - // if (!QDir(newDir).exists()) - //{ - // QDir().mkpath(newDir); - //} - //// - // if (!QFile::rename(oldPath, newPath)) - //{ - // LOG(MODULE_FILEIO, "Cannot rename") - //} - // groups[oldgid].connections.removeAll(id); - // groups[newGroupId].connections.append(id); - // connections[id].groupId = newGroupId; - //// - // PluginHost->Send_ConnectionEvent({ connections[id].displayName, "", Events::ConnectionEntry::ConnectionEvent_Updated }); - //// - // emit OnConnectionGroupChanged(id, oldgid, newGroupId); - // + if (!groups[newGroupId].connections.contains(id)) + { + groups[newGroupId].connections.append(id); + } + PluginHost->Send_ConnectionEvent({ Events::ConnectionEntry::LinkedWithGroup, connections[id].displayName, "" }); + emit OnConnectionLinkedWithGroup(id, newGroupId); return {}; } @@ -290,7 +271,7 @@ namespace Qv2ray::core::handlers auto list = groups[id].connections; for (const auto &conn : list) { - MoveConnectionGroup(conn, DefaultGroupId); + LinkConnectionWithGroup(conn, DefaultGroupId); } // // TODO @@ -303,7 +284,7 @@ namespace Qv2ray::core::handlers QDir(QV2RAY_CONNECTIONS_DIR + id.toString()).removeRecursively(); } // - PluginHost->Send_ConnectionEvent({ groups[id].displayName, "", Events::ConnectionEntry::ConnectionEvent_Deleted }); + PluginHost->Send_ConnectionEvent({ Events::ConnectionEntry::FullyRemoved, groups[id].displayName, "" }); // groups.remove(id); CHSaveConfigData(); @@ -339,7 +320,7 @@ namespace Qv2ray::core::handlers { LOG(MODULE_CORE_HANDLER, "Kernel crashed: " + errMessage) emit OnDisconnected(id); - PluginHost->Send_ConnectivityEvent({ GetDisplayName(id.connectionId), {}, Events::Connectivity::QvConnecticity_Disconnected }); + PluginHost->Send_ConnectivityEvent({ GetDisplayName(id.connectionId), {}, Events::Connectivity::Disconnected }); emit OnKernelCrashed(id, errMessage); } @@ -375,7 +356,7 @@ namespace Qv2ray::core::handlers connectionRootCache[id] = root; // emit OnConnectionModified(id); - PluginHost->Send_ConnectionEvent({ connections[id].displayName, "", Events::ConnectionEntry::ConnectionEvent_Updated }); + PluginHost->Send_ConnectionEvent({ Events::ConnectionEntry::Edited, connections[id].displayName, "" }); if (!skipRestart && kernelHandler->CurrentConnection().connectionId == id) { emit RestartConnection(); @@ -389,7 +370,7 @@ namespace Qv2ray::core::handlers groups[id].displayName = displayName; groups[id].isSubscription = isSubscription; groups[id].creationDate = system_clock::to_time_t(system_clock::now()); - PluginHost->Send_ConnectionEvent({ displayName, "", Events::ConnectionEntry::ConnectionEvent_Created }); + PluginHost->Send_ConnectionEvent({ Events::ConnectionEntry::Created, displayName, "" }); emit OnGroupCreated(id, displayName); CHSaveConfigData(); return id; @@ -403,7 +384,7 @@ namespace Qv2ray::core::handlers return tr("Group does not exist"); } OnGroupRenamed(id, groups[id].displayName, newName); - PluginHost->Send_ConnectionEvent({ newName, groups[id].displayName, Events::ConnectionEntry::ConnectionEvent_Renamed }); + PluginHost->Send_ConnectionEvent({ Events::ConnectionEntry::Renamed, newName, groups[id].displayName }); groups[id].displayName = newName; return {}; } @@ -479,11 +460,11 @@ namespace Qv2ray::core::handlers QMultiMap, ConnectionId> typeMap; for (const auto &conn : groups[id].connections) { - nameMap.insertMulti(GetDisplayName(conn), conn); + nameMap.insert(GetDisplayName(conn), conn); auto [protocol, host, port] = GetConnectionInfo(conn); if (port != 0) { - typeMap.insertMulti({ protocol, host, port }, conn); + typeMap.insert({ protocol, host, port }, conn); } } QDir().mkpath(QV2RAY_CONNECTIONS_DIR); @@ -589,7 +570,7 @@ namespace Qv2ray::core::handlers connections[newId].creationDate = system_clock::to_time_t(system_clock::now()); connections[newId].displayName = displayName; emit OnConnectionCreated({ newId, groupId }, displayName); - PluginHost->Send_ConnectionEvent({ displayName, "", Events::ConnectionEntry::ConnectionEvent_Created }); + PluginHost->Send_ConnectionEvent({ Events::ConnectionEntry::Created, displayName, "" }); UpdateConnection(newId, root); if (!skipSaveConfig) { diff --git a/src/core/handler/ConfigHandler.hpp b/src/core/handler/ConfigHandler.hpp index 8327d9e5..38982e40 100644 --- a/src/core/handler/ConfigHandler.hpp +++ b/src/core/handler/ConfigHandler.hpp @@ -91,9 +91,9 @@ namespace Qv2ray::core::handlers bool UpdateConnection(const ConnectionId &id, const CONFIGROOT &root, bool skipRestart = false); void ClearGroupUsage(const GroupId &id); void ClearConnectionUsage(const ConnectionGroupPair &id); - const std::optional RemoveConnectionFromGroup(const ConnectionId &id, const GroupId &gid); + const std::optional DeleteConnectionFromGroup(const ConnectionId &id, const GroupId &gid); const std::optional RenameConnection(const ConnectionId &id, const QString &newName); - const std::optional MoveConnectionGroup(const ConnectionId &id, const GroupId &newGroupId); + const std::optional LinkConnectionWithGroup(const ConnectionId &id, const GroupId &newGroupId); const ConnectionId CreateConnection(const QString &displayName, const GroupId &groupId, const CONFIGROOT &root, bool skipSaveConfig = false); // @@ -126,7 +126,7 @@ namespace Qv2ray::core::handlers void OnConnectionDeleted(const ConnectionGroupPair &Id); void OnConnectionRemovedFromGroup(const ConnectionGroupPair &groupId); void OnConnectionModified(const ConnectionId &id); - void OnConnectionGroupChanged(const ConnectionId &id, const GroupId &originalGroup, const GroupId &newGroup); + void OnConnectionLinkedWithGroup(const ConnectionId &id, const GroupId &newGroup); // void OnLatencyTestStarted(const ConnectionId &id); void OnLatencyTestFinished(const ConnectionId &id, const uint average); diff --git a/src/core/handler/KernelInstanceHandler.cpp b/src/core/handler/KernelInstanceHandler.cpp index 191b754b..7f93da39 100644 --- a/src/core/handler/KernelInstanceHandler.cpp +++ b/src/core/handler/KernelInstanceHandler.cpp @@ -38,7 +38,7 @@ namespace Qv2ray::core::handlers bool isComplex = IsComplexConfig(root); auto fullConfig = GenerateRuntimeConfig(root); inboundPorts = GetConfigInboundPorts(fullConfig); - PluginHost->Send_ConnectivityEvent({ GetDisplayName(id.connectionId), inboundPorts, Events::Connectivity::QvConnecticity_Connecting }); + PluginHost->Send_ConnectivityEvent({ GetDisplayName(id.connectionId), inboundPorts, Events::Connectivity::Connecting }); QList> inboundInfo; for (const auto &inbound_v : fullConfig["inbounds"].toArray()) { @@ -176,12 +176,12 @@ namespace Qv2ray::core::handlers { emit OnConnected(id); PluginHost->Send_ConnectivityEvent( - { GetDisplayName(id.connectionId), inboundPorts, Events::Connectivity::QvConnecticity_Connected }); + { GetDisplayName(id.connectionId), inboundPorts, Events::Connectivity::Connected }); } else { PluginHost->Send_ConnectivityEvent( - { GetDisplayName(id.connectionId), inboundPorts, Events::Connectivity::QvConnecticity_Disconnected }); + { GetDisplayName(id.connectionId), inboundPorts, Events::Connectivity::Disconnected }); } return result; } @@ -210,7 +210,7 @@ namespace Qv2ray::core::handlers { emit OnConnected(id); PluginHost->Send_ConnectivityEvent( - { GetDisplayName(id.connectionId), inboundPorts, Events::Connectivity::QvConnecticity_Connected }); + { GetDisplayName(id.connectionId), inboundPorts, Events::Connectivity::Connected }); return {}; } else @@ -227,13 +227,13 @@ namespace Qv2ray::core::handlers if (result.has_value()) { PluginHost->Send_ConnectivityEvent( - { GetDisplayName(id.connectionId), inboundPorts, Events::Connectivity::QvConnecticity_Disconnected }); + { GetDisplayName(id.connectionId), inboundPorts, Events::Connectivity::Disconnected }); } else { emit OnConnected(id); PluginHost->Send_ConnectivityEvent( - { GetDisplayName(id.connectionId), inboundPorts, Events::Connectivity::QvConnecticity_Connected }); + { GetDisplayName(id.connectionId), inboundPorts, Events::Connectivity::Connected }); } return result; } @@ -265,7 +265,7 @@ namespace Qv2ray::core::handlers if (isConnected) { PluginHost->Send_ConnectivityEvent( - { GetDisplayName(currentId.connectionId), inboundPorts, Events::Connectivity::QvConnecticity_Disconnecting }); + { GetDisplayName(currentId.connectionId), inboundPorts, Events::Connectivity::Disconnecting }); if (vCoreInstance->KernelStarted) { vCoreInstance->StopConnection(); @@ -280,7 +280,7 @@ namespace Qv2ray::core::handlers // Copy emit OnDisconnected(currentId); PluginHost->Send_ConnectivityEvent( - { GetDisplayName(currentId.connectionId), inboundPorts, Events::Connectivity::QvConnecticity_Disconnected }); + { GetDisplayName(currentId.connectionId), inboundPorts, Events::Connectivity::Disconnected }); currentId.clear(); } else diff --git a/src/ui/w_GroupManager.cpp b/src/ui/w_GroupManager.cpp index 5628ef21..ff8fe7c4 100644 --- a/src/ui/w_GroupManager.cpp +++ b/src/ui/w_GroupManager.cpp @@ -1,4 +1,4 @@ -#include "w_GroupManager.hpp" +#include "w_GroupManager.hpp" #include "common/QvHelpers.hpp" #include "core/connection/Generation.hpp" @@ -7,20 +7,33 @@ #include #include -#define GET_DATA(type, typeConv) \ - [&](const QList list) { \ - QList _list; \ - for (const auto &item : list) \ + +#define SELECTED_ROWS_INDEX \ + ([&]() { \ + const auto &__selection = connectionsTable->selectedItems(); \ + QSet rows; \ + for (const auto &selection : __selection) \ { \ - _list.push_back(item->data(Qt::UserRole).to##typeConv()); \ + rows.insert(connectionsTable->row(selection)); \ + } \ + return rows; \ + }()) + +#define GET_SELECTED_CONNECTION_IDS(connectionIdList) \ + ([&]() { \ + QList _list; \ + for (const auto &i : connectionIdList) \ + { \ + _list.push_back(ConnectionId(connectionsTable->item(i, 0)->data(Qt::UserRole).toString())); \ } \ return _list; \ - } + }()) GroupManager::GroupManager(QWidget *parent) : QDialog(parent) { setupUi(this); QvMessageBusConnect(GroupManager); UpdateColorScheme(); + connectionListRCMenu->addSection(tr("Connection Management")); connectionListRCMenu->addAction(exportConnectionAction); connectionListRCMenu->addAction(deleteConnectionAction); @@ -31,9 +44,9 @@ GroupManager::GroupManager(QWidget *parent) : QDialog(parent) connect(exportConnectionAction, &QAction::triggered, this, &GroupManager::onRCMExportConnectionTriggered); connect(deleteConnectionAction, &QAction::triggered, this, &GroupManager::onRCMDeleteConnectionTriggered); // - connect(ConnectionManager, &QvConfigHandler::OnConnectionGroupChanged, // - [&](const ConnectionId &, const GroupId &, const GroupId &) { // - this->loadConnectionList(currentGroupId); // + connect(ConnectionManager, &QvConfigHandler::OnConnectionLinkedWithGroup, // + [&](const ConnectionId &, const GroupId &) { // + this->loadConnectionList(currentGroupId); // }); // const auto reloadGroupLambda = [&](const ConnectionGroupPair &id) { @@ -59,16 +72,16 @@ GroupManager::GroupManager(QWidget *parent) : QDialog(parent) void GroupManager::onRCMDeleteConnectionTriggered() { - const auto list = GET_DATA(QString, String)(connectionsList->selectedItems()); + const auto list = GET_SELECTED_CONNECTION_IDS(SELECTED_ROWS_INDEX); for (const auto &item : list) { - ConnectionManager->RemoveConnectionFromGroup(ConnectionId(item), currentGroupId); + ConnectionManager->DeleteConnectionFromGroup(ConnectionId(item), currentGroupId); } } void GroupManager::onRCMExportConnectionTriggered() { - const auto list = GET_DATA(QString, String)(connectionsList->selectedItems()); + const auto &list = GET_SELECTED_CONNECTION_IDS(SELECTED_ROWS_INDEX); QFileDialog d; switch (list.count()) { @@ -139,12 +152,31 @@ void GroupManager::ReloadGroupAction() void GroupManager::loadConnectionList(const GroupId &group) { - connectionsList->clear(); - for (auto conn : ConnectionManager->Connections(group)) + connectionsTable->clearContents(); + connectionsTable->model()->removeRows(0, connectionsTable->rowCount()); + const auto &connections = ConnectionManager->Connections(group); + for (auto i = 0; i < connections.count(); i++) { - auto item = new QListWidgetItem(GetDisplayName(conn), connectionsList); - item->setData(Qt::UserRole, conn.toString()); - connectionsList->addItem(item); + const auto &conn = connections.at(i); + connectionsTable->insertRow(i); + auto displayNameItem = new QTableWidgetItem(GetDisplayName(conn)); + displayNameItem->setData(Qt::UserRole, conn.toString()); + auto typeItem = new QTableWidgetItem(GetConnectionProtocolString(conn)); + const auto [type, host, port] = GetConnectionInfo(conn); + auto hostPortItem = new QTableWidgetItem(host + ":" + QSTRN(port)); + // + QStringList groupsNamesString; + for (const auto &group : ConnectionManager->GetGroupId(conn)) + { + groupsNamesString.append(GetDisplayName(group)); + } + auto groupsItem = new QTableWidgetItem(groupsNamesString.join(";")); + connectionsTable->setItem(i, 0, displayNameItem); + connectionsTable->setItem(i, 1, typeItem); + connectionsTable->setItem(i, 2, hostPortItem); + connectionsTable->setItem(i, 3, groupsItem); + // + connectionsTable->resizeColumnsToContents(); } } @@ -153,7 +185,7 @@ void GroupManager::onRCMActionTriggered_Copy() const auto _sender = qobject_cast(sender()); const GroupId groupId{ _sender->data().toString() }; // - const auto list = GET_DATA(QString, String)(connectionsList->selectedItems()); + const auto list = GET_SELECTED_CONNECTION_IDS(SELECTED_ROWS_INDEX); for (const auto &connId : list) { const auto &connectionId = ConnectionId(connId); @@ -166,10 +198,10 @@ void GroupManager::onRCMActionTriggered_Move() const auto _sender = qobject_cast(sender()); const GroupId groupId{ _sender->data().toString() }; // - const auto list = GET_DATA(QString, String)(connectionsList->selectedItems()); + const auto list = GET_SELECTED_CONNECTION_IDS(SELECTED_ROWS_INDEX); for (const auto &connId : list) { - ConnectionManager->MoveConnectionGroup(ConnectionId(connId), groupId); + ConnectionManager->LinkConnectionWithGroup(ConnectionId(connId), groupId); } } @@ -268,7 +300,6 @@ void GroupManager::on_groupList_itemClicked(QListWidgetItem *item) createdAtLabel->setText(timeToString(groupMetaObject.creationDate)); updateIntervalSB->setValue(groupMetaObject.subscriptionOption.updateInterval); // - connectionsList->clear(); loadConnectionList(currentGroupId); } @@ -299,12 +330,6 @@ void GroupManager::on_connectionsList_currentItemChanged(QListWidgetItem *curren } } -void GroupManager::on_connectionsList_customContextMenuRequested(const QPoint &pos) -{ - Q_UNUSED(pos) - connectionListRCMenu->popup(QCursor::pos()); -} - void GroupManager::on_groupIsSubscriptionGroup_clicked(bool checked) { ConnectionManager->SetSubscriptionData(currentGroupId, checked); @@ -323,9 +348,9 @@ void GroupManager::on_deleteSelectedConnBtn_clicked() void GroupManager::on_exportSelectedConnBtn_clicked() { - if (connectionsList->selectedItems().isEmpty()) + if (connectionsTable->selectedItems().isEmpty()) { - connectionsList->selectAll(); + connectionsTable->selectAll(); } onRCMExportConnectionTriggered(); } @@ -345,4 +370,10 @@ void GroupManager::ExportConnectionFilter(CONFIGROOT &root) root["inbounds"] = inbounds; } -#undef GET_DATA +#undef GET_SELECTED_CONNECTION_IDS + +void GroupManager::on_connectionsTable_customContextMenuRequested(const QPoint &pos) +{ + Q_UNUSED(pos) + connectionListRCMenu->popup(QCursor::pos()); +} diff --git a/src/ui/w_GroupManager.hpp b/src/ui/w_GroupManager.hpp index a53ccb1f..38100dd5 100644 --- a/src/ui/w_GroupManager.hpp +++ b/src/ui/w_GroupManager.hpp @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include "base/Qv2rayBase.hpp" #include "core/CoreSafeTypes.hpp" @@ -43,20 +43,16 @@ class GroupManager void on_connectionsList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous); - void on_connectionsList_customContextMenuRequested(const QPoint &pos); - void on_groupIsSubscriptionGroup_clicked(bool checked); void on_groupNameTxt_textEdited(const QString &arg1); - - private slots: void onRCMDeleteConnectionTriggered(); void onRCMExportConnectionTriggered(); - void on_deleteSelectedConnBtn_clicked(); - void on_exportSelectedConnBtn_clicked(); + void on_connectionsTable_customContextMenuRequested(const QPoint &pos); + private: void loadConnectionList(const GroupId &group); void onRCMActionTriggered_Move(); @@ -70,6 +66,7 @@ class GroupManager QAction *deleteConnectionAction = new QAction(tr("Delete Connection(s)"), connectionListRCMenu); QMenu *connectionListRCMenu_CopyToMenu = new QMenu(tr("Copy to...")); QMenu *connectionListRCMenu_MoveToMenu = new QMenu(tr("Move to...")); + QMenu *connectionListRCMenu_LinkToMenu = new QMenu(tr("Link to...")); void UpdateColorScheme(); bool isUpdateInProgress = false; GroupId currentGroupId = NullGroupId; diff --git a/src/ui/w_GroupManager.ui b/src/ui/w_GroupManager.ui index 92f5db60..7033a8af 100644 --- a/src/ui/w_GroupManager.ui +++ b/src/ui/w_GroupManager.ui @@ -168,22 +168,6 @@ Connections - - - - Qt::CustomContextMenu - - - QAbstractItemView::NoEditTriggers - - - QAbstractItemView::ExtendedSelection - - - QListView::Adjust - - - @@ -191,6 +175,36 @@ + + + + Qt::CustomContextMenu + + + QAbstractItemView::SelectRows + + + + Name + + + + + Type + + + + + Host / Port + + + + + Groups + + + + diff --git a/src/ui/w_MainWindow.cpp b/src/ui/w_MainWindow.cpp index 6b32578d..078610e0 100644 --- a/src/ui/w_MainWindow.cpp +++ b/src/ui/w_MainWindow.cpp @@ -175,7 +175,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) // connect(ConnectionManager, &QvConfigHandler::OnConnectionDeleted, this, &MainWindow::OnConnectionDeleted); connect(ConnectionManager, &QvConfigHandler::OnConnectionCreated, this, &MainWindow::OnConnectionCreated); - connect(ConnectionManager, &QvConfigHandler::OnConnectionGroupChanged, this, &MainWindow::OnConnectionGroupChanged); + connect(ConnectionManager, &QvConfigHandler::OnConnectionLinkedWithGroup, this, &MainWindow::OnConnectionLinkedWithGroup); // connect(ConnectionManager, &QvConfigHandler::OnGroupCreated, this, &MainWindow::OnGroupCreated); connect(ConnectionManager, &QvConfigHandler::OnGroupDeleted, this, &MainWindow::OnGroupDeleted); @@ -580,7 +580,7 @@ void MainWindow::on_action_RCM_DeleteThese_triggered() if (GlobalConfig.autoStartId == conn) GlobalConfig.autoStartId.clear(); - ConnectionManager->RemoveConnectionFromGroup(conn.connectionId, conn.groupId); + ConnectionManager->DeleteConnectionFromGroup(conn.connectionId, conn.groupId); } } @@ -843,12 +843,8 @@ void MainWindow::OnConnectionDeleted(const ConnectionGroupPair &id) auto child = connectionNodes.take(id); groupNodes.value(id.groupId)->removeChild(child.get()); } -void MainWindow::OnConnectionGroupChanged(const ConnectionId &id, const GroupId &originalGroup, const GroupId &newGroup) +void MainWindow::OnConnectionLinkedWithGroup(const ConnectionId &id, const GroupId &newGroup) { - const ConnectionGroupPair pair{ id, originalGroup }; - delete GetItemWidget(connectionNodes.value(pair).get()); - groupNodes.value(originalGroup)->removeChild(connectionNodes.value(pair).get()); - connectionNodes.remove(pair); MWAddConnectionItem_p({ id, newGroup }); } void MainWindow::OnGroupCreated(const GroupId &id, const QString &displayName) diff --git a/src/ui/w_MainWindow.hpp b/src/ui/w_MainWindow.hpp index 36345c1f..4ab567f7 100644 --- a/src/ui/w_MainWindow.hpp +++ b/src/ui/w_MainWindow.hpp @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include "common/HTTPRequestHelper.hpp" #include "common/LogHighlighter.hpp" @@ -91,7 +91,7 @@ class MainWindow // void OnConnectionCreated(const ConnectionGroupPair &Id, const QString &displayName); void OnConnectionDeleted(const ConnectionGroupPair &Id); - void OnConnectionGroupChanged(const ConnectionId &id, const GroupId &originalGroup, const GroupId &newGroup); + void OnConnectionLinkedWithGroup(const ConnectionId &id, const GroupId &newGroup); // void OnGroupCreated(const GroupId &id, const QString &displayName); void OnGroupDeleted(const GroupId &id, const QList &connections); diff --git a/src/ui/widgets/ConnectionInfoWidget.cpp b/src/ui/widgets/ConnectionInfoWidget.cpp index cc848b8f..bbe0b0e0 100644 --- a/src/ui/widgets/ConnectionInfoWidget.cpp +++ b/src/ui/widgets/ConnectionInfoWidget.cpp @@ -52,9 +52,9 @@ ConnectionInfoWidget::ConnectionInfoWidget(QWidget *parent) : QWidget(parent) connect(ConnectionManager, &QvConfigHandler::OnConnected, this, &ConnectionInfoWidget::OnConnected); connect(ConnectionManager, &QvConfigHandler::OnDisconnected, this, &ConnectionInfoWidget::OnDisConnected); connect(ConnectionManager, &QvConfigHandler::OnConnectionModified, this, &ConnectionInfoWidget::OnConnectionModified); - connect(ConnectionManager, &QvConfigHandler::OnConnectionGroupChanged, this, &ConnectionInfoWidget::OnConnectionModified); + connect(ConnectionManager, &QvConfigHandler::OnConnectionLinkedWithGroup, this, &ConnectionInfoWidget::OnConnectionModified); connect(ConnectionManager, &QvConfigHandler::OnGroupRenamed, this, &ConnectionInfoWidget::OnGroupRenamed); - connect(ConnectionManager, &QvConfigHandler::OnConnectionGroupChanged, this, &ConnectionInfoWidget::OnConnectionModified); + connect(ConnectionManager, &QvConfigHandler::OnConnectionLinkedWithGroup, this, &ConnectionInfoWidget::OnConnectionModified); } void ConnectionInfoWidget::ShowDetails(const ConnectionGroupPair &_identifier) @@ -163,7 +163,7 @@ void ConnectionInfoWidget::on_deleteBtn_clicked() { if (connectionId != NullConnectionId) { - ConnectionManager->RemoveConnectionFromGroup(connectionId, groupId); + ConnectionManager->DeleteConnectionFromGroup(connectionId, groupId); } else { diff --git a/src/ui/widgets/ConnectionItemWidget.cpp b/src/ui/widgets/ConnectionItemWidget.cpp index 4fbf237e..a77cc123 100644 --- a/src/ui/widgets/ConnectionItemWidget.cpp +++ b/src/ui/widgets/ConnectionItemWidget.cpp @@ -71,7 +71,7 @@ ConnectionItemWidget::ConnectionItemWidget(const GroupId &id, QWidget *parent) : connect(ConnectionManager, &QvConfigHandler::OnConnectionCreated, this, &ConnectionItemWidget::RecalculateConnectionsCount); connect(ConnectionManager, &QvConfigHandler::OnConnectionDeleted, this, &ConnectionItemWidget::RecalculateConnectionsCount); connect(ConnectionManager, &QvConfigHandler::OnConnectionModified, this, &ConnectionItemWidget::RecalculateConnectionsCount); - connect(ConnectionManager, &QvConfigHandler::OnConnectionGroupChanged, this, &ConnectionItemWidget::RecalculateConnectionsCount); + connect(ConnectionManager, &QvConfigHandler::OnConnectionLinkedWithGroup, this, &ConnectionItemWidget::RecalculateConnectionsCount); connect(ConnectionManager, &QvConfigHandler::OnSubscriptionUpdateFinished, this, &ConnectionItemWidget::RecalculateConnectionsCount); // connect(ConnectionManager, &QvConfigHandler::OnGroupRenamed, this, &ConnectionItemWidget::OnGroupItemRenamed); From f9b4813b8dc52f2be70c108e589c73e405aafa71 Mon Sep 17 00:00:00 2001 From: DuckVador Date: Mon, 11 May 2020 00:45:20 +0800 Subject: [PATCH 071/385] add nonblocking TCPing --- makespec/BUILDVERSION | 2 +- src/components/latency/QvTCPing.cpp | 109 ++++++++++++++++++++++++++-- 2 files changed, 104 insertions(+), 7 deletions(-) diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 2f114b59..80969167 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5366 \ No newline at end of file +5367 \ No newline at end of file diff --git a/src/components/latency/QvTCPing.cpp b/src/components/latency/QvTCPing.cpp index 77046fec..b511c10b 100644 --- a/src/components/latency/QvTCPing.cpp +++ b/src/components/latency/QvTCPing.cpp @@ -4,13 +4,113 @@ #else #include #include + #include #include + #include #include #endif #include "QtConcurrent/QtConcurrent" #include "QvTCPing.hpp" #include "core/handler/ConfigHandler.hpp" + +#ifdef _WIN32 + using qv_socket_t=SOCKET; +#else + using qv_socket_t=int; +#endif +namespace +{ + inline int setnonblocking(qv_socket_t sockno,int &opt) + { +#ifdef _WIN32 + ULONG block = 1; + if (ioctlsocket(sockno, FIONBIO, &block) == SOCKET_ERROR) + { + return -1; + } +#else + if ((opt = fcntl (sockno, F_GETFL, NULL)) < 0) { + //get socket flags + return -1; + } + if (fcntl (sockno, F_SETFL, opt | O_NONBLOCK) < 0) { + //set socket non-blocking + return -1; + } +#endif + return 0; + } + + inline int setblocking(qv_socket_t sockno,int &opt) + { +#ifdef _WIN32 + ULONG block = 0; + if (ioctlsocket(sockno, FIONBIO, &block) == SOCKET_ERROR) + { + return -1; + } +#else + if (fcntl (sockno, F_SETFL, opt) < 0) { + //reset socket flags + return -1; + } +#endif + return 0; + } + + + int connect_wait ( + qv_socket_t sockno, + struct sockaddr * addr, + size_t addrlen, + int timeout_sec=5) + { + int res; + int opt; + timeval tv = {0}; + tv.tv_sec = timeout_sec; + tv.tv_usec = 0; + if ((res = setnonblocking(sockno,opt))!=0) + return -1; + if ((res = ::connect (sockno, addr, addrlen)) < 0) { +#ifdef _WIN32 + if (WSAGetLastError() != WSAEWOULDBLOCK) { +#else + if (errno == EINPROGRESS) { +#endif + //connecting + fd_set wait_set; + FD_ZERO (&wait_set); + FD_SET (sockno, &wait_set); + res = select (sockno + 1, NULL, &wait_set, NULL, &tv); + } + } + else { + //connect immediately + res = 1; + } + if (setblocking(sockno,opt)!=0){ + return -1; + } + if (res < 0) { + //an error occured + return -1; + } + else if (res == 0) { + //timeout + return 1; + } + else { + socklen_t len = sizeof (opt); + if (getsockopt (sockno, SOL_SOCKET, SO_ERROR, reinterpret_cast(&opt), &len) < 0) { + return -1; + } + } + return 0; + } +} + namespace Qv2ray::components::tcping { static int resolveHost(const std::string &host, int portnr, struct addrinfo **res); @@ -159,11 +259,8 @@ namespace Qv2ray::components::tcping int testLatency(struct addrinfo *addr, std::chrono::system_clock::time_point *start, std::chrono::system_clock::time_point *end) { -#ifdef _WIN32 - SOCKET fd; -#else - int fd; -#endif + qv_socket_t fd; + const int on = 1; /* int flags; */ int rv = 0; @@ -198,7 +295,7 @@ namespace Qv2ray::components::tcping /* connect to peer */ // Qt has its own connect() function in QObject.... // So we add "::" here - if (::connect(fd, addr->ai_addr, addr->ai_addrlen) == 0) + if (connect_wait(fd, addr->ai_addr, addr->ai_addrlen)==0) { *end = system_clock::now(); #ifdef Q_OS_WIN From cb9ea94f141fc770a0d07a91f8899f21325a89fa Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Mon, 11 May 2020 09:11:47 +0800 Subject: [PATCH 072/385] fix: fixed build for Qt<5.14 --- makespec/BUILDVERSION | 2 +- src/common/QvHelpers.cpp | 16 ++++------------ 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 4d1d260e..a1003e33 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5379 \ No newline at end of file +5380 diff --git a/src/common/QvHelpers.cpp b/src/common/QvHelpers.cpp index 71043373..2a0d2fd1 100644 --- a/src/common/QvHelpers.cpp +++ b/src/common/QvHelpers.cpp @@ -125,21 +125,13 @@ namespace Qv2ray::common QStringList SplitLines(const QString &_string) { +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) return _string.split(QRegExp("[\r\n]"), Qt::SkipEmptyParts); +#else + return _string.split(QRegExp("[\r\n]"), QString::SkipEmptyParts); +#endif } - // list SplitLines_std(const QString &_string) - // { - // list list; - - // for (auto line : _string.split(QRegExp("[\r\n]"), QString::SkipEmptyParts)) - // { - // list.push_back(line.toStdString()); - // } - - // return list; - // } - QStringList GetFileList(const QDir &dir) { return dir.entryList(QStringList{ "*", "*.*" }, QDir::Hidden | QDir::Files); From b51fa558a164f7aad76287825e1bfb4e3035721c Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Mon, 11 May 2020 09:35:45 +0800 Subject: [PATCH 073/385] fix: fixed warnings and added pre-commit hook --- hooks/pre-commit | 12 ++++++++++++ makespec/BUILDVERSION | 2 +- src/common/HTTPRequestHelper.cpp | 4 ++-- src/components/autolaunch/QvAutoLaunch.cpp | 20 ++++++++++---------- src/core/connection/Serialization_ssd.cpp | 2 +- 5 files changed, 26 insertions(+), 14 deletions(-) create mode 100755 hooks/pre-commit diff --git a/hooks/pre-commit b/hooks/pre-commit new file mode 100755 index 00000000..a7d0ad5c --- /dev/null +++ b/hooks/pre-commit @@ -0,0 +1,12 @@ +#!/bin/bash +STAGE_FILES=$(git diff --cached --name-only --diff-filter=ACM -- 'makespec/BUILDVERSION') +#echo $STAGE_FILES +if test ${#STAGE_FILES} -gt 0 +then + echo 'BUILDVERSION already changed, not touching' +else + echo 'Increasing BUILDVERSION' + expr $(cat ./makespec/BUILDVERSION) + 1 > ./makespec/BUILDVERSION + cat ./makespec/BUILDVERSION + git add ./makespec/BUILDVERSION +fi diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index a1003e33..70bea13d 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5380 +5384 diff --git a/src/common/HTTPRequestHelper.cpp b/src/common/HTTPRequestHelper.cpp index 7295b2d2..8c8648bc 100644 --- a/src/common/HTTPRequestHelper.cpp +++ b/src/common/HTTPRequestHelper.cpp @@ -56,7 +56,7 @@ namespace Qv2ray::common } request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy); - request.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, true); + request.setAttribute(QNetworkRequest::Http2AllowedAttribute, true); auto ua = GlobalConfig.networkConfig.userAgent; ua.replace("$VERSION", QV2RAY_VERSION_STRING); request.setHeader(QNetworkRequest::KnownHeaders::UserAgentHeader, ua); @@ -88,7 +88,7 @@ namespace Qv2ray::common void QvHttpRequestHelper::onRequestFinished_p() { - if (reply->attribute(QNetworkRequest::HTTP2WasUsedAttribute).toBool()) + if (reply->attribute(QNetworkRequest::Http2WasUsedAttribute).toBool()) { DEBUG(MODULE_NETWORK, "HTTP/2 was used.") } diff --git a/src/components/autolaunch/QvAutoLaunch.cpp b/src/components/autolaunch/QvAutoLaunch.cpp index cdf2f006..3390bccb 100644 --- a/src/components/autolaunch/QvAutoLaunch.cpp +++ b/src/components/autolaunch/QvAutoLaunch.cpp @@ -184,16 +184,16 @@ namespace Qv2ray::components::autolaunch QTextStream ts(&iniFile); ts.setCodec("UTF-8"); - ts << QLatin1String("[Desktop Entry]") << endl - << QLatin1String("Name=") << QApplication::applicationName() << endl - << QLatin1String("GenericName=") << QLatin1String("V2ray Frontend") << endl - << QLatin1String("Exec=") << binPath << endl - << QLatin1String("Terminal=") << "false" << endl - << QLatin1String("Icon=") << "qv2ray" << endl // always use lowercase for icons - << QLatin1String("Categories=") << "Network" << endl - << QLatin1String("Type=") << "Application" << endl - << QLatin1String("StartupNotify=") << "false" << endl - << QLatin1String("X-GNOME-Autostart-enabled=") << "true" << endl; + ts << QLatin1String("[Desktop Entry]") << Qt::endl + << QLatin1String("Name=") << QApplication::applicationName() + QApplication::arguments().join(" ") << Qt::endl + << QLatin1String("GenericName=") << QLatin1String("V2ray Frontend") << Qt::endl + << QLatin1String("Exec=") << binPath << Qt::endl + << QLatin1String("Terminal=") << "false" << Qt::endl + << QLatin1String("Icon=") << "qv2ray" << Qt::endl // always use lowercase for icons + << QLatin1String("Categories=") << "Network" << Qt::endl + << QLatin1String("Type=") << "Application" << Qt::endl + << QLatin1String("StartupNotify=") << "false" << Qt::endl + << QLatin1String("X-GNOME-Autostart-enabled=") << "true" << Qt::endl; } else { diff --git a/src/core/connection/Serialization_ssd.cpp b/src/core/connection/Serialization_ssd.cpp index 6d2c9622..17f6e541 100644 --- a/src/core/connection/Serialization_ssd.cpp +++ b/src/core/connection/Serialization_ssd.cpp @@ -209,7 +209,7 @@ namespace Qv2ray::core::connection::Serialization OUTBOUNDS outbounds; outbounds.append(GenerateOutboundEntry("shadowsocks", GenerateShadowSocksOUT({ ssObject }), {})); JADD(outbounds) - serverList.insertMulti(totalName, root); + serverList.insert(totalName, root); } // returns the current result From f4b694ff6bc9a873b0497278e6a16aeae0d9d0af Mon Sep 17 00:00:00 2001 From: DuckVador Date: Mon, 11 May 2020 09:40:21 +0800 Subject: [PATCH 074/385] format code --- src/components/latency/QvTCPing.cpp | 90 +++++++++++++++-------------- 1 file changed, 48 insertions(+), 42 deletions(-) diff --git a/src/components/latency/QvTCPing.cpp b/src/components/latency/QvTCPing.cpp index b511c10b..69c732f0 100644 --- a/src/components/latency/QvTCPing.cpp +++ b/src/components/latency/QvTCPing.cpp @@ -2,26 +2,25 @@ #include #include #else - #include - #include - #include - #include #include + #include + #include + #include + #include #include #endif #include "QtConcurrent/QtConcurrent" #include "QvTCPing.hpp" #include "core/handler/ConfigHandler.hpp" - #ifdef _WIN32 - using qv_socket_t=SOCKET; +using qv_socket_t = SOCKET; #else - using qv_socket_t=int; +using qv_socket_t = int; #endif namespace { - inline int setnonblocking(qv_socket_t sockno,int &opt) + inline int setnonblocking(qv_socket_t sockno, int &opt) { #ifdef _WIN32 ULONG block = 1; @@ -30,19 +29,21 @@ namespace return -1; } #else - if ((opt = fcntl (sockno, F_GETFL, NULL)) < 0) { - //get socket flags + if ((opt = fcntl(sockno, F_GETFL, NULL)) < 0) + { + // get socket flags return -1; } - if (fcntl (sockno, F_SETFL, opt | O_NONBLOCK) < 0) { - //set socket non-blocking + if (fcntl(sockno, F_SETFL, opt | O_NONBLOCK) < 0) + { + // set socket non-blocking return -1; } #endif return 0; } - inline int setblocking(qv_socket_t sockno,int &opt) + inline int setblocking(qv_socket_t sockno, int &opt) { #ifdef _WIN32 ULONG block = 0; @@ -51,65 +52,70 @@ namespace return -1; } #else - if (fcntl (sockno, F_SETFL, opt) < 0) { - //reset socket flags + if (fcntl(sockno, F_SETFL, opt) < 0) + { + // reset socket flags return -1; } #endif return 0; } - - int connect_wait ( - qv_socket_t sockno, - struct sockaddr * addr, - size_t addrlen, - int timeout_sec=5) + int connect_wait(qv_socket_t sockno, struct sockaddr *addr, size_t addrlen, int timeout_sec = 5) { int res; int opt; - timeval tv = {0}; + timeval tv = { 0 }; tv.tv_sec = timeout_sec; tv.tv_usec = 0; - if ((res = setnonblocking(sockno,opt))!=0) + if ((res = setnonblocking(sockno, opt)) != 0) return -1; - if ((res = ::connect (sockno, addr, addrlen)) < 0) { + if ((res = ::connect(sockno, addr, addrlen)) < 0) + { #ifdef _WIN32 - if (WSAGetLastError() != WSAEWOULDBLOCK) { + if (WSAGetLastError() != WSAEWOULDBLOCK) + { #else - if (errno == EINPROGRESS) { + if (errno == EINPROGRESS) + { #endif - //connecting + // connecting fd_set wait_set; - FD_ZERO (&wait_set); - FD_SET (sockno, &wait_set); - res = select (sockno + 1, NULL, &wait_set, NULL, &tv); + FD_ZERO(&wait_set); + FD_SET(sockno, &wait_set); + res = select(sockno + 1, NULL, &wait_set, NULL, &tv); } } - else { - //connect immediately + else + { + // connect immediately res = 1; } - if (setblocking(sockno,opt)!=0){ + if (setblocking(sockno, opt) != 0) + { return -1; } - if (res < 0) { - //an error occured + if (res < 0) + { + // an error occured return -1; } - else if (res == 0) { - //timeout + else if (res == 0) + { + // timeout return 1; } - else { - socklen_t len = sizeof (opt); - if (getsockopt (sockno, SOL_SOCKET, SO_ERROR, reinterpret_cast(&opt), &len) < 0) { + else + { + socklen_t len = sizeof(opt); + if (getsockopt(sockno, SOL_SOCKET, SO_ERROR, reinterpret_cast(&opt), &len) < 0) + { return -1; } } return 0; } -} +} // namespace namespace Qv2ray::components::tcping { @@ -295,7 +301,7 @@ namespace Qv2ray::components::tcping /* connect to peer */ // Qt has its own connect() function in QObject.... // So we add "::" here - if (connect_wait(fd, addr->ai_addr, addr->ai_addrlen)==0) + if (connect_wait(fd, addr->ai_addr, addr->ai_addrlen) == 0) { *end = system_clock::now(); #ifdef Q_OS_WIN From 7b7374a09b5f4a0f1a38fb85cdf55839bcc34793 Mon Sep 17 00:00:00 2001 From: DuckSoft Date: Mon, 11 May 2020 13:20:36 +0800 Subject: [PATCH 075/385] naive port detector implementation --- CMakeLists.txt | 2 ++ src/components/port/QvPortDetector.cpp | 13 +++++++++++++ src/components/port/QvPortDetector.hpp | 7 +++++++ 3 files changed, 22 insertions(+) create mode 100644 src/components/port/QvPortDetector.cpp create mode 100644 src/components/port/QvPortDetector.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index a3789ce7..d20dc152 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -174,6 +174,7 @@ set(QV2RAY_SOURCES src/components/plugins/toolbar/QvToolbar_linux.cpp src/components/plugins/toolbar/QvToolbar_win.cpp src/components/plugins/QvPluginHost.cpp + src/components/port/QvPortDetector.cpp src/components/proxy/QvProxyConfigurator.cpp src/components/route/RouteSchemeIO.cpp src/components/speedchart/speedplotview.cpp @@ -269,6 +270,7 @@ set(QV2RAY_SOURCES src/components/speedchart/speedplotview.hpp src/components/speedchart/speedwidget.hpp src/components/update/UpdateChecker.hpp + src/components/port/QvPortDetector.hpp src/core/connection/ConnectionIO.hpp src/core/connection/Generation.hpp src/core/connection/Serialization.hpp diff --git a/src/components/port/QvPortDetector.cpp b/src/components/port/QvPortDetector.cpp new file mode 100644 index 00000000..eca0de22 --- /dev/null +++ b/src/components/port/QvPortDetector.cpp @@ -0,0 +1,13 @@ +#include "QvPortDetector.hpp" + +#include + +namespace Qv2ray::components::port +{ + bool detectPortTCP(quint16 port) + { + QTcpServer server; + return server.listen(QHostAddress::LocalHost, port); + } + +} // namespace Qv2ray::components::port diff --git a/src/components/port/QvPortDetector.hpp b/src/components/port/QvPortDetector.hpp new file mode 100644 index 00000000..dd50c6ff --- /dev/null +++ b/src/components/port/QvPortDetector.hpp @@ -0,0 +1,7 @@ +#pragma once +#include + +namespace Qv2ray::components::port +{ + bool detectPortTCP(quint16 port); +} From 7d2bbd9e7b68e398044fe24294e45e903181e56c Mon Sep 17 00:00:00 2001 From: DuckSoft Date: Tue, 12 May 2020 20:12:49 +0800 Subject: [PATCH 076/385] add warning when users switch off api subsystem --- src/ui/w_PreferencesWindow.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/ui/w_PreferencesWindow.cpp b/src/ui/w_PreferencesWindow.cpp index a83ae8a9..6af797ce 100644 --- a/src/ui/w_PreferencesWindow.cpp +++ b/src/ui/w_PreferencesWindow.cpp @@ -1157,7 +1157,15 @@ void PreferencesWindow::on_enableAPI_stateChanged(int arg1) { LOADINGCHECK NEEDRESTART + CurrentConfig.apiConfig.enableAPI = arg1 == Qt::Checked; + if (arg1 == Qt::Unchecked) + { + const auto msgAPIDisableTitle = tr("Disabling API Subsystem"); + const auto msgAPIDisableMsg = tr("Disabling API subsystem will also disable the statistics function of Qv2ray.") + NEWLINE + // + tr("Speed chart and traffic statistics will be disabled."); + QvMessageBoxWarn(this, msgAPIDisableTitle, msgAPIDisableMsg); + } } void PreferencesWindow::on_updateChannelCombo_currentIndexChanged(int index) From 78aa2f645d083d98956970a2f692f303a8174b11 Mon Sep 17 00:00:00 2001 From: DuckSoft Date: Tue, 12 May 2020 20:20:54 +0800 Subject: [PATCH 077/385] updating translations --- translations/en_US.ts | 12 ++++++++++++ translations/ja_JP.ts | 12 ++++++++++++ translations/ru_RU.ts | 12 ++++++++++++ translations/zh_CN.ts | 12 ++++++++++++ 4 files changed, 48 insertions(+) diff --git a/translations/en_US.ts b/translations/en_US.ts index ebfea120..41df5f9f 100644 --- a/translations/en_US.ts +++ b/translations/en_US.ts @@ -1734,6 +1734,18 @@ But could damage your server if improperly used. For example, for updating subscriptions. + + Disabling API Subsystem + + + + Disabling API subsystem will also disable the statistics function of Qv2ray. + + + + Speed chart and traffic statistics will be disabled. + + QObject diff --git a/translations/ja_JP.ts b/translations/ja_JP.ts index 729473ae..288224b2 100644 --- a/translations/ja_JP.ts +++ b/translations/ja_JP.ts @@ -2450,6 +2450,18 @@ For example, for updating subscriptions. これらの設定はQv2ray自体のためのものです。 例えば、サブスクリプションの更新などです。 + + Disabling API Subsystem + APIサブシステムを無効 + + + Disabling API subsystem will also disable the statistics function of Qv2ray. + APIサブシステムを無効にすると、Qv2rayの統計機能も無効になります。 + + + Speed chart and traffic statistics will be disabled. + スピードチャートや交通統計は無効になります。 + QObject diff --git a/translations/ru_RU.ts b/translations/ru_RU.ts index b82073f9..425a6614 100644 --- a/translations/ru_RU.ts +++ b/translations/ru_RU.ts @@ -2349,6 +2349,18 @@ But could damage your server if improperly used. For example, for updating subscriptions. + + Disabling API Subsystem + + + + Disabling API subsystem will also disable the statistics function of Qv2ray. + + + + Speed chart and traffic statistics will be disabled. + + QObject diff --git a/translations/zh_CN.ts b/translations/zh_CN.ts index c2b03b9c..c1a9d3cc 100644 --- a/translations/zh_CN.ts +++ b/translations/zh_CN.ts @@ -2080,6 +2080,18 @@ For example, for updating subscriptions. 这些设定是针对 Qv2ray 本身的。 例如,用在更新订阅时。 + + Disabling API Subsystem + 禁用 API 子系统 + + + Disabling API subsystem will also disable the statistics function of Qv2ray. + 如果禁用 API 子系统,Qv2ray 的统计功能也会被一同禁用。 + + + Speed chart and traffic statistics will be disabled. + 速度图表和流量统计功能将不再可用。 + QObject From 51b43815bdae9e05bfc7bcad4e4f4b6ed5b41711 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Tue, 12 May 2020 20:22:49 +0800 Subject: [PATCH 078/385] refactor: refactored ConnectionHandler signals --- libs/QJsonStruct | 2 +- makespec/BUILDVERSION | 2 +- src/core/handler/ConfigHandler.cpp | 10 +++++----- src/core/handler/ConfigHandler.hpp | 15 +++++++++------ src/ui/messaging/QvMessageBus.hpp | 2 -- src/ui/w_GroupManager.cpp | 8 ++++---- src/ui/w_ImportConfig.cpp | 4 ++-- src/ui/w_MainWindow.cpp | 9 +++++---- src/ui/w_MainWindow.hpp | 2 +- src/ui/widgets/ConnectionInfoWidget.cpp | 13 +++++++++---- src/ui/widgets/ConnectionInfoWidget.hpp | 3 ++- 11 files changed, 39 insertions(+), 31 deletions(-) diff --git a/libs/QJsonStruct b/libs/QJsonStruct index 09e5706d..fb566f7d 160000 --- a/libs/QJsonStruct +++ b/libs/QJsonStruct @@ -1 +1 @@ -Subproject commit 09e5706dab5c7f221a5080ac430922857c25c94a +Subproject commit fb566f7dfb43e66c3021f2b58a9c2003cd3050ac diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 70bea13d..e386d48f 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5384 +5387 \ No newline at end of file diff --git a/src/core/handler/ConfigHandler.cpp b/src/core/handler/ConfigHandler.cpp index b3a8074d..c0cdc865 100644 --- a/src/core/handler/ConfigHandler.cpp +++ b/src/core/handler/ConfigHandler.cpp @@ -255,7 +255,7 @@ namespace Qv2ray::core::handlers groups[newGroupId].connections.append(id); } PluginHost->Send_ConnectionEvent({ Events::ConnectionEntry::LinkedWithGroup, connections[id].displayName, "" }); - emit OnConnectionLinkedWithGroup(id, newGroupId); + emit OnConnectionLinkedWithGroup({ id, newGroupId }); return {}; } @@ -523,7 +523,7 @@ namespace Qv2ray::core::handlers { // New connection id is required since nothing matched found... LOG(MODULE_CORE_HANDLER, "Generated new connection id for connection: " + _alias) - CreateConnection(_alias, id, config); + CreateConnection(config, _alias, id); } // End guessing connectionId } @@ -561,8 +561,8 @@ namespace Qv2ray::core::handlers connections[connectionId].downLinkData }); } - const ConnectionId QvConfigHandler::CreateConnection(const QString &displayName, const GroupId &groupId, const CONFIGROOT &root, - bool skipSaveConfig) + const ConnectionGroupPair QvConfigHandler::CreateConnection(const CONFIGROOT &root, const QString &displayName, const GroupId &groupId, + bool skipSaveConfig) { LOG(MODULE_CORE_HANDLER, "Creating new connection: " + displayName) ConnectionId newId(GenerateUuid()); @@ -576,7 +576,7 @@ namespace Qv2ray::core::handlers { CHSaveConfigData(); } - return newId; + return { newId, groupId }; } } // namespace Qv2ray::core::handlers diff --git a/src/core/handler/ConfigHandler.hpp b/src/core/handler/ConfigHandler.hpp index 38982e40..8cb3f6b7 100644 --- a/src/core/handler/ConfigHandler.hpp +++ b/src/core/handler/ConfigHandler.hpp @@ -88,14 +88,17 @@ namespace Qv2ray::core::handlers void RestartConnection(); // // Connection Operations. - bool UpdateConnection(const ConnectionId &id, const CONFIGROOT &root, bool skipRestart = false); void ClearGroupUsage(const GroupId &id); void ClearConnectionUsage(const ConnectionGroupPair &id); - const std::optional DeleteConnectionFromGroup(const ConnectionId &id, const GroupId &gid); + // + const ConnectionGroupPair CreateConnection(const CONFIGROOT &root, const QString &displayName, const GroupId &groupId = DefaultGroupId, + bool skipSaveConfig = false); + bool UpdateConnection(const ConnectionId &id, const CONFIGROOT &root, bool skipRestart = false); const std::optional RenameConnection(const ConnectionId &id, const QString &newName); + // + // Connection - Group binding + const std::optional DeleteConnectionFromGroup(const ConnectionId &id, const GroupId &gid); const std::optional LinkConnectionWithGroup(const ConnectionId &id, const GroupId &newGroupId); - 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; @@ -124,9 +127,9 @@ namespace Qv2ray::core::handlers void OnConnectionRenamed(const ConnectionId &Id, const QString &originalName, const QString &newName); void OnConnectionCreated(const ConnectionGroupPair &Id, const QString &displayName); void OnConnectionDeleted(const ConnectionGroupPair &Id); - void OnConnectionRemovedFromGroup(const ConnectionGroupPair &groupId); void OnConnectionModified(const ConnectionId &id); - void OnConnectionLinkedWithGroup(const ConnectionId &id, const GroupId &newGroup); + void OnConnectionRemovedFromGroup(const ConnectionGroupPair &pairId); + void OnConnectionLinkedWithGroup(const ConnectionGroupPair &newPair); // void OnLatencyTestStarted(const ConnectionId &id); void OnLatencyTestFinished(const ConnectionId &id, const uint average); diff --git a/src/ui/messaging/QvMessageBus.hpp b/src/ui/messaging/QvMessageBus.hpp index 71cfb0e9..f2e6ecbd 100644 --- a/src/ui/messaging/QvMessageBus.hpp +++ b/src/ui/messaging/QvMessageBus.hpp @@ -48,8 +48,6 @@ namespace Qv2ray::ui::messaging Q_OBJECT public: explicit QvMessageBusObject(); - - // void EmitGlobalSignal(const QvMBMessage &msg); signals: void QvSendMessage(const QvMBMessage &msg); diff --git a/src/ui/w_GroupManager.cpp b/src/ui/w_GroupManager.cpp index ff8fe7c4..f686f884 100644 --- a/src/ui/w_GroupManager.cpp +++ b/src/ui/w_GroupManager.cpp @@ -33,7 +33,6 @@ GroupManager::GroupManager(QWidget *parent) : QDialog(parent) setupUi(this); QvMessageBusConnect(GroupManager); UpdateColorScheme(); - connectionListRCMenu->addSection(tr("Connection Management")); connectionListRCMenu->addAction(exportConnectionAction); connectionListRCMenu->addAction(deleteConnectionAction); @@ -45,10 +44,11 @@ GroupManager::GroupManager(QWidget *parent) : QDialog(parent) connect(deleteConnectionAction, &QAction::triggered, this, &GroupManager::onRCMDeleteConnectionTriggered); // connect(ConnectionManager, &QvConfigHandler::OnConnectionLinkedWithGroup, // - [&](const ConnectionId &, const GroupId &) { // + [&]() { // this->loadConnectionList(currentGroupId); // }); // + // Anyway just reload it. const auto reloadGroupLambda = [&](const ConnectionGroupPair &id) { if (id.groupId == currentGroupId) this->loadConnectionList(id.groupId); @@ -57,7 +57,7 @@ GroupManager::GroupManager(QWidget *parent) : QDialog(parent) connect(ConnectionManager, &QvConfigHandler::OnConnectionDeleted, reloadGroupLambda); connect(ConnectionManager, &QvConfigHandler::OnConnectionRemovedFromGroup, reloadGroupLambda); // - for (auto group : ConnectionManager->AllGroups()) + for (const auto &group : ConnectionManager->AllGroups()) { auto item = new QListWidgetItem(GetDisplayName(group)); item->setData(Qt::UserRole, group.toString()); @@ -189,7 +189,7 @@ void GroupManager::onRCMActionTriggered_Copy() for (const auto &connId : list) { const auto &connectionId = ConnectionId(connId); - ConnectionManager->CreateConnection(GetDisplayName(connectionId), groupId, ConnectionManager->GetConnectionRoot(connectionId), true); + ConnectionManager->CreateConnection(ConnectionManager->GetConnectionRoot(connectionId), GetDisplayName(connectionId), groupId, true); } } diff --git a/src/ui/w_ImportConfig.cpp b/src/ui/w_ImportConfig.cpp index 5df57199..75da02b0 100644 --- a/src/ui/w_ImportConfig.cpp +++ b/src/ui/w_ImportConfig.cpp @@ -1,4 +1,4 @@ -#include "w_ImportConfig.hpp" +#include "w_ImportConfig.hpp" #include "common/QRCodeHelper.hpp" #include "core/CoreUtils.hpp" @@ -80,7 +80,7 @@ int ImportConfigWindow::ImportConnection() { connName = protocol + "/" + host + ":" + QSTRN(port) + "-" + GenerateRandomString(5); } - ConnectionManager->CreateConnection(connName, groupId, connConf, true); + ConnectionManager->CreateConnection(connConf, connName, groupId, true); } } diff --git a/src/ui/w_MainWindow.cpp b/src/ui/w_MainWindow.cpp index 078610e0..ebbaf14c 100644 --- a/src/ui/w_MainWindow.cpp +++ b/src/ui/w_MainWindow.cpp @@ -176,6 +176,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) connect(ConnectionManager, &QvConfigHandler::OnConnectionDeleted, this, &MainWindow::OnConnectionDeleted); connect(ConnectionManager, &QvConfigHandler::OnConnectionCreated, this, &MainWindow::OnConnectionCreated); connect(ConnectionManager, &QvConfigHandler::OnConnectionLinkedWithGroup, this, &MainWindow::OnConnectionLinkedWithGroup); + connect(ConnectionManager, &QvConfigHandler::OnConnectionRemovedFromGroup, this, &MainWindow::OnConnectionLinkedWithGroup); // connect(ConnectionManager, &QvConfigHandler::OnGroupCreated, this, &MainWindow::OnGroupCreated); connect(ConnectionManager, &QvConfigHandler::OnGroupDeleted, this, &MainWindow::OnGroupDeleted); @@ -843,9 +844,9 @@ void MainWindow::OnConnectionDeleted(const ConnectionGroupPair &id) auto child = connectionNodes.take(id); groupNodes.value(id.groupId)->removeChild(child.get()); } -void MainWindow::OnConnectionLinkedWithGroup(const ConnectionId &id, const GroupId &newGroup) +void MainWindow::OnConnectionLinkedWithGroup(const ConnectionGroupPair &pairId) { - MWAddConnectionItem_p({ id, newGroup }); + MWAddConnectionItem_p(pairId); } void MainWindow::OnGroupCreated(const GroupId &id, const QString &displayName) { @@ -901,8 +902,8 @@ void MainWindow::on_action_RCM_DuplicateThese_triggered() for (const auto &conn : connlist) { - ConnectionManager->CreateConnection(GetDisplayName(conn.connectionId) + tr(" (Copy)"), conn.groupId, - ConnectionManager->GetConnectionRoot(conn.connectionId)); + ConnectionManager->CreateConnection(ConnectionManager->GetConnectionRoot(conn.connectionId), + GetDisplayName(conn.connectionId) + tr(" (Copy)"), conn.groupId); } } diff --git a/src/ui/w_MainWindow.hpp b/src/ui/w_MainWindow.hpp index 4ab567f7..6b2d0780 100644 --- a/src/ui/w_MainWindow.hpp +++ b/src/ui/w_MainWindow.hpp @@ -91,7 +91,7 @@ class MainWindow // void OnConnectionCreated(const ConnectionGroupPair &Id, const QString &displayName); void OnConnectionDeleted(const ConnectionGroupPair &Id); - void OnConnectionLinkedWithGroup(const ConnectionId &id, const GroupId &newGroup); + void OnConnectionLinkedWithGroup(const ConnectionGroupPair &id); // void OnGroupCreated(const GroupId &id, const QString &displayName); void OnGroupDeleted(const GroupId &id, const QList &connections); diff --git a/src/ui/widgets/ConnectionInfoWidget.cpp b/src/ui/widgets/ConnectionInfoWidget.cpp index bbe0b0e0..483450d0 100644 --- a/src/ui/widgets/ConnectionInfoWidget.cpp +++ b/src/ui/widgets/ConnectionInfoWidget.cpp @@ -51,10 +51,10 @@ ConnectionInfoWidget::ConnectionInfoWidget(QWidget *parent) : QWidget(parent) // connect(ConnectionManager, &QvConfigHandler::OnConnected, this, &ConnectionInfoWidget::OnConnected); connect(ConnectionManager, &QvConfigHandler::OnDisconnected, this, &ConnectionInfoWidget::OnDisConnected); - connect(ConnectionManager, &QvConfigHandler::OnConnectionModified, this, &ConnectionInfoWidget::OnConnectionModified); - connect(ConnectionManager, &QvConfigHandler::OnConnectionLinkedWithGroup, this, &ConnectionInfoWidget::OnConnectionModified); connect(ConnectionManager, &QvConfigHandler::OnGroupRenamed, this, &ConnectionInfoWidget::OnGroupRenamed); - connect(ConnectionManager, &QvConfigHandler::OnConnectionLinkedWithGroup, this, &ConnectionInfoWidget::OnConnectionModified); + connect(ConnectionManager, &QvConfigHandler::OnConnectionModified, this, &ConnectionInfoWidget::OnConnectionModified); + connect(ConnectionManager, &QvConfigHandler::OnConnectionLinkedWithGroup, this, &ConnectionInfoWidget::OnConnectionModified_Pair); + connect(ConnectionManager, &QvConfigHandler::OnConnectionRemovedFromGroup, this, &ConnectionInfoWidget::OnConnectionModified_Pair); } void ConnectionInfoWidget::ShowDetails(const ConnectionGroupPair &_identifier) @@ -121,10 +121,15 @@ ConnectionInfoWidget::~ConnectionInfoWidget() void ConnectionInfoWidget::OnConnectionModified(const ConnectionId &id) { - if (id == connectionId) + if (id == this->connectionId) ShowDetails({ id, groupId }); } +void ConnectionInfoWidget::OnConnectionModified_Pair(const ConnectionGroupPair &id) +{ + if (id.connectionId == this->connectionId && id.groupId == this->groupId) + ShowDetails(id); +} void ConnectionInfoWidget::OnGroupRenamed(const GroupId &id, const QString &oldName, const QString &newName) { Q_UNUSED(oldName) diff --git a/src/ui/widgets/ConnectionInfoWidget.hpp b/src/ui/widgets/ConnectionInfoWidget.hpp index 2e481d4c..952a972c 100644 --- a/src/ui/widgets/ConnectionInfoWidget.hpp +++ b/src/ui/widgets/ConnectionInfoWidget.hpp @@ -30,10 +30,11 @@ class ConnectionInfoWidget void on_editJsonBtn_clicked(); void on_deleteBtn_clicked(); - void OnConnectionModified(const ConnectionId &id); void OnGroupRenamed(const GroupId &id, const QString &oldName, const QString &newName); void OnConnected(const ConnectionGroupPair &id); void OnDisConnected(const ConnectionGroupPair &id); + void OnConnectionModified(const ConnectionId &id); + void OnConnectionModified_Pair(const ConnectionGroupPair &id); void on_latencyBtn_clicked(); private: From f7a92257542a010eee6e30323063555282f63bc0 Mon Sep 17 00:00:00 2001 From: DuckSoft Date: Tue, 12 May 2020 20:53:15 +0800 Subject: [PATCH 079/385] add trusted abi --- src/core/kernel/QvKernelABIChecker.cpp | 10 ++++++++++ src/core/kernel/QvKernelABIChecker.hpp | 4 +++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/core/kernel/QvKernelABIChecker.cpp b/src/core/kernel/QvKernelABIChecker.cpp index d2c57b5f..a6749f80 100644 --- a/src/core/kernel/QvKernelABIChecker.cpp +++ b/src/core/kernel/QvKernelABIChecker.cpp @@ -6,6 +6,7 @@ namespace Qv2ray::core::kernel::abi { QvKernelABICompatibility checkCompatibility(QvKernelABIType hostType, QvKernelABIType targetType) { +#ifndef QV2RAY_TRUSTED_ABI switch (hostType) { case ABI_WIN32: @@ -15,12 +16,19 @@ namespace Qv2ray::core::kernel::abi 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; + case ABI_TRUSTED: return ABI_PERFECT; default: return ABI_MAYBE; } +#else + return ABI_PERFECT; +#endif } std::pair, std::optional> deduceKernelABI(const QString &pathCoreExecutable) { +#ifdef QV2RAY_TRUSTED_ABI + return QvKernelABIType::ABI_TRUSTED; +#else QFile file(pathCoreExecutable); if (!file.exists()) return { std::nullopt, QObject::tr("core executable file %1 does not exist").arg(pathCoreExecutable) }; @@ -55,6 +63,7 @@ namespace Qv2ray::core::kernel::abi return { QvKernelABIType::ABI_MACH_O, std::nullopt }; else return { std::nullopt, QObject::tr("cannot deduce the type of core executable file %1").arg(pathCoreExecutable) }; +#endif } QString abiToString(QvKernelABIType abi) @@ -68,6 +77,7 @@ namespace Qv2ray::core::kernel::abi 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"); + case ABI_TRUSTED: return QObject::tr("trusted abi"); default: return QObject::tr("unknown abi"); } } diff --git a/src/core/kernel/QvKernelABIChecker.hpp b/src/core/kernel/QvKernelABIChecker.hpp index d0346388..5e166f13 100644 --- a/src/core/kernel/QvKernelABIChecker.hpp +++ b/src/core/kernel/QvKernelABIChecker.hpp @@ -19,6 +19,7 @@ namespace Qv2ray::core::kernel ABI_ELF_AARCH64, ABI_ELF_ARM, ABI_ELF_OTHER, + ABI_TRUSTED, }; enum QvKernelABICompatibility @@ -42,7 +43,8 @@ namespace Qv2ray::core::kernel #elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_ARM_V7) QvKernelABIType::ABI_ELF_ARM; #else - #error "unknown architecture" + QvKernelABIType::ABI_TRUSTED; + #define QV2RAY_TRUSTED_ABI #endif std::pair, std::optional> deduceKernelABI(const QString &pathCoreExecutable); From 128c4bc9cb4a30b659b62e10da83caea4e15ae65 Mon Sep 17 00:00:00 2001 From: DuckSoft Date: Tue, 12 May 2020 22:57:36 +0800 Subject: [PATCH 080/385] update translations --- translations/en_US.ts | 4 ++++ translations/ja_JP.ts | 4 ++++ translations/ru_RU.ts | 4 ++++ translations/zh_CN.ts | 4 ++++ 4 files changed, 16 insertions(+) diff --git a/translations/en_US.ts b/translations/en_US.ts index 41df5f9f..8a1e1f74 100644 --- a/translations/en_US.ts +++ b/translations/en_US.ts @@ -2137,6 +2137,10 @@ For example, for updating subscriptions. Qv2ray cannot help you change them back. Please don't blame us if things go wrong. + + trusted abi + + Qv2ray::common::QvCommandArgParser diff --git a/translations/ja_JP.ts b/translations/ja_JP.ts index 288224b2..35de90c9 100644 --- a/translations/ja_JP.ts +++ b/translations/ja_JP.ts @@ -2953,6 +2953,10 @@ For example, for updating subscriptions. Qv2ray cannot help you change them back. Please don't blame us if things go wrong. Qv2rayでは元に戻すことはできません。何かあっても私たちのせいにしないでください。 + + trusted abi + 信頼できるABI + Qv2ray::common::QvCommandArgParser diff --git a/translations/ru_RU.ts b/translations/ru_RU.ts index 425a6614..ce25df9f 100644 --- a/translations/ru_RU.ts +++ b/translations/ru_RU.ts @@ -2824,6 +2824,10 @@ For example, for updating subscriptions. Qv2ray cannot help you change them back. Please don't blame us if things go wrong. + + trusted abi + + Qv2ray::common::QvCommandArgParser diff --git a/translations/zh_CN.ts b/translations/zh_CN.ts index c1a9d3cc..dabc3a9f 100644 --- a/translations/zh_CN.ts +++ b/translations/zh_CN.ts @@ -2531,6 +2531,10 @@ For example, for updating subscriptions. Qv2ray cannot help you change them back. Please don't blame us if things go wrong. Qv2ray 很遗憾无法帮你改回来。若因此遇到问题,请勿指责吾等。 + + trusted abi + 受信 ABI + Qv2ray::common::QvCommandArgParser From fbf8d071c72ae61a9d56d2c70e9e5b025e05d0ef Mon Sep 17 00:00:00 2001 From: DuckSoft Date: Tue, 12 May 2020 20:53:15 +0800 Subject: [PATCH 081/385] add trusted abi --- src/core/kernel/QvKernelABIChecker.cpp | 10 ++++++++++ src/core/kernel/QvKernelABIChecker.hpp | 4 +++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/core/kernel/QvKernelABIChecker.cpp b/src/core/kernel/QvKernelABIChecker.cpp index d2c57b5f..a6749f80 100644 --- a/src/core/kernel/QvKernelABIChecker.cpp +++ b/src/core/kernel/QvKernelABIChecker.cpp @@ -6,6 +6,7 @@ namespace Qv2ray::core::kernel::abi { QvKernelABICompatibility checkCompatibility(QvKernelABIType hostType, QvKernelABIType targetType) { +#ifndef QV2RAY_TRUSTED_ABI switch (hostType) { case ABI_WIN32: @@ -15,12 +16,19 @@ namespace Qv2ray::core::kernel::abi 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; + case ABI_TRUSTED: return ABI_PERFECT; default: return ABI_MAYBE; } +#else + return ABI_PERFECT; +#endif } std::pair, std::optional> deduceKernelABI(const QString &pathCoreExecutable) { +#ifdef QV2RAY_TRUSTED_ABI + return QvKernelABIType::ABI_TRUSTED; +#else QFile file(pathCoreExecutable); if (!file.exists()) return { std::nullopt, QObject::tr("core executable file %1 does not exist").arg(pathCoreExecutable) }; @@ -55,6 +63,7 @@ namespace Qv2ray::core::kernel::abi return { QvKernelABIType::ABI_MACH_O, std::nullopt }; else return { std::nullopt, QObject::tr("cannot deduce the type of core executable file %1").arg(pathCoreExecutable) }; +#endif } QString abiToString(QvKernelABIType abi) @@ -68,6 +77,7 @@ namespace Qv2ray::core::kernel::abi 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"); + case ABI_TRUSTED: return QObject::tr("trusted abi"); default: return QObject::tr("unknown abi"); } } diff --git a/src/core/kernel/QvKernelABIChecker.hpp b/src/core/kernel/QvKernelABIChecker.hpp index d0346388..5e166f13 100644 --- a/src/core/kernel/QvKernelABIChecker.hpp +++ b/src/core/kernel/QvKernelABIChecker.hpp @@ -19,6 +19,7 @@ namespace Qv2ray::core::kernel ABI_ELF_AARCH64, ABI_ELF_ARM, ABI_ELF_OTHER, + ABI_TRUSTED, }; enum QvKernelABICompatibility @@ -42,7 +43,8 @@ namespace Qv2ray::core::kernel #elif defined(Q_OS_LINUX) && defined(Q_PROCESSOR_ARM_V7) QvKernelABIType::ABI_ELF_ARM; #else - #error "unknown architecture" + QvKernelABIType::ABI_TRUSTED; + #define QV2RAY_TRUSTED_ABI #endif std::pair, std::optional> deduceKernelABI(const QString &pathCoreExecutable); From 7dabdd66e5d197d4ab4591965bb0bc588688d5c2 Mon Sep 17 00:00:00 2001 From: DuckSoft Date: Tue, 12 May 2020 22:57:36 +0800 Subject: [PATCH 082/385] update translations --- translations/en_US.ts | 4 ++++ translations/ja_JP.ts | 4 ++++ translations/ru_RU.ts | 4 ++++ translations/zh_CN.ts | 4 ++++ 4 files changed, 16 insertions(+) diff --git a/translations/en_US.ts b/translations/en_US.ts index 41df5f9f..8a1e1f74 100644 --- a/translations/en_US.ts +++ b/translations/en_US.ts @@ -2137,6 +2137,10 @@ For example, for updating subscriptions. Qv2ray cannot help you change them back. Please don't blame us if things go wrong. + + trusted abi + + Qv2ray::common::QvCommandArgParser diff --git a/translations/ja_JP.ts b/translations/ja_JP.ts index 288224b2..35de90c9 100644 --- a/translations/ja_JP.ts +++ b/translations/ja_JP.ts @@ -2953,6 +2953,10 @@ For example, for updating subscriptions. Qv2ray cannot help you change them back. Please don't blame us if things go wrong. Qv2rayでは元に戻すことはできません。何かあっても私たちのせいにしないでください。 + + trusted abi + 信頼できるABI + Qv2ray::common::QvCommandArgParser diff --git a/translations/ru_RU.ts b/translations/ru_RU.ts index 425a6614..ce25df9f 100644 --- a/translations/ru_RU.ts +++ b/translations/ru_RU.ts @@ -2824,6 +2824,10 @@ For example, for updating subscriptions. Qv2ray cannot help you change them back. Please don't blame us if things go wrong. + + trusted abi + + Qv2ray::common::QvCommandArgParser diff --git a/translations/zh_CN.ts b/translations/zh_CN.ts index c1a9d3cc..dabc3a9f 100644 --- a/translations/zh_CN.ts +++ b/translations/zh_CN.ts @@ -2531,6 +2531,10 @@ For example, for updating subscriptions. Qv2ray cannot help you change them back. Please don't blame us if things go wrong. Qv2ray 很遗憾无法帮你改回来。若因此遇到问题,请勿指责吾等。 + + trusted abi + 受信 ABI + Qv2ray::common::QvCommandArgParser From 5cc9f7293e4eff0cc085f58bad97d4cf9194bb3f Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Tue, 12 May 2020 23:30:43 +0800 Subject: [PATCH 083/385] fix: prevent API fastfail when vCore starts slowly --- src/core/kernel/APIBackend.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/kernel/APIBackend.hpp b/src/core/kernel/APIBackend.hpp index b8929633..9e1a605c 100644 --- a/src/core/kernel/APIBackend.hpp +++ b/src/core/kernel/APIBackend.hpp @@ -9,7 +9,7 @@ #endif // Check 10 times before telling user that API has failed. -constexpr auto QV2RAY_API_CALL_FAILEDCHECK_THRESHOLD = 10; +constexpr auto QV2RAY_API_CALL_FAILEDCHECK_THRESHOLD = 30; namespace Qv2ray::core::kernel { From 04114e06414d8733f50bec758048d76912d3b896 Mon Sep 17 00:00:00 2001 From: DuckSoft Date: Tue, 12 May 2020 23:32:41 +0800 Subject: [PATCH 084/385] Added NTP Client from retgone/qntp (#606) * getting fucked by qt signal system * removing test code * adding license --- CMakeLists.txt | 4 +- src/components/ntp/QvNTPClient.cpp | 216 +++++++++++++++++++++++++++++ src/components/ntp/QvNTPClient.hpp | 165 ++++++++++++++++++++++ src/main.cpp | 2 + 4 files changed, 386 insertions(+), 1 deletion(-) create mode 100644 src/components/ntp/QvNTPClient.cpp create mode 100644 src/components/ntp/QvNTPClient.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d20dc152..99608306 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -180,6 +180,7 @@ set(QV2RAY_SOURCES src/components/speedchart/speedplotview.cpp src/components/speedchart/speedwidget.cpp src/components/darkmode/DarkmodeDetector.cpp + src/components/ntp/QvNTPClient.cpp src/components/update/UpdateChecker.cpp src/core/connection/ConnectionIO.cpp src/core/connection/Generation.cpp @@ -264,13 +265,14 @@ set(QV2RAY_SOURCES src/components/latency/QvTCPing.hpp src/components/plugins/toolbar/QvToolbar.hpp src/components/plugins/QvPluginHost.hpp + src/components/port/QvPortDetector.hpp src/components/proxy/QvProxyConfigurator.hpp + src/components/ntp/QvNTPClient.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/components/port/QvPortDetector.hpp src/core/connection/ConnectionIO.hpp src/core/connection/Generation.hpp src/core/connection/Serialization.hpp diff --git a/src/components/ntp/QvNTPClient.cpp b/src/components/ntp/QvNTPClient.cpp new file mode 100644 index 00000000..9611d4ce --- /dev/null +++ b/src/components/ntp/QvNTPClient.cpp @@ -0,0 +1,216 @@ +/* This file from part of QNtp, a library that implements NTP protocol. + * + * Copyright (C) 2011 Alexander Fokin + * + * QNtp is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * QNtp is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with QNtp. If not, see . */ + +#include "QvNTPClient.hpp" + +#include + +namespace Qv2ray::components::ntp +{ + + NtpTimestamp NtpTimestamp::fromDateTime(const QDateTime &dateTime) + { + qint64 ntpMSecs = dateTime.toMSecsSinceEpoch() - january_1_1900; + + quint32 seconds = ntpMSecs / 1000; + quint32 fraction = 0x100000000ll * (ntpMSecs % 1000) / 1000; + + NtpTimestamp result; + result.seconds = qToBigEndian(seconds); + result.fraction = qToBigEndian(fraction); + return result; + } + + QDateTime NtpTimestamp::toDateTime(const NtpTimestamp &ntpTime) + { + /* Convert to local-endian. */ + quint32 seconds = qFromBigEndian(ntpTime.seconds); + quint32 fraction = qFromBigEndian(ntpTime.fraction); + + /* Convert NTP timestamp to number of milliseconds passed since Jan 1 1900. */ + qint64 ntpMSecs = seconds * 1000ll + fraction * 1000ll / 0x100000000ll; + + /* Construct Qt date time. */ + return QDateTime::fromMSecsSinceEpoch(ntpMSecs + january_1_1900); + } + + NtpReply::NtpReply() : d(new NtpReplyPrivate()) + { + /* We don't use shared null because NtpReplyPrivate isn't a POD type and + * allocation overhead is negligible here. */ + memset(&d->packet, 0, sizeof(d->packet)); + } + + NtpReply::NtpReply(NtpReplyPrivate *dd) : d(dd) + { + Q_ASSERT(dd != NULL); + } + + NtpReply::NtpReply(const NtpReply &other) : d(other.d) + { + } + + NtpReply::~NtpReply() + { + } + + NtpReply &NtpReply::operator=(const NtpReply &other) + { + d = other.d; + + return *this; + } + + NtpLeapIndicator NtpReply::leapIndicator() const + { + return static_cast(d->packet.basic.flags.leapIndicator); + } + + quint8 NtpReply::versionNumber() const + { + return d->packet.basic.flags.versionNumber; + } + + NtpMode NtpReply::mode() const + { + return static_cast(d->packet.basic.flags.mode); + } + + quint8 NtpReply::stratum() const + { + return d->packet.basic.stratum; + } + + qreal NtpReply::pollInterval() const + { + return std::pow(static_cast(2), static_cast(d->packet.basic.poll)); + } + + qreal NtpReply::precision() const + { + return std::pow(static_cast(2), static_cast(d->packet.basic.precision)); + } + + QDateTime NtpReply::referenceTime() const + { + return NtpTimestamp::toDateTime(d->packet.basic.referenceTimestamp); + } + + QDateTime NtpReply::originTime() const + { + return NtpTimestamp::toDateTime(d->packet.basic.originateTimestamp); + } + + QDateTime NtpReply::receiveTime() const + { + return NtpTimestamp::toDateTime(d->packet.basic.receiveTimestamp); + } + + QDateTime NtpReply::transmitTime() const + { + return NtpTimestamp::toDateTime(d->packet.basic.transmitTimestamp); + } + + QDateTime NtpReply::destinationTime() const + { + return d->destinationTime; + } + + qint64 NtpReply::roundTripDelay() const + { + return originTime().msecsTo(destinationTime()) - receiveTime().msecsTo(transmitTime()); + } + + qint64 NtpReply::localClockOffset() const + { + return (originTime().msecsTo(receiveTime()) + destinationTime().msecsTo(transmitTime())) / 2; + } + + bool NtpReply::isNull() const + { + return d->destinationTime.isNull(); + } + + NtpClient::NtpClient(QObject *parent) : QObject(parent) + { + init(QHostAddress::Any, 0); + } + + NtpClient::NtpClient(const QHostAddress &bindAddress, quint16 bindPort, QObject *parent) : QObject(parent) + { + init(bindAddress, bindPort); + } + + void NtpClient::init(const QHostAddress &bindAddress, quint16 bindPort) + { + mSocket = new QUdpSocket(this); + mSocket->bind(bindAddress, bindPort); + + connect(mSocket, SIGNAL(readyRead()), this, SLOT(readPendingDatagrams())); + } + + NtpClient::~NtpClient() + { + return; + } + + bool NtpClient::sendRequest(const QHostAddress &address, quint16 port) + { + if (mSocket->state() != QAbstractSocket::BoundState) + return false; + + /* Initialize the NTP packet. */ + NtpPacket packet; + memset(&packet, 0, sizeof(packet)); + packet.flags.mode = ClientMode; + packet.flags.versionNumber = 4; + packet.transmitTimestamp = NtpTimestamp::fromDateTime(QDateTime::currentDateTimeUtc()); + + /* Send it. */ + if (mSocket->writeDatagram(reinterpret_cast(&packet), sizeof(packet), address, port) < 0) + return false; + + return true; + } + + void NtpClient::readPendingDatagrams() + { + while (mSocket->hasPendingDatagrams()) + { + NtpFullPacket packet; + memset(&packet, 0, sizeof(packet)); + + QHostAddress address; + quint16 port; + + if (mSocket->readDatagram(reinterpret_cast(&packet), sizeof(packet), &address, &port) < (qint64) sizeof(NtpPacket)) + continue; + + QDateTime now = QDateTime::currentDateTime(); + + /* Prepare reply. */ + NtpReplyPrivate *replyPrivate = new NtpReplyPrivate(); + replyPrivate->packet = packet; + replyPrivate->destinationTime = now; + NtpReply reply(replyPrivate); + + /* Notify. */ + Q_EMIT replyReceived(address, port, reply); + } + } + +} // namespace Qv2ray::components::ntp diff --git a/src/components/ntp/QvNTPClient.hpp b/src/components/ntp/QvNTPClient.hpp new file mode 100644 index 00000000..ff8ac5f1 --- /dev/null +++ b/src/components/ntp/QvNTPClient.hpp @@ -0,0 +1,165 @@ +/* This file from part of QNtp, a library that implements NTP protocol. + * + * Copyright (C) 2011 Alexander Fokin + * + * QNtp is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * QNtp is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with QNtp. If not, see . */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace Qv2ray::components::ntp +{ + const qint64 january_1_1900 = -2208988800000ll; + + enum NtpLeapIndicator + { + NoWarning = 0, /**< No warning. */ + LastMinute61Warning = 1, /**< Last minute has 61 seconds. */ + LastMinute59Warning = 2, /**< Last minute has 59 seconds. */ + UnsynchronizedWarning = 3, /**< Alarm condition (clock not synchronized). */ + }; + + enum NtpMode + { + ReservedMode = 0, /**< Reserved. */ + SymmetricActiveMode = 1, /**< Symmetric active. */ + SymmetricPassiveMode = 2, /**< Symmetric passive. */ + ClientMode = 3, /**< Client. */ + ServerMode = 4, /**< Server. */ + BroadcastMode = 5, /**< Broadcast. */ + ControlMode = 6, /**< NTP control message. */ + PrivateMode = 7, /**< Reserved for private use. */ + }; + + enum NtpStratum + { + UnspecifiedStratum = 0, /**< Unspecified or unavailable. */ + PrimaryStratum = 1, /**< Primary reference (e.g. radio-clock). */ + SecondaryStratumFirst = 2, /**< Secondary reference (via NTP or SNTP). */ + SecondaryStratumLast = 15, + UnsynchronizedStratum = 16, /**< Unsynchronized. */ + /* 17-255 are reserved. */ + }; + + struct NtpPacketFlags + { + unsigned char mode : 3; + unsigned char versionNumber : 3; + unsigned char leapIndicator : 2; + }; + +#pragma pack(push, 1) + + struct NtpTimestamp + { + quint32 seconds; + quint32 fraction; + static inline NtpTimestamp fromDateTime(const QDateTime &dateTime); + static inline QDateTime toDateTime(const NtpTimestamp &ntpTime); + }; + + struct NtpPacket + { + NtpPacketFlags flags; + quint8 stratum; + qint8 poll; + qint8 precision; + qint32 rootDelay; + qint32 rootDispersion; + qint8 referenceID[4]; + NtpTimestamp referenceTimestamp; + NtpTimestamp originateTimestamp; + NtpTimestamp receiveTimestamp; + NtpTimestamp transmitTimestamp; + }; + + struct NtpAuthenticationInfo + { + quint32 keyId; + quint8 messageDigest[16]; + }; + + struct NtpFullPacket + { + NtpPacket basic; + NtpAuthenticationInfo auth; + }; + +#pragma pack(pop) + + class NtpReplyPrivate : public QSharedData + { + public: + NtpFullPacket packet; + QDateTime destinationTime; + }; + + class NtpReply + { + public: + NtpReply(); + NtpReply(const NtpReply &other); + ~NtpReply(); + NtpReply &operator=(const NtpReply &other); + NtpLeapIndicator leapIndicator() const; + quint8 versionNumber() const; + NtpMode mode() const; + quint8 stratum() const; + qreal pollInterval() const; + qreal precision() const; + QDateTime referenceTime() const; + QDateTime originTime() const; + QDateTime receiveTime() const; + QDateTime transmitTime() const; + QDateTime destinationTime() const; + qint64 roundTripDelay() const; + qint64 localClockOffset() const; + bool isNull() const; + + protected: + friend class NtpClient; + NtpReply(NtpReplyPrivate *dd); + + private: + QSharedDataPointer d; + }; + + class NtpClient : public QObject + { + Q_OBJECT; + + public: + NtpClient(QObject *parent = NULL); + NtpClient(const QHostAddress &bindAddress, quint16 bindPort, QObject *parent = NULL); + virtual ~NtpClient(); + bool sendRequest(const QHostAddress &address, quint16 port); + + Q_SIGNALS: + void replyReceived(const QHostAddress &address, quint16 port, const NtpReply &reply); + + private Q_SLOTS: + void readPendingDatagrams(); + + private: + void init(const QHostAddress &bindAddress, quint16 bindPort); + + QUdpSocket *mSocket; + }; +} // namespace Qv2ray::components::ntp diff --git a/src/main.cpp b/src/main.cpp index d76b82cc..1012e58c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -196,6 +196,7 @@ int main(int argc, char *argv[]) case CommandLineHelpRequested: std::cout << parser.Parser()->helpText().toStdString() << std::endl; return 0; } } + #ifdef Q_OS_UNIX // Unix OS root user check. @@ -419,6 +420,7 @@ int main(int argc, char *argv[]) // Initialise Connection Handler PluginHost = new QvPluginHost(); ConnectionManager = new QvConfigHandler(); + // Show MainWindow MainWindow w; QObject::connect(&_qApp, &SingleApplication::instanceStarted, [&]() { From 8bfcbd3cb26aa7632862fc6679871a8431ada61f Mon Sep 17 00:00:00 2001 From: DuckSoft Date: Wed, 13 May 2020 14:12:33 +0800 Subject: [PATCH 085/385] fix equality of routing schemes --- src/base/models/QvSettingsObject.hpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/base/models/QvSettingsObject.hpp b/src/base/models/QvSettingsObject.hpp index 3ba65190..9b50711f 100644 --- a/src/base/models/QvSettingsObject.hpp +++ b/src/base/models/QvSettingsObject.hpp @@ -135,9 +135,11 @@ namespace Qv2ray::base::config QList block; QList proxy; Qv2rayRouteConfig_Impl(){}; - friend bool operator==(const Qv2rayRouteConfig_Impl &left, const Qv2rayRouteConfig_Impl &right) + friend bool operator==(const Qv2rayRouteConfig_Impl &lhs, const Qv2rayRouteConfig_Impl &rhs) { - return left.direct == right.direct && left.block == right.block && left.proxy == left.proxy; + return lhs.block == rhs.block && // + lhs.proxy == rhs.proxy && // + lhs.direct == rhs.direct; } Qv2rayRouteConfig_Impl(const QList &_direct, const QList &_block, const QList &_proxy) : direct(_direct), block(_block), proxy(_proxy){}; From 86da8192be56944009d2ef9f9466821ef433ea48 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Wed, 13 May 2020 15:19:32 +0800 Subject: [PATCH 086/385] add: added more logo designs --- assets/icons/designs/Applogo_Bird_b.svg | 237 ++++++++++++++++++ .../icons/designs/Applogo_Bird_colorful.svg | 237 ++++++++++++++++++ makespec/BUILDVERSION | 2 +- 3 files changed, 475 insertions(+), 1 deletion(-) create mode 100644 assets/icons/designs/Applogo_Bird_b.svg create mode 100644 assets/icons/designs/Applogo_Bird_colorful.svg diff --git a/assets/icons/designs/Applogo_Bird_b.svg b/assets/icons/designs/Applogo_Bird_b.svg new file mode 100644 index 00000000..19ef1386 --- /dev/null +++ b/assets/icons/designs/Applogo_Bird_b.svg @@ -0,0 +1,237 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/assets/icons/designs/Applogo_Bird_colorful.svg b/assets/icons/designs/Applogo_Bird_colorful.svg new file mode 100644 index 00000000..51164118 --- /dev/null +++ b/assets/icons/designs/Applogo_Bird_colorful.svg @@ -0,0 +1,237 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index e386d48f..e9e79fa6 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5387 \ No newline at end of file +5388 From 40c12cb53099a677d77674727adc4ed7ba33d4c2 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Thu, 14 May 2020 11:20:48 +0800 Subject: [PATCH 087/385] add: added new logo PNG --- assets/icons/Applogo_bird_b.png | Bin 0 -> 262371 bytes assets/icons/Applogo_bird_colorful.png | Bin 0 -> 285159 bytes assets/icons/Applogo_bird_g.png | Bin 0 -> 252166 bytes makespec/BUILDVERSION | 2 +- 4 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 assets/icons/Applogo_bird_b.png create mode 100644 assets/icons/Applogo_bird_colorful.png create mode 100644 assets/icons/Applogo_bird_g.png diff --git a/assets/icons/Applogo_bird_b.png b/assets/icons/Applogo_bird_b.png new file mode 100644 index 0000000000000000000000000000000000000000..0e266d5c08af3ba5c8c0b44e20d18fa9436911d8 GIT binary patch literal 262371 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%0A#@K+~R$=`J z*<8NNWl_>ySLRqQ&T!~?bbaw)rq#RYr8~?VviT$SA74E2QKr?IIksuKb9*LPDJG8J zq;dT2(S+GMR@7h5EqCw|e|yX3!gK$S!`s(rRNTGU_WSs!_f&(bva{Ety--sr%x%4N z%C0}f+mAtw7A0zsZ41yY-u6Ec#U%CSvZsbn4YCn34DO0RGT$)XEP?z0PXTBQ#pUqp zu~JwCZ#-A%$!Ximd}ZS>4F^-wchd?S5?kI>&rLZx{Pu0Pp+iOd@y);GRK&6N*fKB7 zXS}=fuwK@GksP&GGxDV)J1$*9?jo^Clehh)p{;r((`fet@J% z76OAkYcBLoy-;v!gG0Z#u%z<>Zq1?j2>uf5?=hoPnC4M0E(*`5-I{NSpuTh|lz}Ka zLTnQW^9TV1KFmXWth$DP-tse87cOcs6$p0YZij27WrkAQvPu)n`_wnr$Z{t_X|LK_ z`ol7>Rh6{I4O?AWPkk-Nj83WSd$-A54Z_Q_V?ZWs4eg6iOjWtNWOt>cis?a($d@k` z7@TXpu_WjeP3_XZ@x&c_=NveAY#p5Q1lL9>&Te zWE~W}O$iNwWm)w}(-JqwQ?C8M4|k&pr8clBkg$Ka7VsOExubZuV@R`68JkvlkYU}& zxPIA!Y};Ys)=;=H(a6g;ye?>9Cp}?v#~xui$?i$q9HXDG%eGrRo|BDTwrflfXpvQ% zW?8D(cIe)c)W~-Uv-g@NwF-&-__OR5hioIIyw3`~|;ci+N2x z*Y*eaZPwp{e`QZ88&%7q_Ys=@?(ct(l)r8)gbo%{lg7(^KEVR-_)V*dU6z1_>lpTw z?S}8iK?avomp|uxa~?>B(sxwLz5yC$7Ajjp0n^QguS}r3kA1r;Rs@!XnUEd37H&*^ z_nbO#PuW6Huke7YN0|T7_J&g#G9~fS$I=_>6#6R=BBK^iYZXPhY@p#$cV0T$Z|I=? zgP09Tsk%=WmMpy>Dg2PU{ME4($-oou116fSnme%npo5$14Nu8HH^&?3Yj|3)P8s@7 zESIMAU#XLiGDfi;zMKi3h42Z6355qXkq)UQaQbJ3~}7c zkKXVkOD^YH)&2Cwii2*{H_xchq|v^gI~xn$>0CtbJkBew$gFzQxHN^ScgqXv>^D#uKuLCW$kJpP z6^=vzXeu6GwT2l8{G4J*>b>dnURhM+joaw`tW9eol#n~F16U3)^!&9}{mT&sFUdB9 zV25GBwHXy|)M49F2R>@r{gAAK~`Y`4I0$O1a@@si!e)8*n;Z0bv58}Vumc& z0{WAF)I(Vvzm5pp|5lLOe)^LE#g5HoTEhW3?hOsD;=}oIiY@#!XQR*)wG}rif0{zd zt4NNsDw^_JdQWaht1Su*YT*^^Yw||)f`ziv_)p5-jM@5kjCJhzv|=;a=Z}68aCl@U zd%)vS&&HapPV#$UK7WWowS&NoJlQU;n~Y9CmGm9-2EQ$IZTg?L2+KlTv`fq12z0B;u6Dn7!lK`Fc~7-&b!8ewIUu(B2-qoJYf>CuLP z`zGPNl$NL*XG_VYUAEb&k zz3%_K0)+FA?pdSZ==Ec{OtdC`W|K-R0#?f;+8T(%^D8XN{v%v)rS?yBJ0(oJ{-Bsx zQ)2FA66HBi^2;&-@^XL4IQQ+?e%c=P;8vH;Hk1Fm@TlN-3oVB0#i4;^45c%Dq<*`! zO{(Ihf{7QEN_ql3_B*fX@aWQLd;B^N)^TA)%W<6|-y^onH@DW<$Cft!d7}Bvj5MvE z*9BJ0?J28O3?j43uU*-kv}x5Dmmzc>8|@hh@H7Rsyd#}U;m#CnMM5cd&@(_Hk5y#Lug9yJdbxx_3^W*r(snFZ|l zbB`mAIrg{KP?C<(mcPlCPouk9niSk*--(hx)%=sz11d8uE_jQ}?ln}Wy1(63``{l_ zOA1}pqG4(0dh_U! z-{v3iud;Dwi|SrzwluX~{a``U^~N=k%c(NJn-5tof?SOQyK*kdqyYiriqt093x0de)Lg#wWR#u*nYPre25+xvd8>n`( zvO6=Ix@IuTpML+fUo3{(4e(Sv|NfhSoNskz$SL_6#N@q*4@(~z=zG?FCOY7Ytchq9tS=) z6K6VKB9+7d;%Xo(JG1qhC?#Rz^TX}24%6vo$`e^-BwE>9s_TO_Tly22-eGOp5}4rl%}4ZxzO~Cew>L=FA(XaOj za$sS=E}6x$#;f2&^;8-Xg!9@YvheSLsr{5tAvyt5lbay=NTZg1=Eh7|^>Q4{ZvlaU zx(f5$NQq9{FiV-061Q+vWW5Qox@i`D{+n%-PK289)2zDTP*@cc*fTaMQ$_bn8U^%` zy6py2>*s`YbQjStZSF9n1csUJR{~{U2>KFa!xSR{fuyt(%C!zVx&v*}(gQ++7!HnU z>*^ghrWzG(T8c`is$Wy2;uom)-{U{WQbA^lKKq`zkRf$kxqAhP9wj#{9pMFj`y6P6 z>XYsOu%7cUM-=sfv zG?qS4Hj(+Rtcv`za(q|l`_h(2(+9w*brPqtX4lZr=3ozJ_f2d=9BqU3T^4xaJz@9M zU0X6Bv9G)`j=1xI0n?_y6Bk(3Hekiif1)aal~hWDQ3*{8>u0~sCrw=ppul}lNL&YK z9Vi+f^s7*DsAm74vp0{2a_|4gFUKk8RA}Wymda8oDqAH>rz1%yC5f>Vp|O;GAFYlg zB73Na5Fr%vs~ZzHKXmE&i(m4ejbne{^P#yb3fk8Yk4lO_cih> zp}!7}+at;Iy}HHLcYs>iTvyVbBe(O`c#;!%#jHT1yO$G+ZJ3oJ82L69w|*rSEi2V+ zo;^N)l63krc8|lbk(qau+3}Js;0j6)y0tjX=J+@GT{TcELK(Ulh&p0FO@vWWbfhH- zC`V~)f4r6S8SG{tdj{&Tc!s=vEiEaXMs-`@%U^oSmU9JkkSJB}qnU0u_rOkN4mwTTS*rww@E$5N^%!Wewk zEKE1pyxAYx3v?;f)Uv+7M3qY<-DSNi;LxJhM)31s*RU%E8M!IX4v<5gTY_@`_*v=V z5Xypd+!`oG^FORIgi>B27wM>Ve`P-Ep`|%63&M|7CBRi?+@m+kKBKC+Lq=WX?0n;%5#r^WZZ+#_EM8OaTWPtw1WKXIM zcbI87-F*XLQfX$=ZkWWR9VWgLgwMDZ#~$jScT7{_f`h2h=#H~{N3>UaR|3{|79mB@ zV-%Yex%gRsT$$Nmd)~15Zl@8O2z7#_A5DFdf{&brabU*wDSq2X<9-!#rGR=B!s@_Z z7&oi;7brkTpf)E61iY|-n; z->X+R%7=ZMAdrE#_76;nXct|7H7!L&LPWdB0)p0tj%cp%dnbb#>qF@f(v;iJjY=W_ z5(?OFU19_Pm+4u3dT2ZiTRWff&sL}eCb{OuKsb{{N0~OXM4wg$>7x5IVk4V==l>sX zhPto&_=Xwth~p$k9iQboSl7~?s4p&HlP^IUTSS~vYZqAVUlOFYgKX+eUWy;@PZtDm zDPW?A{(^MP3f*6ubsc;D`MH5!J!7{}JjpKCFoHCgikIw)^3DP^OMvB|E%qX#?2~sM^^9ndr4K@Wep@-c;j6f65V2@{|bq zvdvI{{p2-%9tcbXN+1SfL`TNidHpXVf?(iRw6|>ItH#dBpg4Nk6PkMBmI)b+3mXOaJ6vIZNT3~aS9V8t5yGI0%k4aZD(zC1}$q4 zbnFP$867D=o^Qyp$uoqIXYMUvb~xn>H?(bFKP3La&gKkglgL=m4@@lG1r^@8(f0)o zGghf*0qy{WgXvY@UBdfbB9l>Py_AMcXyQQ(f&wXIC6i6Y>K7ccvG#2i%^5itXAE(? zsM2k18L{U&^LLHNY8#Hu{{o$QZG&1kjZ45UMpe|MF3TRnY4%hAlENbE7T@-W5_a2N zlO@Y!(e{Ax>Kdm@KWdlwfgxqPJ5*9Hwb*E(20oLwT@kMH9~}Y{oN*02;+syxh>MAA ztGR{9bzHXEBc6V9r!BskRPI<#m`~G>8iEfTVsTY3fv9R9+7>TVFu6d;Hor#1z93|f znnK&qd$JLq{=h@!NAug|LS>T>T*B*HQG7v>8Yym~NIT`&QO15z=2=$yme^US)=2$9I03`z5 z2t&*uI+^>Kk6P+3{Vy60V%1EET7aJRMMk z7lrK~r`coO?_5ab{*166fWEG7#Moc){8Rn`;66EJEa6!W>8k6EE zFZbN8B+z>5v0ntY;R1W=J#abkWw)nNOx5?!27z6m!M}i*7O8|d7%eSC@NdoOT+lk_ z&CuGxS9v=f{>`lxj*wRuwX}-8tqoOc=?QgEfGg{#`Hfqsh5bY&!8YGOqDLS;oAh7~ zts}e?87-PC*tZ+GhpKYn+NB54TG9HBw<|W~Hw+;5QuIv}$6Rr0EmH2Sw$3oLtL z7;@Ebp}*(!zk2NJF+vjUum9pxHf@ZSi;n$24J{$|q(he)XTGCacOO)ZTD9M+wXsRP zWi0e4k|iSWmdidE6%NpKcIdZtN67(P;xLjBE%qwd#cUne%jcaQCB#r(0i}to-JN@| zdbd9q?LJGbVu(8MuRb6-;;5~YPFw|P96KiE*KQpu%PXKh13HhvGQtXQta2xTn8Kyk z(n-oRY@P4Zf6({&%RuP?9QUGQGL>|_&JESZ55>fNgaVAMem;CHlF6F35Yh$|B&Ta? zvTUQNbA6Ax5<DY`I@k4J;X-Vh?#7ln?E)yD8=c6hfWZGGt`J<(q+XM2s#3#{y`9rIAlJCVUYS{k z_ItjyxhJrcvS_2!;nVr0kqcKcvMH%2LsyKDmAdWC<2sB%9}D;;!T()gZb&m|9%s$6 zs{NV=Wx})K>?%;1;S=ZH!(`AI+a`P~CU`ta--$!~PLAa+^)5|{Hm$Ombl%;!3I zxZ&E9EOBS#AxMDVqeGNJIae{MnXbZdqJNA<88X&?F`^er$_V&M( zZs_SFcco{%2r&Hq5x{#FlymGcSz6Ub*Q|PQ1x%n`QW`vV@|e#X|9AmP*i4Fx*+>G# zsbNEI3h5MpB2Y}vKqX`7_I!wspfa=jv|&QV0QH3bu9$?38xwAvHm(PZsm1Vk(X3;v zVHf^A=EqAA{|4+T`vozAM(Z~0 zoj=3{4elcfS{*7p_#1)Ah)p)vUY=lHYhK_}1#0c-`B(7kSolh^y6Hb@xPtdH`&;ZV zs;5uBAs1zj@jxg4gO1NVXSH>F#^je_(xyqZVH(QM&0YQ=6CQ}N&)sXU`J=G?;NeLi}7{fYzTUJW2xi8WW<2&VKbTGWOv;}xAR-9#u^+O^S5^{* zb12G0glC^7=AeLs z15s*_IDH6CZ+j32+o-#_09OxC>IU#qiR<`=d_}0*Lzs?QWl7f}&0caWi7&}mO$UAc z+E32cf2tQ7GeVZ*VHVW39#fG)!aXdrqkFP&3sz}@cgE+YA@%Hxk7SqdN$S}n)S{7B zDgEih-=X1qADx*x${5!J=a;(bW|QpWqth6Rxtc-oaAA3v@-vFKK# zsdO{*L|9L|Xdi)OGq^M;gB?|#zqD|ncoPyR!$m+%v(3kq=H@@n|B#u#TsN&srAFKk zPWn4kb#Ga$mCfGm_a`znw5-(zMvt?%qIf{e-@Jj8&E)xpjewjU)b73HAR{#3UPQz2 zwS|x{fnYR|Z=RLsTc>KzayqE;qcSM6z{In4n=_c-h;b}@(*S1eO33wBHq0X{QUEPQ z$KM3Blp>_nT?2m5LT&_QWI9>D8NZ*%elNGpv+$z-x>3&8Cd9;|ov- z-H9g;0R#k%>^u+{2gjN&?P!wn0Yk&9TAj|Uk$j)^tbL6H;@^gb=cz(OB|+=0XL} zeFo*rP&`k6un91s(hp6-Zw4mQnLHQTug29#T?9DdoA{^T^UVIvm#Q;G7l~#<0c@#; z{m5l#rtK;6Angs63r`T{L<}FUENlXHPGM5{Yk_ZlTMXb2R=kWLvQIq zvq%&bDQ0^(1ts_BW%Q881rSJ3D+%es?^jc(_Iqq=5WcV}8e5&I7=bMsQPWm_W+n|; zl(L@|+Wli-+wtLiB9arYrX*p1ETb{s>=Qr@5ie-E;OQtBjdRL4C>3VEQhC7B5S`KlY;%)Zd};8}I{gPkpJjm0|iu31+I3Yw>#K7fkV!_(|= z2~n;|+G!|2e)e-gA-EKE*I6ws;dq-QOh*mr%dA+=9*Uneprs=+{#1)b!f8tWGgYQi z?~paP7sOyPQzD3L!zq4aL z%4-yLy^z`%ZYBqagb){$fA8edtB;~6q22xiA8p2-EgOQ5t=cAJfQMmobGBC{+);f> z6VW@W<4DM3AuxS(CfDa6)Uy^>Vz+prc}?U9Xl;7D(BEMId+$g~&;=C^C{V~#-RE*Z z@|QjHhlBD$sOhTScC3x6viDhjPR05zQAiz;k8Qv;zLpqC+Ee5|jm#amZ$s*pg@56ZCvCvi*~qZPw6erPJ;y+414 zE=%;}R(tr5!Tk*yJJW6EElB$y@%#5dw3QG<2-&z8PYkZ$+FT(GgEf+1o*P`E8P&Ba zcZ4D(DB!>ZrXgC%>^#B7Lqp@LxxAh3jA8XTi4wETn2=wf75@Rl9|p*QX((HZUdAv= zmL9}ye)b#WcwKs!xv&>``L1~^y$c*H=;tER>O;zM=IyWL&ib2MdzYBZdqJsBg=dO{*yE8pUG0AIA&umtc3wmW9p zap1!h776XM_Dxi`7wrf>hOH1nVIP3a{Hop5yzRp03xmxUkjtn!iJmAjZwNwmkmy}& zEf&>*19}I;ei?&*R*oIh2VdJX5vZe+R?VolQCIu~oy6fvt}I>&Q;nu3(Nxur|1YkM z(gBaSlokRX*uCmlRCm#{YLm+mDaqiW-y4EG4>4kEfPTX^$KvvT9pr;r^CwbCN{z7h zpzeYxXH6Q8%hGwv8>9C;zgEqw%65f&hd`g*r2Hh0+bB*2$|V~6A6gz98@~BzAq^oI zN!0B(H3;D-_jXkcVUBi<{gjVdlhmCdadlbmE0ySCUO781xpxk7Xo+zptq~gZZTuRJR z=@NS+&9*_VHLx@-S?M>MBo&uN;_^gTK=udEP*CT*GHV0X_l-DpUHvYKbo2?TW4*Qp zim=!=jEkxP z4Hzzdhy^8%qz+V+!tDbDgQ~_J#;|2ItDBbx=w$y?_|sQHRx`Nl%tmeq?4)id*vVKt zoOW633M+}(R0E!SDk2kY9Yx$L#c`lzB#YzXk0qlCq8JCcdZa4l|GX-FW~kRJhzIOp zjxBM!t1a&$bm9P|GW1pr;qnA}qBQjcdZIvHT!X58DBLp$3JZ3V_j~({|Ge8k5V^KB za=BB8dx&(T{Q_yIWz4epby*X_dqd%<2p7LT{DQDxuQFS4mZQr0`m>p8}TvRzp{(uam{*U zgLksSv*p)P2m3xAg&?5|uY2GJcvzjtl={l3i@3qq}H!irrT)rA@%wF5^{-93Csr&`3L`h2{P_Rqe)7_OzDyFXg`HhCq_?;od0Dd^sauo z&b|QATona3Ov(|J_~Kx4g(UO-vYND9NKTP}OXPeYx3uURClZ$M!=^Nb3#^x02VK6H zEmt+yUDnd#1J4lD7DAR^V~XESoG?N@x(r}adAR@C^&o84v4xl-zuaUc82L&{#t?ke z3i+tR5PZ-|*&h}us^mG?M6a7srRLBGgJWN2wjCA2cy=z%^Rg_hX5NMPmoRgY*k1z; zuZe^EN2Y9)03qt7#6{SbVk!8uB(2mShhUwh*Es*7naw`CmPZ}rm+r|uDt+zmjebnu zltHg?C48EUrub{4!=ntqKKLvp+9tJ^-bB`eH%0VwJ_nc8Nn4#dY9{c%jNHmF4V{<^ z23}vsziP8+S>r(aN~sQc3ZzMwDs~thxVJGN*K{XIChU9Gce=z`*B;ZCNH?gjbAI6UhP zLalkeJQkb16f9P}c+~yNy8ll9TU7>v-t7m8i|bUEO3$xJL!RHR;Df3dh!=NE1p0h^ z=tH-H|1Txx;(0tDNs)Nht_(M69 zwye;!@}xFEivqD|yU~p(gick& zl~z78F~IUe+51a&5G%WwofVPK^oK(VBE31&T%SzV`2exch|M4;Rxq0edB)5O3&oLbXxx8)hx-K%plo3YdVIKEikVozQ@<{2vG5JQ2>*xZ}8`)hZSB+LC$QRi9ydrC@mYH&e3A#z!!-*Rr9j}eF^yWWxdI^398;e$xcSrhP*Iga!`H~0@I zEQ3Y>9|*cKw2(2Kd1S$~8#6>*6i1{Q(yUpVGOS;7zlotvPmkAWW{{l0@a`IoqSJq9 zZ*9##2Inv@?FUxLk=X&mk_>L7DF7bog#c5wv3MbWIU}0K+V5L=E5!I;X4bqv(}dgv z6q1!NzrjdJd9v`sq;ey(U6gN%C<#--6Hb<>K_xFGMf2~q?N!LCuBI1l&;S(0<^6 ze|hV&V1?HaV$0kLLfV#AwE5K3={pFnbOBZtO z3$5%GBq%4Y6h`g3`&2x{zcpL8`CMz(*g1zi7JvTv=2q)1vB(^Y*4E#n4kwFUmf+5{ z+I%yIyDY|Y-OBYk^1pPnMzx1q>l8do6zih;CowOjoI|Ikkr&1%apZ&KVsrS9Aw5iz zOQK6@++>MgA~|)PcO zaH|{HCdyus8i4#m(18f4TA+J}H8K5JSBF2Qm*1itd)acAl(H7*_;e8y+{*l>Ixl5luzt_}b`|khUydJ_|hT?fe%y^{9iABN;|txLzLkRa>*0xtc88zrru>c=a1f-|_>Kl5!a!Jb zL^OOZYv-j~JN!TXfsgDfl&^4n%JoLznui*kcXC(HBjt>YDfHdel27W3LQdABG{M8< zkhg+xlA`-l@FU*l-rl&ou+0{gKRbnl0dDS&Eqd7$qT4@V^@I@Z{qV+0$nIg0nD@D6 zd5dvp)&7=jY5QH{2o*0@}#r#>!gC%3w&d2>e=yOMSs~< z(i5ppp5yJ(BOQ{w^8EZ7ru?|E!ji`8a-MVX zOj;#s^q9UN%_DVsaR5GdW{cJ|Mks$QRP^lZn)CQab*~j=dKcG~WK>$RpJOx=io4tCeq0Et&yd!**CgcLLm(aot5x0zk%N8hJrtI!B$ zK3li&LJiOTNGJd9gKrFYzt2MT`Y4SF2__PE*g|`cZk`cZV2wA9%@mC`=t?mumvk1U zK0Rd@{v2IwaXr-)Z$xl8{8?=|F~U)@K-RA05f_v1lBSjAH|KYV`gN=>4fi>WY$BjQ z;TrhZYCzM_{Y7Yc3ck56kp0*M{J?O-K6F|F(M zdRyBv=AyG5Sqr4`(#&f(efE7R{h}DXLTE8l67@n)5I>?KIbN%(2Fd5`YScm%8P<{J zpA1gsCF6M59pkzA2J(6E*`J9#*3;f9v1o-vC)i|9Kd;x+l_0OQWP@%hQIf$}q&(;*Sd1wzg2%$`ud8 zcX_kWN3vHM<;(ci-9h!gofTTa3tis9DN!Hr=0>O@yS*)Sq9wIadH2~X3P?CKP;NB5 zsEKbGyC^nl;DOpDX1Z-wM=~MF#}f+oKG854L(k$*AkfW9+KzqM#+eN^#=u~_joaSE zEsJWWy3rOo4qubJUFEi+0ycT4cY&c^_OW_k7mIWZTtcucYjW#C)2y6rNZp;UHOlrm zmjTuFI!4)h2&1dFuVZKjOaCUb;QmVJc2!$X#^u|S#LpBHPsi<#7cqC7{rF;~$LuRw z^R%p&LY`0rlP=^|I`gkYt|FkcWK@(?vNYdp6vESPlU*XaC&n%bFB>se+}Y3lIkQrd zQ10h>l~#n)ugXnyXK(v#YEa&8@P6U#RMjFgyd-OqatEnXDsREpd+&Aq#T~E*34Q%e zi3@1x^U=;0Vd$0|OC3G-R|Tv>n41HcQg;AkN_rB8U6#696-0H^Qd=<>xn|Ce2YPWi z3m-A8!{mRQ_cg)4rP{YD${xm^{tQ{%**~B-#_UM*q`PURlD53|_qVPVtybl@u9th? zcAS&_gZ~bFrCMtTPT*6%{*aI4c#WzOq_UtH&CGTyn(h^LXIf8`A^V0t5Z`$T00r6d z>N}V}0=_^Z^43F-pEwavf)cX4(0a4O_VrVF89SkmwDIS+YrK-vhs@uUCZ1hf3E70` za0ev(HqD6G=Wbr7K2f)EiE^-l)q4Gt=9%Vfv*L@d>@&PrnW$QFN8F}T^P^a&UR+2( zAH4a){F+4MBD(UVg}Gjgpk#<0Ah^NY7)S{|l6#f;XmY)z+LX?qfAt&JAdjMxsU_h< zE}{Hy3YfP)Ic;8Ca>GlY0rbP zrWJ6<-y&Nb>nmW@cZuGt6E%|=vI26zV#R}&1z{LmAQ1= zsnbG#K$I`S4_~@EDKaUnrstN+m>tyr|8HU_@NL$*rX|E9S}A zvryB?%`R&XlY`{aTe zdXxOSii!1jn>5IfE|;Z3bC`?ny&NiQo4o=VgG!>PLl4b&(JEw7QX}iNNn7n=ygVU2 zkERqAl>bLBQYVv6W&WKtD7qH8Pw@h&+E>7~mMP!fl_r7eBF_H_&UviE$!Fa^!pm6P zu=SC0+!fY}F^cwWC+KR+YXzt^XlYKs>w4S2YH|NwR>9%oy`|N>WDz9T^@A}Y#0Rv?A*O&2Bm-hoOzdabkWFqoa}=U zg9LlNsiz>p#kE;6c2g#fMT5O)52o=%J+gM_;*~yA(6saRAKvd@Xa&CrMc+PXx+P`N zA@!FZo8AQ(R?>d=FM(A?oljv0R_Y=~`T~aaAZhp1wof)&?^0@?s);Eiu8L_!q8a ztpzDEebu&LC3NKOop@U)N7EF0$h-m|$)T?a@>}EMj1`-mg+T*9gYsDb zT$x(0$W$q*hWR|2loi@^i4ubb@yX!; z_ZW(QEZ>Wzc3pEEt5eH`?XEy!MjLE^Q5q9l&JsB~TQ@$p-;z&|yfl>cVKqxCNF^`u z)nlAqJ0xBu)YSH;( z-7n3FhO=WvDnd}o=|z9ulG58`r5n1$=;G7|6#1&dmNuESs`O2<{?joOkx3@RGePf@bv-)y7Is2o8svOwOhb&;FMJh2SElVqs7?tJaG^tVIB z^=D`0EX?(H`Em#3y?@5_M(>s_|(~rC9G|$n18&UuaGiSw(XtV*>QZz}y)7qtxi+1T>fq^got<&Rt0i!vG2qpgjquT7>mk^`J&AvI{34Ez zBw#QVy~`0m6q_KFHHSg{w%h2z+0TBe=}(0qiN8Tpl!)$%1@-umq$5!qv~@Fiw@c}i z-9D#7wA(6AWkCQM-o!(=kjHW4()QDgB)zYQ_pPrQ@x~e)LOjxIRDD`?)iIrJ!BO^) zmUE;BCtFluzCXAAtwj(05b*|Xq1qRlQi7{oxp9;$f-jaKw3Z&kNlE1#Be**b&khfA2qM_^XoUnAwn+Z%E<(dB*C**<-!IaGj9K z>1nuDPPGK|PWk&KcBavXqKh8b9p3LH9a2dPX6olB7FzUY9J@8dr+ATY;>4@c0VUpl z=gbjM6nH0%BS2s#sV*EXIvSPV1%MOyi+Qu6aOvZn^7>N8v1o13=>5Xwju(kjTLb=l zjfbPJ^E!&U&U*Mxuk_irOl+C9LytgBmY*f7sUId1WE*EwY18Z&3%37fjBy7%(p;`) zKVp_DCZiv8d5`3X+pMmtfwGT(f&Q^u!z`2eSysbw`|}|j)_o(Kc4jIOr2Y!BV))*g z^a~o?0SZFhd1)^uF_Dx6Ellgg2YDg(E^;>b@Y90tXJwUK+1qpjKPhi_q(M#F$0d~a zx3OC(>S1l;ECx*_icoidh|?7}+P zr;Q*Z&Le&0x(MCSz`D*a|0jI~Y^-rAQMFtM^E84rI4wy}ZUcB3`AeDlAk3_gcF*#Y z4&e&OTe_3NPkY0^TRn$<$vbe9=YBRkX>a*tEC>Lj-Ne))>2({SbE0mLwb0S|GEWiu zEu11H1lg|xq}HoXYTy46QNLgGNN5T<+5aG2SQ}+iA^7eTBk4TS)~} zc_=#G)okJ=7uVr|TvC*4=XR23|^pTrf`5CwAH@l4~cLN9XI-BF zia|O$2KvQkp(I{T-&uQV>nrJvVtw%IR^dwW>MqC&gf-IF%QH;sj3mHnP|o zRC}QF9Kb%fMYGRlYgfIhDiurjQAN#x-j&m0uj=O2x3R-#ZP#WY?&4!JglHf8H}_n_ zF%9-tdxmXU+@XXD(B(Cd|4{(>N?$R^--ByJ$5`<=*`&EiDs8+_rQyO)v8^M87y753 zQ@0*ZNpZ9S`K#0Qx+YZNs9x zynk!`5UBF&*&2>$f$gGk^TwNlyC3eduNBr+gnwC>PkE~h9YBM89eWp>h$&Vv?ok&- zM5rv|kNLYPEwh^Y8-5D|sQmfru#VqA#AJ$8z#|)rZ2(s)^9r$22}#cZyySRtuWCo* zQuc|W>Xam7<6NyJoCJI2&#P<j&c<+N`@YjAA-&q{v8&N4%1r{Tz@;yOdhygd2$p z#CFZw;I!C1a1Xlg6Z`B#7fmG9_g0g5U8)Y8>9nta4gNM&KFuI#j-^z<8;T#D%=>viWqRjP9+H7z@j5vie`93n%PmRgQeS1tPeG z(+u;C>^|_%OArc~BqkC)D|9Ej^E1pB-}5f~MF7W#{3#Yq6-2InuV%U>X57WD%rw}s z=&y#opcRPv)<5g`#PH)c$CJxA_&1 z3l%)VNTQ4Fegj>sTC2gVfnUxc)000C`%2~w#32pls`oAB!5lHs1Vmk)c9H9;Co1Yh*IB+DG4 zuJED?X-9t?bHq7}j}R3t%9e$1t@^~Y|Y7R|1&ww`shK;20OUwWB5 zXZF~$9v{BVEo|ED=6*X?Hrf@J;ur32BPx`mNQ~I;cdA>&cd<+r zHlEyDx}M$Kyqn&&f#&AC`S36tkAn#&%mgyylIhhl(yQEmwzql^Y@!^6$H6X(>&?b% z`046xaKs8&YBCkiS8#c~m+np%$4ur`> zHDkNI<_5i!y6uyj%Gvh;@v01z2e;_?(yLzW-P=KKxPcSsat%sBP;0zrtF_p}AKKcf zn^Ls|a2qx22(CLYJtyS?!DY1Eg`e%kCAO8H|7bY*>c^IR2Z=qU^Y@JNZG|Oi~#F z(gE*eTM9+xh*_7Lr#O@G!LsX9VI2D|0*o&l7{DiP$tv|m#M(WtU+u6_2N z_*Xe;dCSfWkS2a6SEdxZKLCx;vsU=__fnfuP={OHMn-&*V!De8eG%DGg~UwUdeELN z>P}>gd12jgi8g4XMNqEZgPyTU2>NvDPVt=*483W}U$?ZhD4SPDbj?;A4;YH;UAXFwiem}+_Vzt2#R$+`5$SAP4a%UX5#LDyBG&EUAP&HSk9EY zJ7GD90&0D-+Q|fGF;-r2z3r(E{ZoEVs*WVDb0}}kL8%c(5B1l+tclR!8l?}a;>gDT z&JL0XgndB4^E2jMCtRM6@o!guYZ<(yvFaQ{j-%dwqekMpjmrK>jRZWZwNj^t+it)j zl}@iA$xRVe=o6sJ-9(C(O^LwC2OU=?Not!)ZOXH+^?HQ>pGN-SR;(rX&lK6IQtLtw zs-2Sym1`55-^+pE0TO23d4#KlStZ^G1Q*cldTM2K;*c`6_47UJ=&r4e^k~os1(%7@ zX~k=NE1`fFe00g9v*l^5X&2Ty+2SxaA$DxJZQzA~IE&P*4ezru*jxB{U_}*26g^Ny zjmf$7i>_iNZFpRXKGJZfWq={zsLWWy@l_=36Jp_1=P`|G82tL~r+vVg zx#C$Tg(CUSdSg$*jN$MuB8B6aLF@nB3C}hFQglj=ohZGwu05-r{8-V;D#$jwzAc~$ z;efZquTx~vpTzDYIsfV;tKK>bocw1eaoo%c6{W3aR&GC2czLFYX8)558Mt!U@TUs& z9V&Weeq6oKF;!(HH*+pR-6~D-H%>R-QTBE#X@0DJq{%d zBq<1QIj}E=F#l50XBUUri5E)Mz6G_b19Xye)!9Bnu===IPmbn&J1ZGh^iN%;hp$y? zk9_Z3GR<4qglK%^2K=B#3BxP+I3>4Kzsf-fbn`NQloofup9A)n4gZlLYA%S-2DqIz zQQ5&vMc(h~Zst2cjC>o8BM}eSrgN`@jNm<?US?C1s?OJJBeEIx+M;0WYc7(NI?f@W2RLcUUO6C~pkUuzF6y>I%7 znp5ay=aFubmln))%k5E5yR-U~!M>p4RA4G7wC08H@@dILW!tp1|96hqJL7i|bz4T# zjIs;0Y}Rv-uEibM*B_IVO^r>a^o-oOOWvCr9ru5h5-=u8L!stt%R&QRQrbc1V6JR=MiF^aU)(76YS?QRL^cE z(B-kOE(eg+O%#Yv<7Fcis#KL8Rv^YD{WZmrF~dvt-AeUD?oT{lsuncVwPjPssbMKj zs^em${~1^u`*N)hFqB`tw>iokP%YaL^32f+(bVJ8h4rt7nEs=OJY6L0z$IG$j}}66 zra#Abd)Ysx+E$;-1H<3!h24zq8j*{^Tp8~}G|vkPZo%OtdJVIn`f3hKz$tA#Z-UAg zS>MTpI~A@;fztKcoxsY%s<9<9qu=D}R?2^&lY(wY+!rw1OVYbo(M=()xt}qvxjm!^ zmd7hCPI4?mLJPzdF#Zo$z^^+7*fom9HWArQ_lo^br+SD+`k&EJQlp!5-q}2Fs`Q(9 zI=DeF#$?t=pzxMimhSNJq8=i7$yuTSzqX-?4 z1i<#I<+SRfvBJMR0j44}d@Qv-ZU7DJje)KO?7Bbi{TY-I^OxC~-@}MksaMT8QuOSP zQuF}_AgLc()}qUO3a;nXnE|IQZ4vHoT$4Ic3P*xK_?NJix}aT z+u!XeS|kLvo~a`|1|X$iNX)smk^ch|c*oe-_vi*s1!mXUQQpJPek(CC2f*;yQ1P82 z4E-IaXmN1-^1uax;}t_pla!bH`gH!HI2YT`Z78Q5l2FyRlQaCJW+f2cRQ$@pC7%H3f6^Wd5h& zlqdvwO1q&`9YI79gsCpnK%ZIB!c$MrOFbZ-{bfM0Q?)Z15jiu+#c@A~oO&iesnxsF z$$?ji4nXKi_Gum&*TUxMe_j1LKHAic`q515B7?K`}m6)HtHmoz-ngcJE!NUiy3eBfUpWy4yXwu#{pWhMHg zyEyvvj?)k4=Oqj6fD`Z>NaJHfx8}p-W+0K-ZTwc;UGMa{!E^7U4;))Qbv_#Ai zUk{NyiN3Zj`+JtCf>IwiGYDH-0inEgfco6&lBCZKo->0*D_m0p*>HS_&x5M%?HARvnO|9n2=zP!p5$@Q>BpMo z!iH!5h|0T~@#DPC!`BnuMLp(1*w~`!5sXdL+Y&oD{Hbjc0O9W9nh}}+6CrHJQr_%G z$1*KT&P!LCV_5=NXuwc^P^r6Av8fSzrTYHhm*cOIEp`~Z@seMV=W?*@o)hl*1K286 zS&4dWmUdp*_55a<$+wIMC{Vn1jI)JMILk`?fuv`))4(c&rdS!J3ix+9H85N8>KsGe z+Wsy)TXBD_vhOrOV)Ei9M$)dP_yrBG^ope=(5d&66`PnH(rhR@d*v8_A77Dd&d5?Y zQNh`-Ww|+kNu@pfALp>4rKXdO2o~bldQevD=ZzM?JQ}*-+Gu@cp#P&l%$#oY#N2hU z^JeYLz+C$@3#6$l+*6JPu6JwaIylgo;4yUF#oq@^gS==2t`ML`f~VG)>sp?#efzy> zbdehZC`sOJyIrvmwg;1Z)v5L7Q2N=8u(W_Uws)ep^w%nfUz-p2H{b7rT~~GI0SMkx ze9XCBD1zv^e?VytRH!@D-4$@-wUNI~;n7xp3LoD#wp0cUOe0!&CSEI=e`UX>hly{| z&Nt7(SK=z~i1<`JGx<@D`g~uiwi@7z4`jc(&fnp<6mpG%BRKAi=V5MfuR$|Nl5~#* z>eITyxV{|IiZfqbNXJ_%0sleHTzKJ6=X2|Uupf00`(%Qo_R?HZ@&Y0d{5#q?67p_d z$bF1}qmb{T(1l;hCX^B_HseBWX_4K$Cch_J^GxB6wx=noVAUfrBWl(B?p+fmQP@K4 zkSOw%_kbcrmvhTuE*3ebUaMe>2G-CrPrY4^H5GHzn0&!r|}%3Qj4 zfd9_B431MUlgVVCdduohmR3KWx}j=+^SytcI%3(C`$YA@W`1vVVMc2>Q+wh%l5r5! zgA?GRWmghgvj5=AA09QEbPSe4ZME~5cxh>(cf9Ch7yl!8V1k67)Uw~hp$tuFL3zMO z{!8AY6=OPM{>b>vJ)SM`Xz1z*#33p6dhxDAsU8>Eo!J|Kg1kCkg4fe&DbhtqhN`Q5ZM8-cxz zT$OAQ_GvHM`su1;a4SKr$2gXJFmMtj{*KS(dIQe<_-MElMa$Q?!7DFFIbugzbqgeZy#6X+$_0PpXK6{+23+Lg(}oq6XWmU%v&x8`G63lG z9CT{(m?nfio33igO5Dw$*7#>X!(KoHQ?npYV)POLuU>ErR-Q{bhh$}tK$*0rwFSN(re_@ z#tk=VCSoSJY0m7KS`Dwb1M)zL`D<3|ePL0DTQ#@!;hxrS&d=%{cd)4%yKSbwo1e>X zTnTk27)+6LJrdU6_K7e%?YRBCIbt~n6yL!I($^yv`>d~#3SzN))`2n18?!>ycIcC4 zjZeUA2e3cEB1A9$e?M^quOZe$bT(j)jr|p5K zmkr{&0AqO1nHG4wM)y%Hh@Gdja+ba+t90P{ei7$Si0-mOh%?vwlVPU{*$gg?8?QSY zpd7O)!h&lV?2f51tiR4W<5qWIWLQ0Cc; zEqR)Gf;+&z^h$#qc1tsyjJa6(bqlKAf&8+q9(~O>U+yVtqYPen$N*sHp^xon!NEhr zF8)`&?CKtHV9{EW4dlxZ^xH^|`Ce(v@pM?#F^fwh421qS=Ma%rf#s|U`jSUD4X@KR znzsV((p$P<V3FdX_*c1H^<^>Dc zk^lI+$X|~3O{yLFh^@I1(Yt;U?N1oWvHml|;eH3G8c0HdSrHY%?gHns*1%vk*^)$U zACbgWHpduh6M)Yk2&z0SR??(~qu1?IxaJOO{PUsv_ehe3Nhbxw8@K*YZGJGCe>4kg zH#8KRDYpgG%{WpHKy;(&mNqF zpv_xmdT)1)S|@ZEN}&d4GF@kMvH^~~Ak4^gyxDQ50U2YUz#moF;+~h{mmMrptC*n? zD{OxnFrs}0ozNSVbWq)=2v=sZ3FxGQ!)q>nI{sXlylGJJu8q(o@dux9>>`f{@;3N( z(B^(_j{pPsKx0?`b=Kqtbt_DS&koMPSB>?ndDKT6$!+GhuSW%^I-btetlub}UoORL zY0vS2lXb|xYMglaWVV591H$~H-vN(zgl!|wnC5t@jNXfXgv^50E`4{_YJ@wQLJ-ErtrVHvp*g=;SaQPmn}E*(}!@?@Gn5@ z?@QCFYH;i7b8>3WcD`v2cOMirxqYZw-A?JiRmLM)N9huC!HT0*$An1L)&W4%#^{|0 z(}7=8SKAyKjzJve(+Zvc%hb^^z7$FMpYhgQKN{kt=@s%8$n6G0ooq^* z82Q&U<}|y%f=O>M9ZlTv%?l|tw}VbS+7#XGR~y$ozi2WRzf32ffS5$PRiAmsggi)2 z*_z6zU3OtU_M3jxs3}-`1WWEcs{067K;pSH9tP&xtgsK^DjV25QL2Y5f&qrF+MpZ+ z+Unfe!uO!8Yxf(TDU@={lnQnJTkXRE@3j-UUYfWN3qNEG?>pGuoQL{xA|WknpO
IY5WH(BkiIS4IV?HMv|ox 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%0DhL=4!A1AC*2}<8^Qi3ueLzH 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 z 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 FN=OUf}8D*@xn_4pJ8u46sh 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 z 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 OH?FBEq+K!j<9`9Rvt9cD literal 0 HcmV?d00001 diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index e9e79fa6..2f055f06 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5388 +5389 From 7c1d9b05eb2c8173136a444ee4d60eac9dfb29f5 Mon Sep 17 00:00:00 2001 From: DuckSoft Date: Thu, 14 May 2020 14:15:20 +0800 Subject: [PATCH 088/385] fixing a return typo --- src/core/kernel/QvKernelABIChecker.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/kernel/QvKernelABIChecker.cpp b/src/core/kernel/QvKernelABIChecker.cpp index a6749f80..7f05bdb4 100644 --- a/src/core/kernel/QvKernelABIChecker.cpp +++ b/src/core/kernel/QvKernelABIChecker.cpp @@ -27,7 +27,7 @@ namespace Qv2ray::core::kernel::abi std::pair, std::optional> deduceKernelABI(const QString &pathCoreExecutable) { #ifdef QV2RAY_TRUSTED_ABI - return QvKernelABIType::ABI_TRUSTED; + return { QvKernelABIType::ABI_TRUSTED, std::nullopt }; #else QFile file(pathCoreExecutable); if (!file.exists()) From 21c5e9a6750b143fd337901ba9237cd36e78f0ab Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Thu, 14 May 2020 22:15:20 +0800 Subject: [PATCH 089/385] update: fixed build && misc updates --- makespec/BUILDVERSION | 2 +- src/base/Qv2rayBase.hpp | 2 +- src/base/models/QvConfigIdentifier.hpp | 3 +++ src/common/HTTPRequestHelper.cpp | 8 ++++++++ src/core/handler/ConfigHandler.cpp | 27 +++++++++++++++++++++++-- src/core/handler/ConfigHandler.hpp | 2 +- src/ui/w_GroupManager.cpp | 2 +- src/ui/w_MainWindow.cpp | 2 +- src/ui/widgets/ConnectionInfoWidget.cpp | 2 +- 9 files changed, 42 insertions(+), 8 deletions(-) diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 2f055f06..a6bfcd0b 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5389 +5390 \ No newline at end of file diff --git a/src/base/Qv2rayBase.hpp b/src/base/Qv2rayBase.hpp index 770029f1..e9652f65 100644 --- a/src/base/Qv2rayBase.hpp +++ b/src/base/Qv2rayBase.hpp @@ -43,7 +43,7 @@ using namespace Qv2ray::base::objects::transfer; #define QV2RAY_CONFIG_DIR (Qv2ray::Qv2rayConfigPath) #define QV2RAY_CONFIG_FILE (QV2RAY_CONFIG_DIR + "Qv2ray.conf") // -#define QV2RAY_GROUP_DIR (QV2RAY_CONFIG_DIR + "groups/") +#define QV2RAY_ROUTING_DIR (QV2RAY_CONFIG_DIR + "rounting/") #define QV2RAY_CONNECTIONS_DIR (QV2RAY_CONFIG_DIR + "connections/") // #define QV2RAY_PLUGIN_SETTINGS_DIR (QV2RAY_CONFIG_DIR + "plugin_settings/") diff --git a/src/base/models/QvConfigIdentifier.hpp b/src/base/models/QvConfigIdentifier.hpp index 5383216b..f9123474 100644 --- a/src/base/models/QvConfigIdentifier.hpp +++ b/src/base/models/QvConfigIdentifier.hpp @@ -42,11 +42,14 @@ namespace Qv2ray::base // Define several safetypes to prevent misuse of QString. class __QvGroup; class __QvConnection; + class __QvRoute; typedef IDType<__QvGroup> GroupId; typedef IDType<__QvConnection> ConnectionId; + typedef IDType<__QvRoute> RoutingId; // inline const static auto NullConnectionId = ConnectionId("null"); inline const static auto NullGroupId = GroupId("null"); + inline const static auto NullRoutingId = RoutingId("null"); // class ConnectionGroupPair { diff --git a/src/common/HTTPRequestHelper.cpp b/src/common/HTTPRequestHelper.cpp index 8c8648bc..c814675c 100644 --- a/src/common/HTTPRequestHelper.cpp +++ b/src/common/HTTPRequestHelper.cpp @@ -56,7 +56,11 @@ namespace Qv2ray::common } request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy); +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) request.setAttribute(QNetworkRequest::Http2AllowedAttribute, true); +#else + request.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, true); +#endif auto ua = GlobalConfig.networkConfig.userAgent; ua.replace("$VERSION", QV2RAY_VERSION_STRING); request.setHeader(QNetworkRequest::KnownHeaders::UserAgentHeader, ua); @@ -88,7 +92,11 @@ namespace Qv2ray::common void QvHttpRequestHelper::onRequestFinished_p() { +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) if (reply->attribute(QNetworkRequest::Http2WasUsedAttribute).toBool()) +#else + if (reply->attribute(QNetworkRequest::HTTP2WasUsedAttribute).toBool()) +#endif { DEBUG(MODULE_NETWORK, "HTTP/2 was used.") } diff --git a/src/core/handler/ConfigHandler.cpp b/src/core/handler/ConfigHandler.cpp index c0cdc865..89ef9398 100644 --- a/src/core/handler/ConfigHandler.cpp +++ b/src/core/handler/ConfigHandler.cpp @@ -22,7 +22,6 @@ namespace Qv2ray::core::handlers { const auto groupObject = GroupObject::fromJson(groupJson.value(groupId).toObject()); groups.insert(GroupId{ groupId }, groupObject); - // for (const auto &connId : groupObject.connections) { connections[connId].__qvConnectionRefCount++; @@ -216,9 +215,33 @@ namespace Qv2ray::core::handlers return {}; } - const std::optional QvConfigHandler::DeleteConnectionFromGroup(const ConnectionId &id, const GroupId &gid) + const std::optional QvConfigHandler::RemoveConnectionFromGroup(const ConnectionId &id, const GroupId &gid) { CheckConnectionExistance(id); + if (groups[gid].connections.contains(id)) + { + auto removedEntries = groups[gid].connections.removeAll(id); + if (removedEntries > 0) + { + LOG(MODULE_CONNECTION, "Found same connection occured multiple times in a group.") + } + // Decrease reference count. + connections[id].__qvConnectionRefCount--; + } + // + if (connections[id].__qvConnectionRefCount == 0) + { + LOG(MODULE_CONNECTION, "Removing a connection") + connectionRootCache.remove(id); + } + + if (GlobalConfig.autoStartId == ConnectionGroupPair{ id, gid }) + { + } + + PluginHost->Send_ConnectionEvent({ Events::ConnectionEntry::RemovedFromGroup, GetDisplayName(id), "" }); + emit OnConnectionRemovedFromGroup({ id, gid }); + // auto groupId = connections[id].groupId; // QFile connectionFile((groups[groupId].isSubscription ? QV2RAY_SUBSCRIPTION_DIR : QV2RAY_CONNECTIONS_DIR) + groupId.toString() + "/" + // id.toString() + QV2RAY_CONFIG_FILE_EXTENSION); diff --git a/src/core/handler/ConfigHandler.hpp b/src/core/handler/ConfigHandler.hpp index 8cb3f6b7..d4fb0280 100644 --- a/src/core/handler/ConfigHandler.hpp +++ b/src/core/handler/ConfigHandler.hpp @@ -97,7 +97,7 @@ namespace Qv2ray::core::handlers const std::optional RenameConnection(const ConnectionId &id, const QString &newName); // // Connection - Group binding - const std::optional DeleteConnectionFromGroup(const ConnectionId &id, const GroupId &gid); + const std::optional RemoveConnectionFromGroup(const ConnectionId &id, const GroupId &gid); const std::optional LinkConnectionWithGroup(const ConnectionId &id, const GroupId &newGroupId); // // Get Conncetion Property diff --git a/src/ui/w_GroupManager.cpp b/src/ui/w_GroupManager.cpp index f686f884..c35af0cf 100644 --- a/src/ui/w_GroupManager.cpp +++ b/src/ui/w_GroupManager.cpp @@ -75,7 +75,7 @@ void GroupManager::onRCMDeleteConnectionTriggered() const auto list = GET_SELECTED_CONNECTION_IDS(SELECTED_ROWS_INDEX); for (const auto &item : list) { - ConnectionManager->DeleteConnectionFromGroup(ConnectionId(item), currentGroupId); + ConnectionManager->RemoveConnectionFromGroup(ConnectionId(item), currentGroupId); } } diff --git a/src/ui/w_MainWindow.cpp b/src/ui/w_MainWindow.cpp index ebbaf14c..cabf4edf 100644 --- a/src/ui/w_MainWindow.cpp +++ b/src/ui/w_MainWindow.cpp @@ -581,7 +581,7 @@ void MainWindow::on_action_RCM_DeleteThese_triggered() if (GlobalConfig.autoStartId == conn) GlobalConfig.autoStartId.clear(); - ConnectionManager->DeleteConnectionFromGroup(conn.connectionId, conn.groupId); + ConnectionManager->RemoveConnectionFromGroup(conn.connectionId, conn.groupId); } } diff --git a/src/ui/widgets/ConnectionInfoWidget.cpp b/src/ui/widgets/ConnectionInfoWidget.cpp index 483450d0..c95971b5 100644 --- a/src/ui/widgets/ConnectionInfoWidget.cpp +++ b/src/ui/widgets/ConnectionInfoWidget.cpp @@ -168,7 +168,7 @@ void ConnectionInfoWidget::on_deleteBtn_clicked() { if (connectionId != NullConnectionId) { - ConnectionManager->DeleteConnectionFromGroup(connectionId, groupId); + ConnectionManager->RemoveConnectionFromGroup(connectionId, groupId); } else { From 2c7bd53f08580fee7cc0a2cf3dff16d3fd5f8f22 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Fri, 15 May 2020 00:04:02 +0800 Subject: [PATCH 090/385] add: added Connection Linking/Moving/Copying via group manager --- makespec/BUILDVERSION | 2 +- src/base/models/QvConfigIdentifier.hpp | 2 +- src/core/handler/ConfigHandler.cpp | 104 +++++++++++++++--------- src/core/handler/ConfigHandler.hpp | 10 ++- src/ui/w_GroupManager.cpp | 42 +++++++--- src/ui/w_GroupManager.hpp | 5 +- src/ui/w_MainWindow.cpp | 3 +- src/ui/widgets/ConnectionItemWidget.cpp | 2 +- 8 files changed, 109 insertions(+), 61 deletions(-) diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index a6bfcd0b..38331698 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5390 \ No newline at end of file +5391 diff --git a/src/base/models/QvConfigIdentifier.hpp b/src/base/models/QvConfigIdentifier.hpp index f9123474..89d64a32 100644 --- a/src/base/models/QvConfigIdentifier.hpp +++ b/src/base/models/QvConfigIdentifier.hpp @@ -115,7 +115,7 @@ namespace Qv2ray::base // int __qvConnectionRefCount; // - ConnectionObject() : lastConnected(), latency(QVTCPING_VALUE_NODATA), upLinkData(0), downLinkData(0){}; + ConnectionObject() : lastConnected(), latency(QVTCPING_VALUE_NODATA), upLinkData(0), downLinkData(0), __qvConnectionRefCount(0){}; JSONSTRUCT_REGISTER(ConnectionObject, F(lastConnected, latency, upLinkData, downLinkData), B(__Qv2rayConfigObjectBase)) }; diff --git a/src/core/handler/ConfigHandler.cpp b/src/core/handler/ConfigHandler.cpp index 89ef9398..2b166e6c 100644 --- a/src/core/handler/ConfigHandler.cpp +++ b/src/core/handler/ConfigHandler.cpp @@ -33,6 +33,12 @@ namespace Qv2ray::core::handlers auto const &connectionObject = connections.value(id); if (connectionObject.__qvConnectionRefCount == 0) { + QFile connectionFile(QV2RAY_CONNECTIONS_DIR + id.toString() + QV2RAY_CONFIG_FILE_EXTENSION); + if (connectionFile.exists()) + { + if (!connectionFile.remove()) + LOG(MODULE_CONNECTION, "Failed to remove connection config file") + } connections.remove(id); LOG(MODULE_CORE_HANDLER, "Dropped connection id: " + id.toString() + " since it's not in a group") } @@ -215,73 +221,93 @@ namespace Qv2ray::core::handlers return {}; } - const std::optional QvConfigHandler::RemoveConnectionFromGroup(const ConnectionId &id, const GroupId &gid) + bool QvConfigHandler::RemoveConnectionFromGroup(const ConnectionId &id, const GroupId &gid, bool blockSignal) { - CheckConnectionExistance(id); + CheckConnectionExistanceEx(id, false); + LOG(MODULE_CONNECTION, "Removing connection : " + id.toString()) if (groups[gid].connections.contains(id)) { auto removedEntries = groups[gid].connections.removeAll(id); - if (removedEntries > 0) + if (removedEntries > 1) { LOG(MODULE_CONNECTION, "Found same connection occured multiple times in a group.") } // Decrease reference count. - connections[id].__qvConnectionRefCount--; - } - // - if (connections[id].__qvConnectionRefCount == 0) - { - LOG(MODULE_CONNECTION, "Removing a connection") - connectionRootCache.remove(id); + connections[id].__qvConnectionRefCount -= removedEntries; } if (GlobalConfig.autoStartId == ConnectionGroupPair{ id, gid }) { + GlobalConfig.autoStartId.clear(); } - + // + // Emit everything first then clear the connection map. PluginHost->Send_ConnectionEvent({ Events::ConnectionEntry::RemovedFromGroup, GetDisplayName(id), "" }); + emit OnConnectionRemovedFromGroup({ id, gid }); - // auto groupId = connections[id].groupId; - // 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, "", Events::ConnectionEntry::Deleted }); - // connections.remove(id); - // groups[groupId].connections.removeAll(id); - //// - // if (GlobalConfig.autoStartId == id.toString()) - //{ - // GlobalConfig.autoStartId.clear(); - //} - //// - // emit OnConnectionDeleted(id, groupId); - //// - // bool exists = connectionFile.exists(); - // if (exists) - //{ - // bool removed = connectionFile.remove(); - // if (removed) - // { - // return {}; - // } - // return "Failed to remove file"; - //} - return tr("File does not exist."); + // + if (connections[id].__qvConnectionRefCount <= 0) + { + LOG(MODULE_CONNECTION, "Fully removing a connection from cache.") + connectionRootCache.remove(id); + // + QFile connectionFile(QV2RAY_CONNECTIONS_DIR + id.toString() + QV2RAY_CONFIG_FILE_EXTENSION); + if (connectionFile.exists()) + { + if (!connectionFile.remove()) + LOG(MODULE_CONNECTION, "Failed to remove connection config file") + } + } + return true; } - const std::optional QvConfigHandler::LinkConnectionWithGroup(const ConnectionId &id, const GroupId &newGroupId) + bool QvConfigHandler::LinkConnectionWithGroup(const ConnectionId &id, const GroupId &newGroupId, bool blockSignal) { - CheckConnectionExistance(id); + CheckConnectionExistanceEx(id, false); if (!groups[newGroupId].connections.contains(id)) { groups[newGroupId].connections.append(id); } PluginHost->Send_ConnectionEvent({ Events::ConnectionEntry::LinkedWithGroup, connections[id].displayName, "" }); + emit OnConnectionLinkedWithGroup({ id, newGroupId }); + return {}; } + bool QvConfigHandler::MoveConnectionFromToGroup(const ConnectionId &id, const GroupId &sourceGid, const GroupId &targetGid, bool blockSignal) + { + CheckConnectionExistanceEx(id, false); + CheckGroupExistanceEx(targetGid, false); + CheckGroupExistanceEx(sourceGid, false); + // + if (!groups[sourceGid].connections.contains(id)) + { + LOG(MODULE_CONNECTION, "Trying to move a connection away from a group it does not belong to.") + return false; + } + if (groups[targetGid].connections.contains(id)) + { + LOG(MODULE_CONNECTION, "The connection: " + id.toString() + " has already been in the target group: " + targetGid.toString()) + auto removedCount = groups[sourceGid].connections.removeAll(id); + connections[id].__qvConnectionRefCount -= removedCount; + } + else + { + // If the target group does not contain this connection. + auto removedCount = groups[sourceGid].connections.removeAll(id); + connections[id].__qvConnectionRefCount -= removedCount; + // + groups[targetGid].connections.append(id); + connections[id].__qvConnectionRefCount++; + } + + emit OnConnectionMovedToGroup({ id, targetGid }, sourceGid); + + return true; + } + const std::optional QvConfigHandler::DeleteGroup(const GroupId &id) { CheckGroupExistance(id); diff --git a/src/core/handler/ConfigHandler.hpp b/src/core/handler/ConfigHandler.hpp index d4fb0280..356dd58d 100644 --- a/src/core/handler/ConfigHandler.hpp +++ b/src/core/handler/ConfigHandler.hpp @@ -97,8 +97,9 @@ namespace Qv2ray::core::handlers const std::optional RenameConnection(const ConnectionId &id, const QString &newName); // // Connection - Group binding - const std::optional RemoveConnectionFromGroup(const ConnectionId &id, const GroupId &gid); - const std::optional LinkConnectionWithGroup(const ConnectionId &id, const GroupId &newGroupId); + bool RemoveConnectionFromGroup(const ConnectionId &id, const GroupId &gid, bool blockSignal = false); + bool MoveConnectionFromToGroup(const ConnectionId &id, const GroupId &sourceGid, const GroupId &targetGid, bool blockSignal = false); + bool LinkConnectionWithGroup(const ConnectionId &id, const GroupId &newGroupId, bool blockSignal = false); // // Get Conncetion Property const CONFIGROOT GetConnectionRoot(const ConnectionId &id) const; @@ -124,10 +125,11 @@ namespace Qv2ray::core::handlers void OnKernelLogAvailable(const ConnectionGroupPair &id, const QString &log); void OnStatsAvailable(const ConnectionGroupPair &id, const quint64 upS, const quint64 downS, const quint64 upD, const quint64 downD); // - void OnConnectionRenamed(const ConnectionId &Id, const QString &originalName, const QString &newName); void OnConnectionCreated(const ConnectionGroupPair &Id, const QString &displayName); - void OnConnectionDeleted(const ConnectionGroupPair &Id); void OnConnectionModified(const ConnectionId &id); + void OnConnectionRenamed(const ConnectionId &Id, const QString &originalName, const QString &newName); + // + void OnConnectionMovedToGroup(const ConnectionGroupPair &newPair, const GroupId &originalGroup); void OnConnectionRemovedFromGroup(const ConnectionGroupPair &pairId); void OnConnectionLinkedWithGroup(const ConnectionGroupPair &newPair); // diff --git a/src/ui/w_GroupManager.cpp b/src/ui/w_GroupManager.cpp index c35af0cf..9849e287 100644 --- a/src/ui/w_GroupManager.cpp +++ b/src/ui/w_GroupManager.cpp @@ -39,23 +39,23 @@ GroupManager::GroupManager(QWidget *parent) : QDialog(parent) connectionListRCMenu->addSeparator(); connectionListRCMenu->addMenu(connectionListRCMenu_CopyToMenu); connectionListRCMenu->addMenu(connectionListRCMenu_MoveToMenu); + connectionListRCMenu->addMenu(connectionListRCMenu_LinkToMenu); // connect(exportConnectionAction, &QAction::triggered, this, &GroupManager::onRCMExportConnectionTriggered); connect(deleteConnectionAction, &QAction::triggered, this, &GroupManager::onRCMDeleteConnectionTriggered); // connect(ConnectionManager, &QvConfigHandler::OnConnectionLinkedWithGroup, // [&]() { // - this->loadConnectionList(currentGroupId); // + this->reloadConnectionsList(currentGroupId); // }); // // Anyway just reload it. const auto reloadGroupLambda = [&](const ConnectionGroupPair &id) { if (id.groupId == currentGroupId) - this->loadConnectionList(id.groupId); + this->reloadConnectionsList(id.groupId); }; - connect(ConnectionManager, &QvConfigHandler::OnConnectionCreated, reloadGroupLambda); - connect(ConnectionManager, &QvConfigHandler::OnConnectionDeleted, reloadGroupLambda); - connect(ConnectionManager, &QvConfigHandler::OnConnectionRemovedFromGroup, reloadGroupLambda); + // connect(ConnectionManager, &QvConfigHandler::OnConnectionCreated, reloadGroupLambda); + // connect(ConnectionManager, &QvConfigHandler::OnConnectionDeleted, reloadGroupLambda); // for (const auto &group : ConnectionManager->AllGroups()) { @@ -67,7 +67,7 @@ GroupManager::GroupManager(QWidget *parent) : QDialog(parent) { groupList->setCurrentItem(groupList->item(0)); } - ReloadGroupAction(); + reloadGroupRCMActions(); } void GroupManager::onRCMDeleteConnectionTriggered() @@ -75,8 +75,9 @@ void GroupManager::onRCMDeleteConnectionTriggered() const auto list = GET_SELECTED_CONNECTION_IDS(SELECTED_ROWS_INDEX); for (const auto &item : list) { - ConnectionManager->RemoveConnectionFromGroup(ConnectionId(item), currentGroupId); + ConnectionManager->RemoveConnectionFromGroup(ConnectionId(item), currentGroupId, true); } + reloadConnectionsList(currentGroupId); } void GroupManager::onRCMExportConnectionTriggered() @@ -130,7 +131,7 @@ void GroupManager::onRCMExportConnectionTriggered() } } -void GroupManager::ReloadGroupAction() +void GroupManager::reloadGroupRCMActions() { connectionListRCMenu_CopyToMenu->clear(); connectionListRCMenu_MoveToMenu->clear(); @@ -138,19 +139,23 @@ void GroupManager::ReloadGroupAction() { auto cpAction = new QAction(GetDisplayName(group), connectionListRCMenu_CopyToMenu); auto mvAction = new QAction(GetDisplayName(group), connectionListRCMenu_MoveToMenu); + auto lnAction = new QAction(GetDisplayName(group), connectionListRCMenu_LinkToMenu); // cpAction->setData(group.toString()); mvAction->setData(group.toString()); + lnAction->setData(group.toString()); // connectionListRCMenu_CopyToMenu->addAction(cpAction); connectionListRCMenu_MoveToMenu->addAction(mvAction); + connectionListRCMenu_LinkToMenu->addAction(lnAction); // connect(cpAction, &QAction::triggered, this, &GroupManager::onRCMActionTriggered_Copy); connect(mvAction, &QAction::triggered, this, &GroupManager::onRCMActionTriggered_Move); + connect(lnAction, &QAction::triggered, this, &GroupManager::onRCMActionTriggered_Link); } } -void GroupManager::loadConnectionList(const GroupId &group) +void GroupManager::reloadConnectionsList(const GroupId &group) { connectionsTable->clearContents(); connectionsTable->model()->removeRows(0, connectionsTable->rowCount()); @@ -191,6 +196,20 @@ void GroupManager::onRCMActionTriggered_Copy() const auto &connectionId = ConnectionId(connId); ConnectionManager->CreateConnection(ConnectionManager->GetConnectionRoot(connectionId), GetDisplayName(connectionId), groupId, true); } + reloadConnectionsList(currentGroupId); +} + +void GroupManager::onRCMActionTriggered_Link() +{ + const auto _sender = qobject_cast(sender()); + const GroupId groupId{ _sender->data().toString() }; + // + const auto list = GET_SELECTED_CONNECTION_IDS(SELECTED_ROWS_INDEX); + for (const auto &connId : list) + { + ConnectionManager->LinkConnectionWithGroup(ConnectionId(connId), groupId, false); + } + reloadConnectionsList(currentGroupId); } void GroupManager::onRCMActionTriggered_Move() @@ -201,8 +220,9 @@ void GroupManager::onRCMActionTriggered_Move() const auto list = GET_SELECTED_CONNECTION_IDS(SELECTED_ROWS_INDEX); for (const auto &connId : list) { - ConnectionManager->LinkConnectionWithGroup(ConnectionId(connId), groupId); + ConnectionManager->MoveConnectionFromToGroup(ConnectionId(connId), currentGroupId, groupId, false); } + reloadConnectionsList(currentGroupId); } void GroupManager::UpdateColorScheme() @@ -300,7 +320,7 @@ void GroupManager::on_groupList_itemClicked(QListWidgetItem *item) createdAtLabel->setText(timeToString(groupMetaObject.creationDate)); updateIntervalSB->setValue(groupMetaObject.subscriptionOption.updateInterval); // - loadConnectionList(currentGroupId); + reloadConnectionsList(currentGroupId); } void GroupManager::on_groupList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous) diff --git a/src/ui/w_GroupManager.hpp b/src/ui/w_GroupManager.hpp index 38100dd5..9b012339 100644 --- a/src/ui/w_GroupManager.hpp +++ b/src/ui/w_GroupManager.hpp @@ -54,10 +54,11 @@ class GroupManager void on_connectionsTable_customContextMenuRequested(const QPoint &pos); private: - void loadConnectionList(const GroupId &group); + void reloadConnectionsList(const GroupId &group); void onRCMActionTriggered_Move(); void onRCMActionTriggered_Copy(); - void ReloadGroupAction(); + void onRCMActionTriggered_Link(); + void reloadGroupRCMActions(); // void ExportConnectionFilter(CONFIGROOT &root); // diff --git a/src/ui/w_MainWindow.cpp b/src/ui/w_MainWindow.cpp index cabf4edf..5dea0cb3 100644 --- a/src/ui/w_MainWindow.cpp +++ b/src/ui/w_MainWindow.cpp @@ -173,10 +173,9 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) connect(ConnectionManager, &QvConfigHandler::OnStatsAvailable, this, &MainWindow::OnStatsAvailable); connect(ConnectionManager, &QvConfigHandler::OnKernelLogAvailable, this, &MainWindow::OnVCoreLogAvailable); // - connect(ConnectionManager, &QvConfigHandler::OnConnectionDeleted, this, &MainWindow::OnConnectionDeleted); + connect(ConnectionManager, &QvConfigHandler::OnConnectionRemovedFromGroup, this, &MainWindow::OnConnectionDeleted); connect(ConnectionManager, &QvConfigHandler::OnConnectionCreated, this, &MainWindow::OnConnectionCreated); connect(ConnectionManager, &QvConfigHandler::OnConnectionLinkedWithGroup, this, &MainWindow::OnConnectionLinkedWithGroup); - connect(ConnectionManager, &QvConfigHandler::OnConnectionRemovedFromGroup, this, &MainWindow::OnConnectionLinkedWithGroup); // connect(ConnectionManager, &QvConfigHandler::OnGroupCreated, this, &MainWindow::OnGroupCreated); connect(ConnectionManager, &QvConfigHandler::OnGroupDeleted, this, &MainWindow::OnGroupDeleted); diff --git a/src/ui/widgets/ConnectionItemWidget.cpp b/src/ui/widgets/ConnectionItemWidget.cpp index a77cc123..e3ad9e4d 100644 --- a/src/ui/widgets/ConnectionItemWidget.cpp +++ b/src/ui/widgets/ConnectionItemWidget.cpp @@ -69,10 +69,10 @@ ConnectionItemWidget::ConnectionItemWidget(const GroupId &id, QWidget *parent) : // OnGroupItemRenamed(id, "", originalItemName); connect(ConnectionManager, &QvConfigHandler::OnConnectionCreated, this, &ConnectionItemWidget::RecalculateConnectionsCount); - connect(ConnectionManager, &QvConfigHandler::OnConnectionDeleted, this, &ConnectionItemWidget::RecalculateConnectionsCount); connect(ConnectionManager, &QvConfigHandler::OnConnectionModified, this, &ConnectionItemWidget::RecalculateConnectionsCount); connect(ConnectionManager, &QvConfigHandler::OnConnectionLinkedWithGroup, this, &ConnectionItemWidget::RecalculateConnectionsCount); connect(ConnectionManager, &QvConfigHandler::OnSubscriptionUpdateFinished, this, &ConnectionItemWidget::RecalculateConnectionsCount); + connect(ConnectionManager, &QvConfigHandler::OnConnectionRemovedFromGroup, this, &ConnectionItemWidget::RecalculateConnectionsCount); // connect(ConnectionManager, &QvConfigHandler::OnGroupRenamed, this, &ConnectionItemWidget::OnGroupItemRenamed); } From 53a7dcbc2d7c208686a30ffd3abb2b393f4232e7 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Fri, 15 May 2020 00:25:55 +0800 Subject: [PATCH 091/385] fix: fixed build --- makespec/BUILDVERSION | 2 +- src/components/autolaunch/QvAutoLaunch.cpp | 24 +++++++++++++--------- src/ui/w_PreferencesWindow.cpp | 6 +++--- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index bd6b502b..4e44c289 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5400 +5401 \ No newline at end of file diff --git a/src/components/autolaunch/QvAutoLaunch.cpp b/src/components/autolaunch/QvAutoLaunch.cpp index 3390bccb..55a74437 100644 --- a/src/components/autolaunch/QvAutoLaunch.cpp +++ b/src/components/autolaunch/QvAutoLaunch.cpp @@ -1,5 +1,7 @@ #include "QvAutoLaunch.hpp" +#include "base/Qv2rayBase.hpp" + #include #include #include @@ -184,16 +186,18 @@ namespace Qv2ray::components::autolaunch QTextStream ts(&iniFile); ts.setCodec("UTF-8"); - ts << QLatin1String("[Desktop Entry]") << Qt::endl - << QLatin1String("Name=") << QApplication::applicationName() + QApplication::arguments().join(" ") << Qt::endl - << QLatin1String("GenericName=") << QLatin1String("V2ray Frontend") << Qt::endl - << QLatin1String("Exec=") << binPath << Qt::endl - << QLatin1String("Terminal=") << "false" << Qt::endl - << QLatin1String("Icon=") << "qv2ray" << Qt::endl // always use lowercase for icons - << QLatin1String("Categories=") << "Network" << Qt::endl - << QLatin1String("Type=") << "Application" << Qt::endl - << QLatin1String("StartupNotify=") << "false" << Qt::endl - << QLatin1String("X-GNOME-Autostart-enabled=") << "true" << Qt::endl; + ts << QLatin1String("[Desktop Entry]") << NEWLINE // + << QLatin1String("Name=") << QApplication::applicationName() + QApplication::arguments().join(" ") << NEWLINE // + << QLatin1String("GenericName=") << QLatin1String("V2ray Frontend") << NEWLINE // + << QLatin1String("Exec=") << binPath << NEWLINE // + << QLatin1String("Terminal=") << "false" << NEWLINE // + << QLatin1String("Icon=") << "qv2ray" << NEWLINE // + << QLatin1String("Categories=") << "Network" << NEWLINE // + << QLatin1String("Type=") << "Application" << NEWLINE // + << QLatin1String("StartupNotify=") << "false" << NEWLINE // + << QLatin1String("X-GNOME-Autostart-enabled=") << "true" << NEWLINE; + ts.flush(); + iniFile.close(); } else { diff --git a/src/ui/w_PreferencesWindow.cpp b/src/ui/w_PreferencesWindow.cpp index c6f4500b..ce9cb2e1 100644 --- a/src/ui/w_PreferencesWindow.cpp +++ b/src/ui/w_PreferencesWindow.cpp @@ -1,4 +1,4 @@ -#include "w_PreferencesWindow.hpp" +#include "w_PreferencesWindow.hpp" #include "common/HTTPRequestHelper.hpp" #include "common/QvHelpers.hpp" @@ -1153,8 +1153,8 @@ void PreferencesWindow::on_enableAPI_stateChanged(int arg1) { LOADINGCHECK NEEDRESTART - - CurrentConfig.apiConfig.enableAPI = arg1 == Qt::Checked; + + CurrentConfig.kernelConfig.enableAPI = arg1 == Qt::Checked; if (arg1 == Qt::Unchecked) { const auto msgAPIDisableTitle = tr("Disabling API Subsystem"); From 3df2755a170a4e46aa89009a6310ff51065242e3 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Fri, 15 May 2020 00:28:45 +0800 Subject: [PATCH 092/385] trigger: trigger CI rebuild --- makespec/BUILDVERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 4e44c289..2ecfc282 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5401 \ No newline at end of file +5402 From 7963f03f55bf5cf5a5cbc7b1c596d31da9c7477e Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Fri, 15 May 2020 00:44:20 +0800 Subject: [PATCH 093/385] fix: fixed connection issue --- makespec/BUILDVERSION | 2 +- src/core/handler/ConfigHandler.cpp | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 2ecfc282..597adfa6 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5402 +5403 \ No newline at end of file diff --git a/src/core/handler/ConfigHandler.cpp b/src/core/handler/ConfigHandler.cpp index 2b166e6c..698b3865 100644 --- a/src/core/handler/ConfigHandler.cpp +++ b/src/core/handler/ConfigHandler.cpp @@ -330,7 +330,7 @@ namespace Qv2ray::core::handlers } else { - QDir(QV2RAY_CONNECTIONS_DIR + id.toString()).removeRecursively(); + QFile(QV2RAY_CONNECTIONS_DIR + id.toString()).remove(); } // PluginHost->Send_ConnectionEvent({ Events::ConnectionEntry::FullyRemoved, groups[id].displayName, "" }); @@ -347,11 +347,10 @@ namespace Qv2ray::core::handlers const std::optional QvConfigHandler::StartConnection(const ConnectionId &id, const GroupId &group) { - return {}; - // CheckConnectionExistance(id); - // connections[id].lastConnected = system_clock::to_time_t(system_clock::now()); - // CONFIGROOT root = GetConnectionRoot(id); - // return kernelHandler->StartConnection(id, root); + CheckConnectionExistance(id); + connections[id].lastConnected = system_clock::to_time_t(system_clock::now()); + CONFIGROOT root = GetConnectionRoot(id); + return kernelHandler->StartConnection({ id, group }, root); } void QvConfigHandler::RestartConnection() // const ConnectionId &id From 74f40f0775b21aebdf3a4872ca7733314fb00ab5 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Fri, 15 May 2020 00:46:39 +0800 Subject: [PATCH 094/385] fix: fixed connection issue 2 --- makespec/BUILDVERSION | 2 +- src/core/handler/ConfigHandler.cpp | 12 +----------- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 597adfa6..1db59029 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5403 \ No newline at end of file +5404 diff --git a/src/core/handler/ConfigHandler.cpp b/src/core/handler/ConfigHandler.cpp index 698b3865..b0769eac 100644 --- a/src/core/handler/ConfigHandler.cpp +++ b/src/core/handler/ConfigHandler.cpp @@ -320,17 +320,7 @@ namespace Qv2ray::core::handlers auto list = groups[id].connections; for (const auto &conn : list) { - LinkConnectionWithGroup(conn, DefaultGroupId); - } - // - // TODO - if (groups[id].isSubscription) - { - // QDir(QV2RAY_SUBSCRIPTION_DIR + id.toString()).removeRecursively(); - } - else - { - QFile(QV2RAY_CONNECTIONS_DIR + id.toString()).remove(); + MoveConnectionFromToGroup(conn, id, DefaultGroupId); } // PluginHost->Send_ConnectionEvent({ Events::ConnectionEntry::FullyRemoved, groups[id].displayName, "" }); From e5614e7ee1a04e5655ba9d53d65460a8611e55d8 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Fri, 15 May 2020 15:48:33 +0800 Subject: [PATCH 095/385] add: added customize UI style capability --- .gitmodules | 3 ++ 3rdparty/uistyles | 1 + CMakeLists.txt | 8 +++- assets/credit.html | 1 + makespec/BUILDVERSION | 2 +- src/base/Qv2rayBase.hpp | 1 + src/main.cpp | 15 +++---- src/ui/styles/StyleManager.cpp | 75 ++++++++++++++++++++++++++++++++++ src/ui/styles/StyleManager.hpp | 38 +++++++++++++++++ src/ui/w_PreferencesWindow.cpp | 7 ++-- 10 files changed, 137 insertions(+), 14 deletions(-) create mode 160000 3rdparty/uistyles create mode 100644 src/ui/styles/StyleManager.cpp create mode 100644 src/ui/styles/StyleManager.hpp diff --git a/.gitmodules b/.gitmodules index 9d280afb..da7a64d9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -19,3 +19,6 @@ [submodule "libs/QJsonStruct"] path = libs/QJsonStruct url = https://github.com/Qv2ray/QJsonStruct +[submodule "3rdparty/uistyles"] + path = 3rdparty/uistyles + url = https://github.com/Qv2ray/QvUIStyles diff --git a/3rdparty/uistyles b/3rdparty/uistyles new file mode 160000 index 00000000..9417ac24 --- /dev/null +++ b/3rdparty/uistyles @@ -0,0 +1 @@ +Subproject commit 9417ac243e55b0c1171a6042de58cebab812b6cb diff --git a/CMakeLists.txt b/CMakeLists.txt index 99608306..f2c9946f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -154,6 +154,8 @@ endif() set(QVPLUGIN_INTERFACE_INCLUDE_DIR "src/components/plugins/interface") include(src/components/plugins/interface/QvPluginInterface.cmake) +include(3rdparty/uistyles/uistyles.cmake) + set(QV2RAY_SOURCES ${QVPLUGIN_INTERFACE_HEADERS} 3rdparty/libsemver/version.cpp @@ -207,6 +209,7 @@ set(QV2RAY_SOURCES src/ui/models/InboundNodeModel.cpp src/ui/models/OutboundNodeModel.cpp src/ui/models/RuleNodeModel.cpp + src/ui/styles/StyleManager.cpp src/ui/widgets/ConnectionInfoWidget.cpp src/ui/widgets/ConnectionItemWidget.cpp src/ui/widgets/QvAutoCompleteTextEdit.cpp @@ -294,6 +297,7 @@ set(QV2RAY_SOURCES src/ui/models/NodeModelsBase.hpp src/ui/models/OutboundNodeModel.hpp src/ui/models/RuleNodeModel.hpp + src/ui/styles/StyleManager.hpp src/ui/widgets/ConnectionInfoWidget.hpp src/ui/widgets/ConnectionItemWidget.hpp src/ui/widgets/QvAutoCompleteTextEdit.hpp @@ -322,7 +326,9 @@ add_custom_target(lupdate set_target_properties(lupdate PROPERTIES EXCLUDE_FROM_ALL TRUE) -set(QRC_RESOURCES ${CMAKE_SOURCE_DIR}/resources.qrc) +set(QRC_RESOURCES + ${UISTYLE_QRCS} + ${CMAKE_SOURCE_DIR}/resources.qrc) set(QT_LIBRARY Qt5::Core diff --git a/assets/credit.html b/assets/credit.html index 63ed7c1d..bea6ac42 100644 --- a/assets/credit.html +++ b/assets/credit.html @@ -18,4 +18,5 @@ p, li { white-space: pre-wrap; }

Copyright (c) 2019 ShadowSocks (@shadowsocks): libQtShadowsocks (LGPLv3)

Copyright (c) 2015-2020 qBittorrent (Anton Lashkov) (@qBittorrent): speedplotview (GPLv2)

Copyright (c) 2020 Diffusions Nu-book Inc. (nu-book): zxing-cpp (Apache)

+

Copyright (c) 2020 feiyangqingyun: QWidgetDemo (Mulan PSL v1)

diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 1db59029..8929f0c2 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5404 +5418 \ No newline at end of file diff --git a/src/base/Qv2rayBase.hpp b/src/base/Qv2rayBase.hpp index e9652f65..f9f4d67b 100644 --- a/src/base/Qv2rayBase.hpp +++ b/src/base/Qv2rayBase.hpp @@ -128,6 +128,7 @@ namespace Qv2ray // Configuration Path QStringList list; list << QV2RAY_CONFIG_DIR + dirName; + list << ":/" + dirName; // #ifdef Q_OS_LINUX // Linux platform directories. diff --git a/src/main.cpp b/src/main.cpp index 30159a74..39d51347 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,10 +1,11 @@ -#include "3rdparty/SingleApplication/singleapplication.h" +#include "3rdparty/SingleApplication/singleapplication.h" #include "common/CommandArgs.hpp" #include "common/QvHelpers.hpp" #include "common/QvTranslator.hpp" #include "core/handler/ConfigHandler.hpp" #include "core/settings/SettingsBackend.hpp" #include "src/components/plugins/QvPluginHost.hpp" +#include "ui/styles/StyleManager.hpp" #include "ui/w_MainWindow.hpp" #include @@ -275,6 +276,7 @@ int main(int argc, char *argv[]) "Copyright (c) 2019 ShadowSocks (@shadowsocks): libQtShadowsocks (LGPLv3)" NEWLINE // "Copyright (c) 2015-2020 qBittorrent (Anton Lashkov) (@qBittorrent): speedplotview (GPLv2)" NEWLINE // "Copyright (c) 2020 Diffusions Nu-book Inc. (@nu-book): zxing-cpp (Apache)" NEWLINE // + "Copyright (c) 2020 feiyangqingyun: QWidgetDemo (Mulan PSL v1)" NEWLINE // NEWLINE) // // LOG(MODULE_INIT, "Qv2ray Start Time: " + QSTRN(QTime::currentTime().msecsSinceStartOfDay())) @@ -361,15 +363,9 @@ int main(int argc, char *argv[]) font.setFamily("Microsoft YaHei"); _qApp.setFont(font); #endif - // Set custom themes. - QStringList themes = QStyleFactory::keys(); - //_qApp.setDesktopFileName("qv2ray.desktop"); + StyleManager = new QvStyleManager(); + StyleManager->ApplyStyle(confObject.uiConfig.theme); - if (themes.contains(confObject.uiConfig.theme)) - { - LOG(MODULE_INIT + " " + MODULE_UI, "Setting Qv2ray UI themes: " + confObject.uiConfig.theme) - qApp->setStyle(confObject.uiConfig.theme); - } #if (QV2RAY_USE_BUILTIN_DARKTHEME) LOG(MODULE_UI, "Using built-in theme.") @@ -436,6 +432,7 @@ int main(int argc, char *argv[]) auto rcode = _qApp.exec(); delete ConnectionManager; delete PluginHost; + delete StyleManager; LOG(MODULE_INIT, "Quitting normally") return rcode; #ifndef QT_DEBUG diff --git a/src/ui/styles/StyleManager.cpp b/src/ui/styles/StyleManager.cpp new file mode 100644 index 00000000..51ddc21f --- /dev/null +++ b/src/ui/styles/StyleManager.cpp @@ -0,0 +1,75 @@ +#include "StyleManager.hpp" + +#include "base/Qv2rayBase.hpp" +#include "common/QvHelpers.hpp" + +#include +#include + +namespace Qv2ray::ui::styles +{ + QvStyleManager::QvStyleManager() + { + ReloadStyles(); + } + + void QvStyleManager::ReloadStyles() + { + styles.clear(); + for (const auto &key : QStyleFactory::keys()) + { + LOG(MODULE_UI, "Found factory style: " + key) + QvStyle style; + style.Name = key; + style.Type = QvStyle::QVSTYLE_FACTORY; + styles.insert(key, style); + } + + for (const auto &styleDir : Qv2rayAssetsPaths("uistyles")) + { + for (const auto &file : GetFileList(QDir(styleDir))) + { + QFileInfo fileInfo(styleDir + "/" + file); + if (fileInfo.suffix() == "css" || fileInfo.suffix() == "qss" || fileInfo.suffix() == "qvstyle") + { + LOG(MODULE_UI, "Found QSS style at: \"" + fileInfo.absoluteFilePath() + "\"") + QvStyle style; + style.Name = fileInfo.baseName(); + style.qssPath = fileInfo.absoluteFilePath(); + style.Type = QvStyle::QVSTYLE_QSS; + styles.insert(style.Name, style); + } + } + } + } + + bool QvStyleManager::ApplyStyle(const QString &style) + { + if (!styles.contains(style)) + return false; + const auto &s = styles[style]; + switch (s.Type) + { + case QvStyle::QVSTYLE_QSS: + { + LOG(MODULE_UI, "Applying UI QSS style: " + s.qssPath) + qApp->setStyle(QStyleFactory::create("fusion")); + qApp->processEvents(); + // + const auto content = StringFromFile(s.qssPath); + QString paletteColor = content.mid(20, 7); + qApp->setPalette(QPalette(QColor(paletteColor))); + qApp->setStyleSheet(content); + return true; + } + case QvStyle::QVSTYLE_FACTORY: + { + LOG(MODULE_UI, "Applying UI style: " + s.Name) + qApp->setStyle(QStyleFactory::create(s.Name)); + qApp->setStyleSheet(""); + return true; + } + default: return false; + } + } +} // namespace Qv2ray::ui::styles diff --git a/src/ui/styles/StyleManager.hpp b/src/ui/styles/StyleManager.hpp new file mode 100644 index 00000000..ad2676c7 --- /dev/null +++ b/src/ui/styles/StyleManager.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include +#include +#include + +namespace Qv2ray::ui::styles +{ + struct QvStyle + { + enum StyleType + { + QVSTYLE_FACTORY, + QVSTYLE_QSS + } Type; + QString Name; + QString qssPath; + }; + + class QvStyleManager + { + public: + QvStyleManager(); + inline QStringList AllStyles() const + { + return styles.keys(); + } + bool ApplyStyle(const QString &); + + private: + void ReloadStyles(); + QMap styles; + }; + + inline QvStyleManager *StyleManager = nullptr; +} // namespace Qv2ray::ui::styles + +using namespace Qv2ray::ui::styles; diff --git a/src/ui/w_PreferencesWindow.cpp b/src/ui/w_PreferencesWindow.cpp index ce9cb2e1..06489d6f 100644 --- a/src/ui/w_PreferencesWindow.cpp +++ b/src/ui/w_PreferencesWindow.cpp @@ -10,6 +10,7 @@ #include "core/handler/ConfigHandler.hpp" #include "core/kernel/V2rayKernelInteractions.hpp" #include "core/settings/SettingsBackend.hpp" +#include "ui/styles/StyleManager.hpp" #include "ui/widgets/RouteSettingsMatrix.hpp" #include @@ -66,7 +67,7 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent), Current SetAutoStartButtonsState(GetLaunchAtLoginStatus()); // nsBarContentCombo->addItems(NetSpeedPluginMessages.values()); - themeCombo->addItems(QStyleFactory::keys()); + themeCombo->addItems(StyleManager->AllStyles()); // qvVersion->setText(QV2RAY_VERSION_STRING); qvBuildInfo->setText(QV2RAY_BUILD_INFO); @@ -83,7 +84,7 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent), Current #if (QV2RAY_USE_BUILTIN_DARKTHEME) // If we use built in theme, it should always be fusion. themeCombo->setEnabled(!CurrentConfig.uiConfig.useDarkTheme); - darkThemeLabel->setText(tr("Use Darkmode Theme")); + darkThemeLabel->setText(tr("Use built-in darkmode Theme")); #endif languageComboBox->setCurrentText(CurrentConfig.uiConfig.language); logLevelComboBox->setCurrentIndex(CurrentConfig.logLevel); @@ -339,7 +340,7 @@ void PreferencesWindow::on_buttonBox_accepted() { NEEDRESTART } - qApp->setStyle(QStyleFactory::create(CurrentConfig.uiConfig.theme)); + StyleManager->ApplyStyle(CurrentConfig.uiConfig.theme); SaveGlobalSettings(CurrentConfig); UIMessageBus.EmitGlobalSignal(QvMBMessage::UPDATE_COLORSCHEME); if (NeedRestart) From c625d163ff172d518ce8eac50bb92f8d40efb170 Mon Sep 17 00:00:00 2001 From: DuckVador Date: Fri, 15 May 2020 16:06:08 +0800 Subject: [PATCH 096/385] Let's unit-test! --- CMakeLists.txt | 95 +++++++++++++++--------- makespec/BUILDVERSION | 2 +- test/CMakeLists.txt | 23 ++++++ test/src/core/connection/TestParseSS.cpp | 22 ++++++ 4 files changed, 107 insertions(+), 35 deletions(-) create mode 100644 test/CMakeLists.txt create mode 100644 test/src/core/connection/TestParseSS.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 99608306..02cd3a29 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -153,9 +153,34 @@ endif() set(QVPLUGIN_INTERFACE_INCLUDE_DIR "src/components/plugins/interface") include(src/components/plugins/interface/QvPluginInterface.cmake) - -set(QV2RAY_SOURCES - ${QVPLUGIN_INTERFACE_HEADERS} +set(QV2RAY_SOURCES_NO_MAIN + 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/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 3rdparty/libsemver/version.cpp src/base/Qv2rayLog.cpp src/common/CommandArgs.cpp @@ -182,22 +207,6 @@ set(QV2RAY_SOURCES src/components/darkmode/DarkmodeDetector.cpp src/components/ntp/QvNTPClient.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 @@ -273,18 +282,6 @@ set(QV2RAY_SOURCES 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 @@ -307,8 +304,13 @@ set(QV2RAY_SOURCES src/ui/w_PluginManager.hpp src/ui/w_ScreenShot_Core.hpp src/ui/w_GroupManager.hpp + ${QVPLUGIN_INTERFACE_HEADERS} assets/qv2ray.rc ) +set(QV2RAY_SOURCES + ${QV2RAY_SOURCES_NO_MAIN} + src/main.cpp + ) if(EMBED_TRANSLATIONS) add_definitions(-DEMBED_TRANSLATIONS) @@ -331,9 +333,9 @@ set(QT_LIBRARY Qt5::Network ) -add_executable(${PROJECT_NAME} +add_library(${PROJECT_NAME}-lib STATIC ${GUI_TYPE} - ${QV2RAY_SOURCES} + ${QV2RAY_SOURCES_NO_MAIN} ${QNODEEDITOR_SOURCES} ${QNODEEDITOR_QRC_RESOURCES} ${SINGLEAPPLICATION_SOURCES} @@ -346,7 +348,17 @@ add_executable(${PROJECT_NAME} ${QM_FILES} ) +add_executable(${PROJECT_NAME} + src/main.cpp + ) + + + target_link_libraries(${PROJECT_NAME} + ${PROJECT_NAME}-lib + ) + +target_link_libraries(${PROJECT_NAME}-lib ${QT_LIBRARY} ${QV2RAY_PROTOBUF_LIBRARY} ${QV2RAY_BACKEND_LIBRARIES} @@ -355,7 +367,6 @@ target_link_libraries(${PROJECT_NAME} Threads::Threads ) - target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src @@ -368,6 +379,22 @@ target_include_directories(${PROJECT_NAME} PRIVATE ${cpp-httplib_INCLUDE_DIRS} ) +target_include_directories(${PROJECT_NAME}-lib PUBLIC + ${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 (BUILD_TESTING) + add_subdirectory(test) +endif() + if(APPLE) find_package(Iconv REQUIRED) find_library(CARBON NAMES Carbon) diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 1db59029..d59fdea1 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5404 +5415 \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 00000000..a7102339 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,23 @@ +find_package(Qt5Test REQUIRED) +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_AUTOMOC ON) +enable_testing(true) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +function(ADD_QV2RAY_TEST TEST_NAME TEST_SOURCE) + add_executable(${TEST_NAME} ${TEST_SOURCE}) + target_include_directories(${TEST_NAME} + PRIVATE + $ + ) + target_link_libraries( + ${TEST_NAME} + PRIVATE + $<$:${PROJECT_NAME}-lib> + Qt5::Test + ) + +add_test(NAME QV2RAY_TEST_${TEST_NAME} COMMAND $) +endfunction() + +ADD_QV2RAY_TEST(parse_ss_url src/core/connection/TestParseSS.cpp) diff --git a/test/src/core/connection/TestParseSS.cpp b/test/src/core/connection/TestParseSS.cpp new file mode 100644 index 00000000..4517e25b --- /dev/null +++ b/test/src/core/connection/TestParseSS.cpp @@ -0,0 +1,22 @@ +#include +#include "src/core/connection/Serialization.hpp" +#include "libs/QJsonStruct/QJsonIO.hpp" + +class ParseSSUrl: public QObject { + Q_OBJECT +private slots: + void t1() { + using namespace Qv2ray::core::connection::Serialization; + QString err; + QString alias="ssurl1"; + auto c=ss::Deserialize("ss://YmYtY2ZiOnRlc3RAMTkyLjE2OC4xMDAuMTo4ODg4", &alias, &err); + auto s=ShadowSocksServerObject::fromJson(c["outbounds"].toArray().first().toObject()["settings"].toObject()["servers"].toArray().first().toObject()); + QVERIFY(s.address == "192.168.100.1"); + QVERIFY(s.port == 8888); + QVERIFY(s.password=="test"); + QVERIFY(s.method=="bf-cfb"); + } +}; + +QTEST_MAIN(ParseSSUrl) +#include "TestParseSS.moc" From cd778540dd29b9f33d26154c8ee4035e258f6a74 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Fri, 15 May 2020 16:32:34 +0800 Subject: [PATCH 097/385] fix: fixed statsPort missing during config migration --- makespec/BUILDVERSION | 2 +- src/base/models/QvSettingsObject.hpp | 3 ++- src/core/settings/SettingsUpgrade.cpp | 6 +++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 8929f0c2..42cbd559 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5418 \ No newline at end of file +5419 \ No newline at end of file diff --git a/src/base/models/QvSettingsObject.hpp b/src/base/models/QvSettingsObject.hpp index 11c9c9d8..309298ac 100644 --- a/src/base/models/QvSettingsObject.hpp +++ b/src/base/models/QvSettingsObject.hpp @@ -183,7 +183,8 @@ namespace Qv2ray::base::config QString v2CorePath_win; QString v2AssetsPath_win; Qv2rayConfig_Kernel() - : v2CorePath_linux(), v2AssetsPath_linux(), // + : enableAPI(true), statsPort(15490), // + v2CorePath_linux(), v2AssetsPath_linux(), // v2CorePath_macx(), v2AssetsPath_macx(), // v2CorePath_win(), v2AssetsPath_win() // {}; diff --git a/src/core/settings/SettingsUpgrade.cpp b/src/core/settings/SettingsUpgrade.cpp index c8e0059f..5f58a302 100644 --- a/src/core/settings/SettingsUpgrade.cpp +++ b/src/core/settings/SettingsUpgrade.cpp @@ -354,10 +354,10 @@ namespace Qv2ray // Moved apiConfig into kernelConfig auto kernelConfig = root["kernelConfig"].toObject(); - kernelConfig["enableAPI"] = root["enableAPI"]; - kernelConfig["statsPort"] = root["statsPort"]; + kernelConfig["enableAPI"] = root["apiConfig"].toObject()["enableAPI"]; + kernelConfig["statsPort"] = root["apiConfig"].toObject()["statsPort"]; + root["kernelConfig"] = kernelConfig; UPGRADELOG("Finished upgrading config file for Qv2ray Group Routing update.") - break; } default: From 3b06b0702d9544b97b19aa0a8b954aaee0dd642a Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Fri, 15 May 2020 16:41:48 +0800 Subject: [PATCH 098/385] rm: removed QvToolbarConfig from config file --- makespec/BUILDVERSION | 2 +- src/base/models/QvSettingsObject.hpp | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 42cbd559..21d8bad5 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5419 \ No newline at end of file +5420 diff --git a/src/base/models/QvSettingsObject.hpp b/src/base/models/QvSettingsObject.hpp index 309298ac..16890ec5 100644 --- a/src/base/models/QvSettingsObject.hpp +++ b/src/base/models/QvSettingsObject.hpp @@ -285,7 +285,6 @@ namespace Qv2ray::base::config Qv2rayConfig_Kernel kernelConfig; Qv2rayConfig_Update updateConfig; Qv2rayConfig_Network networkConfig; - Qv2rayConfig_ToolBar toolBarConfig; Qv2rayConfig_Inbounds inboundConfig; Qv2rayConfig_Outbounds outboundConfig; Qv2rayConfig_Advanced advancedConfig; @@ -302,7 +301,6 @@ namespace Qv2ray::base::config kernelConfig(), // updateConfig(), // networkConfig(), // - toolBarConfig(), // inboundConfig(), // outboundConfig(), // advancedConfig(), // @@ -311,7 +309,6 @@ namespace Qv2ray::base::config JSONSTRUCT_REGISTER(Qv2rayConfigObject, // F(config_version, tProxySupport, autoStartId, autoStartBehavior, logLevel), // F(uiConfig, advancedConfig, pluginConfig, updateConfig, kernelConfig, networkConfig), // - F(inboundConfig, outboundConfig, connectionConfig), // - F(toolBarConfig)) + F(inboundConfig, outboundConfig, connectionConfig)) }; } // namespace Qv2ray::base::config From e8ab89c9c0071a1791cf79830ab32178a4acce57 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Fri, 15 May 2020 16:53:18 +0800 Subject: [PATCH 099/385] rm: fully removed QvNetworkToolbar --- CMakeLists.txt | 4 - makespec/BUILDVERSION | 2 +- src/base/models/QvSettingsObject.hpp | 28 -- src/base/models/QvStartupConfig.hpp | 2 - src/common/CommandArgs.cpp | 8 - src/common/CommandArgs.hpp | 1 - src/components/plugins/toolbar/QvToolbar.cpp | 179 -------- src/components/plugins/toolbar/QvToolbar.hpp | 57 --- .../plugins/toolbar/QvToolbar_linux.cpp | 97 ----- .../plugins/toolbar/QvToolbar_win.cpp | 136 ------ src/ui/w_MainWindow.cpp | 13 - src/ui/w_PreferencesWindow.cpp | 301 ------------- src/ui/w_PreferencesWindow.hpp | 46 +- src/ui/w_PreferencesWindow.ui | 398 ------------------ 14 files changed, 2 insertions(+), 1270 deletions(-) delete mode 100644 src/components/plugins/toolbar/QvToolbar.cpp delete mode 100644 src/components/plugins/toolbar/QvToolbar.hpp delete mode 100644 src/components/plugins/toolbar/QvToolbar_linux.cpp delete mode 100644 src/components/plugins/toolbar/QvToolbar_win.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index f2c9946f..777ad74c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -172,9 +172,6 @@ set(QV2RAY_SOURCES 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/port/QvPortDetector.cpp src/components/proxy/QvProxyConfigurator.cpp @@ -266,7 +263,6 @@ set(QV2RAY_SOURCES 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/port/QvPortDetector.hpp src/components/proxy/QvProxyConfigurator.hpp diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 21d8bad5..fe624966 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5420 +5421 \ No newline at end of file diff --git a/src/base/models/QvSettingsObject.hpp b/src/base/models/QvSettingsObject.hpp index 16890ec5..60cb79e7 100644 --- a/src/base/models/QvSettingsObject.hpp +++ b/src/base/models/QvSettingsObject.hpp @@ -9,34 +9,6 @@ const int QV2RAY_CONFIG_VERSION = 12; namespace Qv2ray::base::config { - struct QvBarLine - { - QString Family; - bool Bold, Italic; - int ColorA, ColorR, ColorG, ColorB; - int ContentType; - double Size; - QString Message; - QvBarLine() - : Family("Consolas"), Bold(true), Italic(false), ColorA(255), ColorR(255), ColorG(255), ColorB(255), ContentType(0), Size(9), - Message(""){}; - JSONSTRUCT_REGISTER(QvBarLine, F(Bold, Italic, ColorA, ColorR, ColorG, ColorB, Size, Family, Message, ContentType)) - }; - - struct QvBarPage - { - int OffsetYpx; - QList Lines; - QvBarPage() : OffsetYpx(5){}; - JSONSTRUCT_REGISTER(QvBarPage, F(OffsetYpx, Lines)) - }; - - struct Qv2rayConfig_ToolBar - { - QList Pages; - JSONSTRUCT_REGISTER(Qv2rayConfig_ToolBar, F(Pages)) - }; - struct Qv2rayConfig_ForwardProxy { bool enableForwardProxy; diff --git a/src/base/models/QvStartupConfig.hpp b/src/base/models/QvStartupConfig.hpp index 8f88f79f..a10d0570 100644 --- a/src/base/models/QvStartupConfig.hpp +++ b/src/base/models/QvStartupConfig.hpp @@ -12,8 +12,6 @@ namespace Qv2ray bool forceRunAsRootUser; /// Enable Debug Log. bool debugLog; - /// Enable Network toolbar plugin. - bool enableToolbarPlguin; /// Disable Qt scale factors support. bool noScaleFactors; /// Disable all plugin features. diff --git a/src/common/CommandArgs.cpp b/src/common/CommandArgs.cpp index 51f24736..f4f7d88f 100644 --- a/src/common/CommandArgs.cpp +++ b/src/common/CommandArgs.cpp @@ -10,7 +10,6 @@ namespace Qv2ray::common 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") { @@ -22,7 +21,6 @@ namespace Qv2ray::common parser.addOption(debugOption); parser.addOption(noScaleFactorOption); parser.addOption(noPluginsOption); - parser.addOption(withToolbarOption); helpOption = parser.addHelpOption(); versionOption = parser.addVersionOption(); } @@ -71,12 +69,6 @@ namespace Qv2ray::common StartupOption.noPlugins = true; } - if (parser.isSet(withToolbarOption)) - { - DEBUG(MODULE_INIT, "withToolbarOption is set.") - StartupOption.enableToolbarPlguin = true; - } - return CommandLineOk; } diff --git a/src/common/CommandArgs.hpp b/src/common/CommandArgs.hpp index dbab70cc..e0217401 100644 --- a/src/common/CommandArgs.hpp +++ b/src/common/CommandArgs.hpp @@ -29,7 +29,6 @@ namespace Qv2ray::common QCommandLineOption debugOption; QCommandLineOption noScaleFactorOption; QCommandLineOption noPluginsOption; - QCommandLineOption withToolbarOption; QCommandLineOption helpOption; QCommandLineOption versionOption; }; diff --git a/src/components/plugins/toolbar/QvToolbar.cpp b/src/components/plugins/toolbar/QvToolbar.cpp deleted file mode 100644 index 613ea5bc..00000000 --- a/src/components/plugins/toolbar/QvToolbar.cpp +++ /dev/null @@ -1,179 +0,0 @@ -#include "components/plugins/toolbar/QvToolbar.hpp" - -#include "common/QvHelpers.hpp" -#include "core/handler/ConfigHandler.hpp" - -#include - -namespace Qv2ray::components::plugins -{ - namespace Toolbar - { - void StopProcessingPlugins() - { - //#ifdef Q_OS_LINUX - // //_linux::StopMessageQThread(); - //#endif - //#ifdef Q_OS_WIN - // //_win::KillNamedPipeThread(); - //#endif - } - - /// Public Function - CALL ONLY ONCE - - /// To start processing plugins' command. - void StartProcessingPlugins() - { - //#ifdef Q_OS_LINUX - // //_linux::StartMessageQThread(); - //#endif - //#ifdef Q_OS_WIN - // //_win::StartNamedPipeThread(); - //#endif - } - QString GetAnswerToRequest(const QString &) - { - // auto req = pchRequest.trimmed(); - // QString reply = "{}"; - - // if (req == "START") - // { - // emit ConnectionManager->RestartConnection(); - // } - // else if (req == "STOP") - // { - // emit ConnectionManager->StopConnection(); - // } - // else if (req == "RESTART") - // { - // emit ConnectionManager->RestartConnection(); - // } - - // auto BarConfig = GlobalConfig.toolBarConfig; - - // for (auto i = 0; i < BarConfig.Pages.size(); i++) - // { - // for (auto j = 0; j < BarConfig.Pages[i].Lines.size(); j++) - // { - //#define CL BarConfig.Pages[i].Lines[j] - - // switch (CL.ContentType) - // { - // case 0: - // { - // // Custom Text - // // We do nothing... - // break; - // } - - // case 101: - // { - // // Current Time - // CL.Message = QTime().currentTime().toString("hh:mm:ss"); - // break; - // } - - // case 102: - // { - // // Current Date - // CL.Message = QDate().currentDate().toString("yyyy-MM-dd"); - // break; - // } - - // case 103: - // { - // // Current Qv2ray Version - // CL.Message = QV2RAY_VERSION_STRING; - // break; - // } - - // case 104: - // { - // // Current Connection Name - // CL.Message = GetDisplayName(KernelInstance->CurrentConnection()); - // break; - // } - - // case 105: - // { - // // Current Connection Status - // CL.Message = KernelInstance->CurrentConnection() == NullConnectionId ? QObject::tr("Not connected") : - // QObject::tr("Connected"); - // break; - // } - - // // case 201: - // //{ - // // // Total upload speed; - // // CL.Message = FormatBytes(get<0>(GetConnectionUsageAmount())) + "/s"; - // // break; - // //} - // // - // // case 202: - // //{ - // // // Total download speed; - // // CL.Message = FormatBytes(vinstance->getAllSpeedDown()) + "/s"; - // // break; - // //} - // // - // // case 203: - // //{ - // // // Upload speed for tag - // // CL.Message = FormatBytes(vinstance->getTagSpeedUp(CL.Message)) + "/s"; - // // break; - // //} - // // - // // case 204: - // //{ - // // // Download speed for tag - // // CL.Message = FormatBytes(vinstance->getTagSpeedDown(CL.Message)) + "/s"; - // // break; - // //} - - // case 301: - // { - // // Total Upload - // CL.Message = FormatBytes(get<0>(GetConnectionUsageAmount(KernelInstance->CurrentConnection()))); - // break; - // } - - // case 302: - // { - // // Total download - // CL.Message = FormatBytes(get<1>(GetConnectionUsageAmount(KernelInstance->CurrentConnection()))); - // break; - // } - - // // case 303: - // //{ - // // // Upload for tag - // // CL.Message = FormatBytes(vinstance->getTagDataUp(CL.Message)); - // // break; - // //} - // // - // // case 304: - // //{ - // // // Download for tag - // // CL.Message = FormatBytes(vinstance->getTagDataDown(CL.Message)); - // // break; - // //} - - // case 305: - // { - // // Connection latency - // CL.Message = QSTRN(GetConnectionLatency(KernelInstance->CurrentConnection())) + " ms"; - // break; - // } - // default: - // { - // CL.Message = "Not Implemented"; - // break; - // } - // } - // } - // } - //#undef CL - // reply = JsonToString(BarConfig.toJson()); - return "Stopped Supporting"; - } - } // namespace Toolbar -} // namespace Qv2ray::components::plugins diff --git a/src/components/plugins/toolbar/QvToolbar.hpp b/src/components/plugins/toolbar/QvToolbar.hpp deleted file mode 100644 index dac1dafa..00000000 --- a/src/components/plugins/toolbar/QvToolbar.hpp +++ /dev/null @@ -1,57 +0,0 @@ -#pragma once - -#include "base/Qv2rayBase.hpp" -#define QV2RAY_NETSPEED_PLUGIN_PIPE_NAME_LINUX "Qv2ray_NetSpeed_Widget_LocalSocket" -#define QV2RAY_NETSPEED_PLUGIN_PIPE_NAME_WIN "\\\\.\\pipe\\qv2ray_desktop_netspeed_toolbar_pipe" - -namespace Qv2ray::components::plugins -{ - namespace Toolbar - { - /// NO NOT CHANGE THE ORDER - static const QMap NetSpeedPluginMessages{ - { 0, QObject::tr("Custom Text") }, - // Current Status - { 101, QObject::tr("Current Time") }, - { 102, QObject::tr("Current Date") }, - { 103, QObject::tr("Current Qv2ray Version") }, - { 104, QObject::tr("Current Connection Name") }, - { 105, QObject::tr("Current Connection Status") }, - // Speeds - { 201, QObject::tr("Total Upload Speed") }, - { 202, QObject::tr("Total Download Speed") }, - //{ 203, QObject::tr("Upload Speed for Specific Tag") }, - //{ 204, QObject::tr("Download Speed for Specific Tag") }, - // Datas - { 301, QObject::tr("Total Uploaded Data") }, - { 302, QObject::tr("Total Downloaded Data") }, - //{ 303, QObject::tr("Uploaded Data for Specific Tag") }, - //{ 304, QObject::tr("Downloaded Data for Specific Tag") } - { 305, QObject::tr("Current Connection Latency") } // - }; - void StartProcessingPlugins(); - void StopProcessingPlugins(); -#ifdef Q_OS_WIN - namespace _win - { - void StartNamedPipeThread(); - void KillNamedPipeThread(); - } // namespace _win -#endif -#ifdef Q_OS_LINUX - namespace _linux - { - // This function is called within a QThread - // Actually it should the entrypoint of a thread. - void StartMessageQThread(); - void StopMessageQThread(); - - } // namespace _linux -#endif - - QString GetAnswerToRequest(const QString &pchRequest); - } // namespace Toolbar -} // namespace Qv2ray::components::plugins - -using namespace Qv2ray::components; -using namespace Qv2ray::components::plugins::Toolbar; diff --git a/src/components/plugins/toolbar/QvToolbar_linux.cpp b/src/components/plugins/toolbar/QvToolbar_linux.cpp deleted file mode 100644 index 314f995a..00000000 --- a/src/components/plugins/toolbar/QvToolbar_linux.cpp +++ /dev/null @@ -1,97 +0,0 @@ -#include -#ifdef Q_OS_LINUX - #include "common/QvHelpers.hpp" - #include "components/plugins/toolbar/QvToolbar.hpp" - - #include - #include -namespace Qv2ray::components::plugins::Toolbar -{ - namespace _linux - { - static QThread *linuxWorkerThread; - static QLocalServer *server; - static volatile bool isExiting = false; - - void qobject_proxy() - { - QLocalSocket *socket = server->nextPendingConnection(); - - if (!socket->waitForConnected() || !socket->waitForReadyRead()) - return; - - try - { - while (!isExiting && socket->isOpen() && socket->isValid() && socket->waitForReadyRead()) - { - // CANNOT PROPERLY READ... - // Temp-ly fixed (but why and how?) - auto in = QString(socket->readAll()); - - if (!isExiting && !in.isEmpty()) - { - auto out = GetAnswerToRequest(in); - // - socket->write(out.toUtf8()); - socket->flush(); - } - else - { - QThread::msleep(200); - } - } - } - catch (...) - { - LOG(MODULE_PLUGINHOST, "Closing a broken socket.") - } - } - void DataMessageQThread() - { - server = new QLocalServer(); - // BUG Sometimes failed to listen due to improper close of last - // session. - bool listening = server->listen(QV2RAY_NETSPEED_PLUGIN_PIPE_NAME_LINUX); - - while (!isExiting && !listening) - { - QThread::msleep(500); - listening = server->listen(QV2RAY_NETSPEED_PLUGIN_PIPE_NAME_LINUX); - } - - bool timeOut = false; - server->setSocketOptions(QLocalServer::WorldAccessOption); - QObject::connect(server, &QLocalServer::newConnection, &qobject_proxy); - - while (!isExiting) - { - bool result = server->waitForNewConnection(5000, &timeOut); - DEBUG(MODULE_PLUGINHOST, "Plugin thread listening failed: " + server->errorString()) - DEBUG(MODULE_PLUGINHOST, - "waitForNewConnection: " + QString(result ? "true" : "false") + ", " + QString(timeOut ? "true" : "false")) - } - - server->close(); - delete server; - } - void StartMessageQThread() - { - linuxWorkerThread = QThread::create(_linux::DataMessageQThread); - linuxWorkerThread->start(); - } - - void StopMessageQThread() - { - isExiting = true; - - if (linuxWorkerThread->isRunning()) - { - LOG(MODULE_PLUGINHOST, "Waiting for linuxWorkerThread to stop.") - linuxWorkerThread->wait(); - } - - delete _linux::linuxWorkerThread; - } - } // namespace _linux -} // namespace Qv2ray::components::plugins::Toolbar -#endif diff --git a/src/components/plugins/toolbar/QvToolbar_win.cpp b/src/components/plugins/toolbar/QvToolbar_win.cpp deleted file mode 100644 index 495a8660..00000000 --- a/src/components/plugins/toolbar/QvToolbar_win.cpp +++ /dev/null @@ -1,136 +0,0 @@ -#include -#ifdef Q_OS_WIN - #include "common/QvHelpers.hpp" - #include "components/plugins/toolbar/QvToolbar.hpp" - - #include -namespace Qv2ray::components::plugins::Toolbar -{ - namespace _win - { - // Private Headers - constexpr int BUFSIZE = 10240; - DWORD WINAPI NamedPipeMasterThread(LPVOID lpvParam); - DWORD WINAPI InstanceThread(LPVOID); - static LPVOID ThreadHandle; - static bool isExiting = false; - - // - void KillNamedPipeThread() - { - isExiting = true; - TerminateThread(ThreadHandle, 0); - } - // - void StartNamedPipeThread() - { - auto hThread = CreateThread(nullptr, 0, NamedPipeMasterThread, nullptr, 0, nullptr); - - if (hThread == nullptr) - { - LOG(MODULE_PLUGINHOST, "CreateThread failed, GLE=" + QSTRN(GetLastError())) - return; - } - else - CloseHandle(hThread); - } - - DWORD WINAPI NamedPipeMasterThread(LPVOID lpvParam) - { - Q_UNUSED(lpvParam) - BOOL fConnected = FALSE; - DWORD dwThreadId = 0; - HANDLE hPipe = INVALID_HANDLE_VALUE; - auto lpszPipename = QString(QV2RAY_NETSPEED_PLUGIN_PIPE_NAME_WIN).toStdWString(); - - while (!isExiting) - { - // printf("Pipe Server: Main thread awaiting client connection - // on %s\n", lpszPipename.c_str()); - hPipe = CreateNamedPipe(lpszPipename.c_str(), PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, - PIPE_UNLIMITED_INSTANCES, BUFSIZE, BUFSIZE, 0, nullptr); - - if (hPipe == INVALID_HANDLE_VALUE) - { - LOG(MODULE_PLUGINHOST, "CreateNamedPipe failed, GLE=" + QSTRN(GetLastError())) - return static_cast(-1); - } - - fConnected = ConnectNamedPipe(hPipe, nullptr) ? true : (GetLastError() == ERROR_PIPE_CONNECTED); - - if (fConnected) - { - LOG(MODULE_PLUGINHOST, "Client connected, creating a processing thread") - ThreadHandle = CreateThread(nullptr, 0, InstanceThread, hPipe, 0, &dwThreadId); - - if (ThreadHandle == nullptr) - { - LOG(MODULE_PLUGINHOST, "CreateThread failed, GLE=" + QSTRN(GetLastError())) - return static_cast(-1); - } - else - CloseHandle(ThreadHandle); - } - else - CloseHandle(hPipe); - } - - return 0; - } - - DWORD WINAPI InstanceThread(LPVOID lpvParam) - { - DWORD cbBytesRead = 0, cbReplyBytes = 0, cbWritten = 0; - BOOL fSuccess = false; - HANDLE hPipe = static_cast(lpvParam); - TCHAR pchRequest[BUFSIZE] = { 0 }; - - while (!isExiting) - { - fSuccess = ReadFile(hPipe, pchRequest, BUFSIZE * sizeof(TCHAR), &cbBytesRead, nullptr); - - if (!fSuccess || cbBytesRead == 0) - { - if (GetLastError() == ERROR_BROKEN_PIPE) - { - LOG(MODULE_PLUGINHOST, "InstanceThread: client disconnected, GLE=" + QSTRN(GetLastError())) - } - else - { - LOG(MODULE_PLUGINHOST, "InstanceThread ReadFile failed, GLE=" + QSTRN(GetLastError())) - } - - break; - } - - auto req = QString::fromStdWString(pchRequest); - QString replyQString = "{}"; - - if (!isExiting) - { - replyQString = GetAnswerToRequest(req); - // - // REPLY as std::string - std::string pchReply = replyQString.toUtf8().constData(); - cbReplyBytes = static_cast(pchReply.length() + 1) * sizeof(CHAR); - // cbReplyBytes = static_cast(replyQString.length() + - // 1) * sizeof(TCHAR); - // - fSuccess = WriteFile(hPipe, pchReply.c_str(), cbReplyBytes, &cbWritten, nullptr); - - if (!fSuccess || cbReplyBytes != cbWritten) - { - LOG(MODULE_PLUGINHOST, "InstanceThread WriteFile failed, GLE=" + QSTRN(GetLastError())) - break; - } - } - } - - FlushFileBuffers(hPipe); - DisconnectNamedPipe(hPipe); - CloseHandle(hPipe); - return 1; - } - } // namespace _win -} // namespace Qv2ray::components::plugins::Toolbar -#endif diff --git a/src/ui/w_MainWindow.cpp b/src/ui/w_MainWindow.cpp index 5dea0cb3..acb5e68a 100644 --- a/src/ui/w_MainWindow.cpp +++ b/src/ui/w_MainWindow.cpp @@ -1,7 +1,6 @@ #include "w_MainWindow.hpp" #include "components/plugins/QvPluginHost.hpp" -#include "components/plugins/toolbar/QvToolbar.hpp" #include "components/proxy/QvProxyConfigurator.hpp" #include "components/update/UpdateChecker.hpp" #include "core/settings/SettingsBackend.hpp" @@ -332,13 +331,6 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) this->show(); // tray_action_ShowHide->setText(needShowWindow ? tr("Hide") : tr("Show")); - // - if (StartupOption.enableToolbarPlguin) - { - LOG(MODULE_UI, "Plugin daemon is enabled.") - StartProcessingPlugins(); - } - CheckSubscriptionsUpdate(); // splitter->setSizes(QList() << 100 << 300); @@ -497,11 +489,6 @@ void MainWindow::ToggleVisibility() void MainWindow::on_actionExit_triggered() { ConnectionManager->StopConnection(); - if (StartupOption.enableToolbarPlguin) - { - StopProcessingPlugins(); - } - ExitQv2ray(); } diff --git a/src/ui/w_PreferencesWindow.cpp b/src/ui/w_PreferencesWindow.cpp index 06489d6f..a415f892 100644 --- a/src/ui/w_PreferencesWindow.cpp +++ b/src/ui/w_PreferencesWindow.cpp @@ -5,7 +5,6 @@ #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" #include "core/kernel/V2rayKernelInteractions.hpp" @@ -42,14 +41,6 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent), Current QvMessageBusConnect(PreferencesWindow); textBrowser->setHtml(StringFromFile(":/assets/credit.html")); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); - // - // Set network Toolbar page state. - networkToolbarPage->setEnabled(StartupOption.enableToolbarPlguin); - - if (!StartupOption.enableToolbarPlguin) - { - networkToolbarInfoLabel->setText(tr("Qv2ray Network Toolbar is disabled and still under test. Add --withToolbarPlugin to enable.")); - } // We add locales auto langs = Qv2rayTranslator->GetAvailableLanguages(); @@ -65,8 +56,6 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent), Current // Set auto start button state SetAutoStartButtonsState(GetLaunchAtLoginStatus()); - // - nsBarContentCombo->addItems(NetSpeedPluginMessages.values()); themeCombo->addItems(StyleManager->AllStyles()); // qvVersion->setText(QV2RAY_VERSION_STRING); @@ -199,28 +188,6 @@ PreferencesWindow::PreferencesWindow(QWidget *parent) : QDialog(parent), Current 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()) + " " + - tr("Item(s)")); - } - - if (CurrentConfig.toolBarConfig.Pages.size() > 0) - { - nsBarPagesList->setCurrentRow(0); - on_nsBarPagesList_currentRowChanged(0); - } - else - { - networkToolbarSettingsFrame->setEnabled(false); - nsBarLinesList->setEnabled(false); - nsBarLineDelBTN->setEnabled(false); - nsBarLineAddBTN->setEnabled(false); - nsBarPageYOffset->setEnabled(false); - } - - CurrentBarPageId = 0; - // // Empty for global config. auto autoStartConnId = CurrentConfig.autoStartId.connectionId; auto autoStartGroupId = CurrentConfig.autoStartId.groupId; @@ -691,274 +658,6 @@ void PreferencesWindow::on_socksUDPIP_textEdited(const QString &arg1) } } -// ------------------- NET SPEED PLUGIN OPERATIONS -// ----------------------------------------------------------------- - -#define CurrentBarPage CurrentConfig.toolBarConfig.Pages[this->CurrentBarPageId] -#define CurrentBarLine CurrentBarPage.Lines[this->CurrentBarLineId] -#define SET_LINE_LIST_TEXT nsBarLinesList->currentItem()->setText(GetBarLineDescription(CurrentBarLine)); - -void PreferencesWindow::on_nsBarPageAddBTN_clicked() -{ - QvBarPage page; - CurrentConfig.toolBarConfig.Pages.push_back(page); - CurrentBarPageId = CurrentConfig.toolBarConfig.Pages.size() - 1; - // Add default line. - QvBarLine line; - CurrentBarPage.Lines.push_back(line); - CurrentBarLineId = 0; - nsBarPagesList->addItem(QSTRN(CurrentBarPageId)); - ShowLineParameters(CurrentBarLine); - LOG(MODULE_UI, "Adding new page Id: " + QSTRN(CurrentBarPageId)) - nsBarPageDelBTN->setEnabled(true); - nsBarLineAddBTN->setEnabled(true); - nsBarLineDelBTN->setEnabled(true); - nsBarLinesList->setEnabled(true); - nsBarPageYOffset->setEnabled(true); - on_nsBarPagesList_currentRowChanged(static_cast(CurrentBarPageId)); - nsBarPagesList->setCurrentRow(static_cast(CurrentBarPageId)); -} - -void PreferencesWindow::on_nsBarPageDelBTN_clicked() -{ - if (nsBarPagesList->currentRow() >= 0) - { - CurrentConfig.toolBarConfig.Pages.removeAt(nsBarPagesList->currentRow()); - nsBarPagesList->takeItem(nsBarPagesList->currentRow()); - - if (nsBarPagesList->count() <= 0) - { - nsBarPageDelBTN->setEnabled(false); - nsBarLineAddBTN->setEnabled(false); - nsBarLineDelBTN->setEnabled(false); - nsBarLinesList->setEnabled(false); - networkToolbarSettingsFrame->setEnabled(false); - nsBarPageYOffset->setEnabled(false); - nsBarLinesList->clear(); - } - } -} - -void PreferencesWindow::on_nsBarPageYOffset_valueChanged(int arg1) -{ - LOADINGCHECK - CurrentBarPage.OffsetYpx = arg1; -} - -void PreferencesWindow::on_nsBarLineAddBTN_clicked() -{ - // WARNING Is it really just this simple? - QvBarLine line; - CurrentBarPage.Lines.push_back(line); - CurrentBarLineId = CurrentBarPage.Lines.size() - 1; - nsBarLinesList->addItem(QSTRN(CurrentBarLineId)); - ShowLineParameters(CurrentBarLine); - nsBarLineDelBTN->setEnabled(true); - LOG(MODULE_UI, "Adding new line Id: " + QSTRN(CurrentBarLineId)) - nsBarLinesList->setCurrentRow(static_cast(CurrentBarPage.Lines.size() - 1)); -} - -void PreferencesWindow::on_nsBarLineDelBTN_clicked() -{ - if (nsBarLinesList->currentRow() >= 0) - { - CurrentBarPage.Lines.removeAt(nsBarLinesList->currentRow()); - nsBarLinesList->takeItem(nsBarLinesList->currentRow()); - CurrentBarLineId = 0; - - if (nsBarLinesList->count() <= 0) - { - networkToolbarSettingsFrame->setEnabled(false); - nsBarLineDelBTN->setEnabled(false); - } - - // TODO Disabling some UI; - } -} - -void PreferencesWindow::on_nsBarPagesList_currentRowChanged(int currentRow) -{ - if (currentRow < 0) - return; - - // Change page. - // We reload the lines - // Set all parameters item to the property of the first line. - CurrentBarPageId = currentRow; - CurrentBarLineId = 0; - nsBarPageYOffset->setValue(CurrentBarPage.OffsetYpx); - nsBarLinesList->clear(); - - if (!CurrentBarPage.Lines.empty()) - { - for (const auto &line : CurrentBarPage.Lines) - { - auto description = GetBarLineDescription(line); - nsBarLinesList->addItem(description); - } - - nsBarLinesList->setCurrentRow(0); - ShowLineParameters(CurrentBarLine); - } - else - { - networkToolbarSettingsFrame->setEnabled(false); - } -} - -void PreferencesWindow::on_nsBarLinesList_currentRowChanged(int currentRow) -{ - if (currentRow < 0) - return; - - CurrentBarLineId = currentRow; - ShowLineParameters(CurrentBarLine); -} - -void PreferencesWindow::on_fontComboBox_currentFontChanged(const QFont &f) -{ - LOADINGCHECK - CurrentBarLine.Family = f.family(); - SET_LINE_LIST_TEXT -} - -void PreferencesWindow::on_nsBarFontBoldCB_stateChanged(int arg1) -{ - LOADINGCHECK - CurrentBarLine.Bold = arg1 == Qt::Checked; - SET_LINE_LIST_TEXT -} - -void PreferencesWindow::on_nsBarFontItalicCB_stateChanged(int arg1) -{ - LOADINGCHECK - CurrentBarLine.Italic = arg1 == Qt::Checked; - SET_LINE_LIST_TEXT -} - -void PreferencesWindow::on_nsBarFontASB_valueChanged(int arg1) -{ - LOADINGCHECK - CurrentBarLine.ColorA = arg1; - ShowLineParameters(CurrentBarLine); - SET_LINE_LIST_TEXT -} - -void PreferencesWindow::on_nsBarFontRSB_valueChanged(int arg1) -{ - LOADINGCHECK - CurrentBarLine.ColorR = arg1; - ShowLineParameters(CurrentBarLine); - SET_LINE_LIST_TEXT -} - -void PreferencesWindow::on_nsBarFontGSB_valueChanged(int arg1) -{ - LOADINGCHECK - CurrentBarLine.ColorG = arg1; - ShowLineParameters(CurrentBarLine); - SET_LINE_LIST_TEXT -} - -void PreferencesWindow::on_nsBarFontBSB_valueChanged(int arg1) -{ - LOADINGCHECK - CurrentBarLine.ColorB = arg1; - ShowLineParameters(CurrentBarLine); - SET_LINE_LIST_TEXT -} - -void PreferencesWindow::on_nsBarFontSizeSB_valueChanged(double arg1) -{ - LOADINGCHECK - CurrentBarLine.Size = arg1; - SET_LINE_LIST_TEXT -} - -QString PreferencesWindow::GetBarLineDescription(const QvBarLine &barLine) -{ - QString result = "Empty"; - result = NetSpeedPluginMessages[barLine.ContentType]; - - if (barLine.ContentType == 0) - { - result += " (" + barLine.Message + ")"; - } - - result = result.append(barLine.Bold ? ", " + tr("Bold") : ""); - result = result.append(barLine.Italic ? ", " + tr("Italic") : ""); - return result; -} - -void PreferencesWindow::ShowLineParameters(QvBarLine &barLine) -{ - finishedLoading = false; - - if (!barLine.Family.isEmpty()) - { - fontComboBox->setCurrentFont(QFont(barLine.Family)); - } - - // Colors - nsBarFontASB->setValue(barLine.ColorA); - nsBarFontBSB->setValue(barLine.ColorB); - nsBarFontGSB->setValue(barLine.ColorG); - nsBarFontRSB->setValue(barLine.ColorR); - // - QColor color = QColor::fromRgb(barLine.ColorR, barLine.ColorG, barLine.ColorB, barLine.ColorA); - QString s(QStringLiteral("background: #") + ((color.red() < 16) ? "0" : "") + QString::number(color.red(), 16) + - ((color.green() < 16) ? "0" : "") + QString::number(color.green(), 16) + ((color.blue() < 16) ? "0" : "") + - QString::number(color.blue(), 16) + ";"); - chooseColorBtn->setStyleSheet(s); - nsBarFontSizeSB->setValue(barLine.Size); - nsBarFontBoldCB->setChecked(barLine.Bold); - nsBarFontItalicCB->setChecked(barLine.Italic); - nsBarContentCombo->setCurrentText(NetSpeedPluginMessages[barLine.ContentType]); - nsBarTagTxt->setText(barLine.Message); - finishedLoading = true; - networkToolbarSettingsFrame->setEnabled(true); -} - -void PreferencesWindow::on_chooseColorBtn_clicked() -{ - LOADINGCHECK - QColorDialog d(QColor::fromRgb(CurrentBarLine.ColorR, CurrentBarLine.ColorG, CurrentBarLine.ColorB, CurrentBarLine.ColorA), this); - d.exec(); - - if (d.result() == QDialog::DialogCode::Accepted) - { - d.selectedColor().getRgb(&CurrentBarLine.ColorR, &CurrentBarLine.ColorG, &CurrentBarLine.ColorB, &CurrentBarLine.ColorA); - ShowLineParameters(CurrentBarLine); - SET_LINE_LIST_TEXT - } -} - -void PreferencesWindow::on_nsBarTagTxt_textEdited(const QString &arg1) -{ - LOADINGCHECK - CurrentBarLine.Message = arg1; - SET_LINE_LIST_TEXT -} - -void PreferencesWindow::on_nsBarContentCombo_currentIndexChanged(const QString &arg1) -{ - LOADINGCHECK - CurrentBarLine.ContentType = NetSpeedPluginMessages.key(arg1); - SET_LINE_LIST_TEXT -} - -void PreferencesWindow::on_applyNSBarSettingsBtn_clicked() -{ - if (QvMessageBoxAsk(this, tr("Apply network toolbar settings"), - tr("All other modified settings will be applied as well after this object.") + NEWLINE + - tr("Do you want to continue?")) == QMessageBox::Yes) - { - auto conf = GlobalConfig; - conf.toolBarConfig = CurrentConfig.toolBarConfig; - SaveGlobalSettings(conf); - } -} - void PreferencesWindow::on_themeCombo_currentTextChanged(const QString &arg1) { LOADINGCHECK diff --git a/src/ui/w_PreferencesWindow.hpp b/src/ui/w_PreferencesWindow.hpp index 7fe50d7c..5a8cfa58 100644 --- a/src/ui/w_PreferencesWindow.hpp +++ b/src/ui/w_PreferencesWindow.hpp @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include "base/Qv2rayBase.hpp" #include "ui/messaging/QvMessageBus.hpp" @@ -72,44 +72,6 @@ class PreferencesWindow void on_socksUDPIP_textEdited(const QString &arg1); - void on_nsBarPageAddBTN_clicked(); - - void on_nsBarPageDelBTN_clicked(); - - void on_nsBarPageYOffset_valueChanged(int arg1); - - void on_nsBarLineAddBTN_clicked(); - - void on_nsBarLineDelBTN_clicked(); - - void on_nsBarPagesList_currentRowChanged(int currentRow); - - void on_nsBarLinesList_currentRowChanged(int currentRow); - - void on_fontComboBox_currentFontChanged(const QFont &f); - - void on_nsBarFontBoldCB_stateChanged(int arg1); - - void on_nsBarFontItalicCB_stateChanged(int arg1); - - void on_nsBarFontASB_valueChanged(int arg1); - - void on_nsBarFontRSB_valueChanged(int arg1); - - void on_nsBarFontGSB_valueChanged(int arg1); - - void on_nsBarFontBSB_valueChanged(int arg1); - - void on_nsBarFontSizeSB_valueChanged(double arg1); - - void on_chooseColorBtn_clicked(); - - void on_nsBarTagTxt_textEdited(const QString &arg1); - - void on_nsBarContentCombo_currentIndexChanged(const QString &arg1); - - void on_applyNSBarSettingsBtn_clicked(); - void on_selectVCoreBtn_clicked(); void on_vCorePathTxt_textEdited(const QString &arg1); @@ -212,12 +174,6 @@ class PreferencesWindow // RouteSettingsMatrixWidget *routeSettingsWidget; void SetAutoStartButtonsState(bool isAutoStart); - // Set ui parameters for a line; - void ShowLineParameters(QvBarLine &line); - QString GetBarLineDescription(const QvBarLine &barLine); - // - int CurrentBarLineId; - int CurrentBarPageId; // bool NeedRestart = false; bool finishedLoading = false; diff --git a/src/ui/w_PreferencesWindow.ui b/src/ui/w_PreferencesWindow.ui index 87a1fe5c..9622baf1 100644 --- a/src/ui/w_PreferencesWindow.ui +++ b/src/ui/w_PreferencesWindow.ui @@ -1391,389 +1391,6 @@ Custom DNS Settings - - - true - - - Network Toolbar Settings - - - - - - You can config how the network speed toolbar looks like in this panel - - - - - - - Items - - - - - - - 0 - 0 - - - - - 26 - 26 - - - - - - - - - - - - - 0 - 0 - - - - - 26 - 26 - - - - + - - - - - - - Page Y Axis Offset - - - - - - - Pages - - - - - - - Lines - - - - - - - 1024 - - - - - - - Qt::Vertical - - - - 20 - 38 - - - - - - - - - 0 - 0 - - - - - 26 - 26 - - - - - - - - - - - - - 0 - 0 - - - - - 26 - 26 - - - - + - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - - - - - true - - - - 75 - true - true - - - - color: red - - - This feature is not stable and no documentation is provided, please use it at your own risk! - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Content - - - - - - Content Type - - - - - - - - - - Text/Tag - - - - - - - - - - - - - - 0 - 0 - - - - Text Style - - - - - - Font - - - - - - - - - - - - Bold - - - - - - - Italic - - - - - - - - - Size - - - - - - - - - - Color - - - - - - - - - A: - - - - - - - G: - - - - - - - 255 - - - - - - - 255 - - - - - - - 255 - - - - - - - 255 - - - - - - - R: - - - - - - - Select Color - - - - - - - B: - - - - - - - - - Style - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Apply Network Speed Bar UI Settings - - - - - - - - About @@ -2122,21 +1739,6 @@ Custom DNS Settings fpUseAuthCB fpUsernameTx fpPasswordTx - nsBarPagesList - nsBarPageAddBTN - nsBarPageDelBTN - nsBarPageYOffset - nsBarLinesList - nsBarLineAddBTN - nsBarLineDelBTN - nsBarContentCombo - nsBarTagTxt - fontComboBox - nsBarFontBoldCB - nsBarFontItalicCB - nsBarFontSizeSB - nsBarFontASB - applyNSBarSettingsBtn From 4e2efe14dc63c59979c13c27e79dae11a45b3991 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Fri, 15 May 2020 17:03:10 +0800 Subject: [PATCH 100/385] fix: prevent crash on subscription update --- makespec/BUILDVERSION | 2 +- src/core/handler/ConfigHandler.cpp | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index fe624966..a5b8ba56 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5421 \ No newline at end of file +5422 diff --git a/src/core/handler/ConfigHandler.cpp b/src/core/handler/ConfigHandler.cpp index b0769eac..9f0a3c20 100644 --- a/src/core/handler/ConfigHandler.cpp +++ b/src/core/handler/ConfigHandler.cpp @@ -570,11 +570,10 @@ namespace Qv2ray::core::handlers // Check if anything left behind (not being updated or changed significantly) LOG(MODULE_CORE_HANDLER, "Removed old connections not have been matched.") - for (auto conn : connectionsOrig) + for (const auto &conn : originalConnections) { LOG(MODULE_CORE_HANDLER, "Removing: " + conn.toString()) - abort(); - // DeleteConnection(conn); + RemoveConnectionFromGroup(conn, id); } // Update the time From cf491bc2ac204eb74745e628469b0a76c21e2eab Mon Sep 17 00:00:00 2001 From: ymshenyu Date: Fri, 15 May 2020 17:14:01 +0800 Subject: [PATCH 101/385] update debian/rules --- debian/rules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/rules b/debian/rules index b102e12d..c9cc8f3e 100755 --- a/debian/rules +++ b/debian/rules @@ -16,6 +16,6 @@ export QT_SELECT := 5 dh $@ --buildsystem=cmake+ninja override_dh_auto_configure: - dh_auto_configure -- -DEMBED_TRANSLATIONS=ON -DCMAKE_BUILD_TYPE=Release + dh_auto_configure -- -DEMBED_TRANSLATIONS=ON override_dh_auto_test: From e5a3494285538053ea85df94c6e1ea94fe766671 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Fri, 15 May 2020 17:38:42 +0800 Subject: [PATCH 102/385] fix: fixed subscription update connection item count check --- libs/QJsonStruct | 2 +- makespec/BUILDVERSION | 2 +- src/core/connection/Serialization.cpp | 19 +-- src/core/handler/ConfigHandler.cpp | 169 ++++++++++++++------------ 4 files changed, 101 insertions(+), 91 deletions(-) diff --git a/libs/QJsonStruct b/libs/QJsonStruct index fb566f7d..9cd669e1 160000 --- a/libs/QJsonStruct +++ b/libs/QJsonStruct @@ -1 +1 @@ -Subproject commit fb566f7dfb43e66c3021f2b58a9c2003cd3050ac +Subproject commit 9cd669e1d1f5e36d2e7bd249ea8a2811a5de1ee6 diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index a5b8ba56..c070ed2a 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5422 +5423 diff --git a/src/core/connection/Serialization.cpp b/src/core/connection/Serialization.cpp index ca236e3e..937cebcb 100644 --- a/src/core/connection/Serialization.cpp +++ b/src/core/connection/Serialization.cpp @@ -5,6 +5,7 @@ #include "components/plugins/QvPluginHost.hpp" #include "core/CoreUtils.hpp" #include "core/handler/ConfigHandler.hpp" +#include "libs/QJsonStruct/QJsonIO.hpp" namespace Qv2ray::core::connection { @@ -17,21 +18,13 @@ namespace Qv2ray::core::connection { auto conf = vmess::Deserialize(link, prefix, errMessage); // - if (GlobalConfig.advancedConfig.setAllowInsecureCiphers || GlobalConfig.advancedConfig.setAllowInsecure) + auto allowIC = GlobalConfig.advancedConfig.setAllowInsecureCiphers; + auto allowI = GlobalConfig.advancedConfig.setAllowInsecure; + if (allowI || allowIC) { - 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; + QJsonIO::SetValue(conf, allowI, "outbounds", 0, "streamSettings", "tlsSettings", "allowInsecure"); + QJsonIO::SetValue(conf, allowIC, "outbounds", 0, "streamSettings", "tlsSettings", "allowInsecureCiphers"); } - // connectionConf.insert(*prefix, conf); } else if (link.startsWith("ss://")) diff --git a/src/core/handler/ConfigHandler.cpp b/src/core/handler/ConfigHandler.cpp index 9f0a3c20..16f6116c 100644 --- a/src/core/handler/ConfigHandler.cpp +++ b/src/core/handler/ConfigHandler.cpp @@ -479,100 +479,117 @@ namespace Qv2ray::core::handlers { return false; } - // List that is holding connection IDs to be updated. - auto subsList = SplitLines(DecodeSubscriptionString(subscriptionData)); + QMultiHash allSubscriptionConnections; // - if (subsList.count() < 5) + // + // + // ====================================================================================== Begin reading subscription { - auto yes = QvMessageBoxAsk( - nullptr, tr("Update Subscription"), - tr("%1 entrie(s) have been found from the subscription source, do you want to continue?").arg(subsList.count())) == - QMessageBox::Yes; - if (!yes) + auto subscriptionLines = SplitLines(DecodeSubscriptionString(subscriptionData)); + for (const auto &line : subscriptionLines) { - return false; + QString __alias; + QString __errMessage; + // Assign a group name, to pass the name check. + QString __groupName = GetDisplayName(id); + auto connectionConfigMap = ConvertConfigFromString(line.trimmed(), &__alias, &__errMessage, &__groupName); + if (!__errMessage.isEmpty()) + LOG(MODULE_SUBSCRIPTION, "Error: " + __errMessage) + for (const auto &val : connectionConfigMap) + { + allSubscriptionConnections.insert(connectionConfigMap.key(val), val); + } + } + if (allSubscriptionConnections.count() < 5) + { + LOG(MODULE_SUBSCRIPTION, "Find a subscription with less than 5 connections.") + if (QvMessageBoxAsk(nullptr, tr("Update Subscription"), + tr("%1 entrie(s) have been found from the subscription source, do you want to continue?") + .arg(allSubscriptionConnections.count())) != QMessageBox::Yes) + + return false; } } + // ====================================================================================== End reading subscription + // + // + // + // ====================================================================================== Begin Connection Data Storage // Anyway, we try our best to preserve the connection id. QMultiMap nameMap; QMultiMap, ConnectionId> typeMap; - for (const auto &conn : groups[id].connections) { - nameMap.insert(GetDisplayName(conn), conn); - auto [protocol, host, port] = GetConnectionInfo(conn); - if (port != 0) + // Store connection type metadata into map. + for (const auto &conn : groups[id].connections) { - typeMap.insert({ protocol, host, port }, conn); - } - } - QDir().mkpath(QV2RAY_CONNECTIONS_DIR); - bool hasErrorOccured = false; - // Copy construct here. - auto connectionsOrig = groups[id].connections; - groups[id].connections.clear(); - // - for (auto vmess : subsList) - { - QString errMessage; - auto ssdGroupName = GetDisplayName(id); - QString __alias; - auto conf = ConvertConfigFromString(vmess.trimmed(), &__alias, &errMessage, &ssdGroupName); - Q_UNUSED(ssdGroupName) - // Things may go wrong when updating a subscription with ssd:// link - for (auto _alias : conf.keys()) - { - for (const auto &config : conf.values(_alias)) + nameMap.insert(GetDisplayName(conn), conn); + const auto [protocol, host, port] = GetConnectionInfo(conn); + if (port != 0) { - if (!errMessage.isEmpty()) - { - LOG(MODULE_SUBSCRIPTION, "Processing a subscription with following error: " + errMessage) - hasErrorOccured = true; - continue; - } - bool canGetOutboundData = false; - // Should not have complex connection we assume. - auto outboundData = GetConnectionInfo(config, &canGetOutboundData); - // - // Begin guessing new ConnectionId - if (nameMap.contains(_alias)) - { - // Just go and save the connection... - LOG(MODULE_CORE_HANDLER, "Reused connection id from name: " + _alias) - auto _conn = nameMap.take(_alias); - groups[id].connections << _conn; - UpdateConnection(_conn, config); - // Remove Connection Id from the list. - connectionsOrig.removeAll(_conn); - typeMap.remove(typeMap.key(_conn)); - } - else if (canGetOutboundData && typeMap.contains(outboundData)) - { - LOG(MODULE_CORE_HANDLER, "Reused connection id from protocol/host/port pair for connection: " + _alias) - auto _conn = typeMap.take(outboundData); - groups[id].connections << _conn; - // Update Connection Properties - UpdateConnection(_conn, config); - RenameConnection(_conn, _alias); - // Remove Connection Id from the list. - connectionsOrig.removeAll(_conn); - nameMap.remove(nameMap.key(_conn)); - } - else - { - // New connection id is required since nothing matched found... - LOG(MODULE_CORE_HANDLER, "Generated new connection id for connection: " + _alias) - CreateConnection(config, _alias, id); - } - // End guessing connectionId + typeMap.insert({ protocol, host, port }, conn); } } } + // ====================================================================================== End Connection Data Storage + // + bool hasErrorOccured = false; + // Copy construct here. + auto originalConnectionIdList = groups[id].connections; + groups[id].connections.clear(); + // + for (const auto &config : allSubscriptionConnections) + { + const auto _alias = allSubscriptionConnections.key(config); + QString errMessage; + + if (!errMessage.isEmpty()) + { + LOG(MODULE_SUBSCRIPTION, "Processing a subscription with following error: " + errMessage) + hasErrorOccured = true; + continue; + } + bool canGetOutboundData = false; + // Should not have complex connection we assume. + auto outboundData = GetConnectionInfo(config, &canGetOutboundData); + // + // ====================================================================================== Begin guessing new ConnectionId + if (nameMap.contains(_alias)) + { + // Just go and save the connection... + LOG(MODULE_CORE_HANDLER, "Reused connection id from name: " + _alias) + auto _conn = nameMap.take(_alias); + groups[id].connections << _conn; + UpdateConnection(_conn, config); + // Remove Connection Id from the list. + originalConnectionIdList.removeAll(_conn); + typeMap.remove(typeMap.key(_conn)); + } + else if (canGetOutboundData && typeMap.contains(outboundData)) + { + LOG(MODULE_CORE_HANDLER, "Reused connection id from protocol/host/port pair for connection: " + _alias) + auto _conn = typeMap.take(outboundData); + groups[id].connections << _conn; + // Update Connection Properties + UpdateConnection(_conn, config); + RenameConnection(_conn, _alias); + // Remove Connection Id from the list. + originalConnectionIdList.removeAll(_conn); + nameMap.remove(nameMap.key(_conn)); + } + else + { + // New connection id is required since nothing matched found... + LOG(MODULE_CORE_HANDLER, "Generated new connection id for connection: " + _alias) + CreateConnection(config, _alias, id); + } + // ====================================================================================== End guessing new ConnectionId + } // Check if anything left behind (not being updated or changed significantly) LOG(MODULE_CORE_HANDLER, "Removed old connections not have been matched.") - for (const auto &conn : originalConnections) + for (const auto &conn : originalConnectionIdList) { - LOG(MODULE_CORE_HANDLER, "Removing: " + conn.toString()) + LOG(MODULE_CORE_HANDLER, "Removing connections not in the new subscription: " + conn.toString()) RemoveConnectionFromGroup(conn, id); } From 9615cbdc8fa1ed53cbc6c0c9f7a399c463cececf Mon Sep 17 00:00:00 2001 From: DuckSoft Date: Fri, 15 May 2020 18:38:23 +0800 Subject: [PATCH 103/385] added vmess v1 upgrader (#609) --- src/core/connection/Serialization_vmess.cpp | 28 ++++++++++++++------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/core/connection/Serialization_vmess.cpp b/src/core/connection/Serialization_vmess.cpp index 8b19cda5..44785d13 100644 --- a/src/core/connection/Serialization_vmess.cpp +++ b/src/core/connection/Serialization_vmess.cpp @@ -52,8 +52,8 @@ namespace Qv2ray::core::connection vmessUriRoot["host"] = transfer.httpSettings.host.join(","); vmessUriRoot["path"] = transfer.httpSettings.path; } - - if(!vmessUriRoot.contains("type") || vmessUriRoot["type"].toString().isEmpty()) + + if (!vmessUriRoot.contains("type") || vmessUriRoot["type"].toString().isEmpty()) { vmessUriRoot["type"] = "none"; } @@ -110,13 +110,6 @@ namespace Qv2ray::core::connection return default; } - // Explicitly don't support v1 vmess links. - if (!vmessConf.contains("v")) - { - *errMessage = QObject::tr("seems like a v1 vmess, we don't support it"); - return default; - } - // -------------------------------------------------------------------------------------- CONFIGROOT root; QString ps, add, id, net, type, host, path, tls; @@ -155,6 +148,23 @@ namespace Qv2ray::core::connection LOG(MODULE_IMPORT, " --> PS: " + ps) \ } \ } + + // vmess v1 upgrader + if (!vmessConf.contains("v")) + { + LOG(MODULE_IMPORT, "Detected deprecated vmess v1. Trying to upgrade...") + if (const auto network = vmessConf["net"].toString(); network == "ws" || network == "h2") + { + const QStringList hostComponents = vmessConf["host"].toString().replace(" ", "").split(";"); + if (const auto nParts = hostComponents.length(); nParts == 1) + vmessConf["path"] = hostComponents[0], vmessConf["host"] = ""; + else if (nParts == 2) + vmessConf["path"] = hostComponents[0], vmessConf["host"] = hostComponents[1]; + else + vmessConf["path"] = "/", vmessConf["host"] = ""; + } + } + // Strict check of VMess protocol, to check if the specified value // is in the correct range. // From 77b902022966f42858a3867899cc06475ae4cf21 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Fri, 15 May 2020 20:34:57 +0800 Subject: [PATCH 104/385] fix: fixed group manager RCM --- makespec/BUILDVERSION | 2 +- src/core/handler/ConfigHandler.cpp | 2 +- src/core/handler/ConfigHandler.hpp | 2 +- src/ui/w_GroupManager.cpp | 22 ++++++++++++---------- 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index c070ed2a..d3b62c28 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5423 +5424 diff --git a/src/core/handler/ConfigHandler.cpp b/src/core/handler/ConfigHandler.cpp index 16f6116c..1d290189 100644 --- a/src/core/handler/ConfigHandler.cpp +++ b/src/core/handler/ConfigHandler.cpp @@ -402,7 +402,7 @@ namespace Qv2ray::core::handlers return result; } - const GroupId QvConfigHandler::CreateGroup(const QString displayName, bool isSubscription) + const GroupId QvConfigHandler::CreateGroup(const QString &displayName, bool isSubscription) { GroupId id(GenerateRandomString()); groups[id].displayName = displayName; diff --git a/src/core/handler/ConfigHandler.hpp b/src/core/handler/ConfigHandler.hpp index 356dd58d..6766bf77 100644 --- a/src/core/handler/ConfigHandler.hpp +++ b/src/core/handler/ConfigHandler.hpp @@ -110,7 +110,7 @@ namespace Qv2ray::core::handlers void StartLatencyTest(const ConnectionId &id); // // Group Operations - const GroupId CreateGroup(const QString displayName, bool isSubscription); + const GroupId CreateGroup(const QString &displayName, bool isSubscription); const std::optional DeleteGroup(const GroupId &id); const std::optional RenameGroup(const GroupId &id, const QString &newName); // const optional DuplicateGroup(const GroupId &id); diff --git a/src/ui/w_GroupManager.cpp b/src/ui/w_GroupManager.cpp index 9849e287..20566c23 100644 --- a/src/ui/w_GroupManager.cpp +++ b/src/ui/w_GroupManager.cpp @@ -49,13 +49,9 @@ GroupManager::GroupManager(QWidget *parent) : QDialog(parent) this->reloadConnectionsList(currentGroupId); // }); // - // Anyway just reload it. - const auto reloadGroupLambda = [&](const ConnectionGroupPair &id) { - if (id.groupId == currentGroupId) - this->reloadConnectionsList(id.groupId); - }; - // connect(ConnectionManager, &QvConfigHandler::OnConnectionCreated, reloadGroupLambda); - // connect(ConnectionManager, &QvConfigHandler::OnConnectionDeleted, reloadGroupLambda); + connect(ConnectionManager, &QvConfigHandler::OnGroupCreated, this, &GroupManager::reloadGroupRCMActions); + connect(ConnectionManager, &QvConfigHandler::OnGroupDeleted, this, &GroupManager::reloadGroupRCMActions); + connect(ConnectionManager, &QvConfigHandler::OnGroupRenamed, this, &GroupManager::reloadGroupRCMActions); // for (const auto &group : ConnectionManager->AllGroups()) { @@ -66,6 +62,12 @@ GroupManager::GroupManager(QWidget *parent) : QDialog(parent) if (groupList->count() > 0) { groupList->setCurrentItem(groupList->item(0)); + on_groupList_itemClicked(groupList->item(0)); + } + else + { + groupInfoGroupBox->setEnabled(false); + tabWidget->setEnabled(false); } reloadGroupRCMActions(); } @@ -253,7 +255,7 @@ GroupManager::~GroupManager() void GroupManager::on_addGroupButton_clicked() { - auto const key = QSTRN(QTime::currentTime().msecsSinceStartOfDay()); + auto const key = tr("New Group") + " - " + GenerateRandomString(5); auto id = ConnectionManager->CreateGroup(key, true); // auto item = new QListWidgetItem(key); @@ -289,6 +291,7 @@ void GroupManager::on_removeGroupButton_clicked() else { groupInfoGroupBox->setEnabled(false); + tabWidget->setEnabled(false); } } } @@ -323,9 +326,8 @@ void GroupManager::on_groupList_itemClicked(QListWidgetItem *item) reloadConnectionsList(currentGroupId); } -void GroupManager::on_groupList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous) +void GroupManager::on_groupList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *) { - Q_UNUSED(previous) on_groupList_itemClicked(current); } From 2d4d41468f507aa767508892055e2904208cc64d Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Fri, 15 May 2020 21:51:14 +0800 Subject: [PATCH 105/385] fix: ok, so --- CMakeLists.txt | 142 ++++++++++++++++++++---------------------- makespec/BUILDVERSION | 2 +- 2 files changed, 69 insertions(+), 75 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 920f28c7..cd28e798 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,9 +5,6 @@ file(STRINGS "${CMAKE_SOURCE_DIR}/makespec/BUILDVERSION" QV2RAY_BUILD_VERSION) file(STRINGS "${CMAKE_SOURCE_DIR}/makespec/VERSIONSUFFIX" QV2RAY_VERSION_SUFFIX) if(NOT CMAKE_BUILD_TYPE STREQUAL "Release") - math(EXPR QV2RAY_BUILD_VERSION "1 + ${QV2RAY_BUILD_VERSION}") - message("Increasing BUILDVERSION") - file(WRITE "${CMAKE_SOURCE_DIR}/makespec/BUILDVERSION" ${QV2RAY_BUILD_VERSION}) add_definitions(-DNODE_DEBUG_DRAWING) endif() @@ -207,6 +204,69 @@ set(QV2RAY_SOURCES_NO_MAIN src/components/darkmode/DarkmodeDetector.cpp src/components/ntp/QvNTPClient.cpp src/components/update/UpdateChecker.cpp + # 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/QvPluginHost.hpp + src/components/port/QvPortDetector.hpp + src/components/proxy/QvProxyConfigurator.hpp + src/components/ntp/QvNTPClient.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 + ${QVPLUGIN_INTERFACE_HEADERS} + assets/qv2ray.rc + ) + +set(QV2RAY_UI_SOURCES + src/main.cpp + 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/styles/StyleManager.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/widgets/InboundSettingsWidget.hpp + src/ui/widgets/ConnectionSettingsWidget.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_GroupManager.hpp src/ui/editors/w_InboundEditor.cpp src/ui/editors/w_JsonEditor.cpp src/ui/editors/w_OutboundEditor.cpp @@ -248,80 +308,16 @@ set(QV2RAY_SOURCES_NO_MAIN 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/QvPluginHost.hpp - src/components/port/QvPortDetector.hpp - src/components/proxy/QvProxyConfigurator.hpp - src/components/ntp/QvNTPClient.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/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/styles/StyleManager.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/widgets/InboundSettingsWidget.hpp - src/ui/widgets/ConnectionSettingsWidget.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_GroupManager.hpp - ${QVPLUGIN_INTERFACE_HEADERS} - assets/qv2ray.rc - ) - -set(QV2RAY_SOURCES - ${QV2RAY_SOURCES_NO_MAIN} - src/main.cpp ) 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) + list(APPEND QV2RAY_UI_SOURCES ${CMAKE_BINARY_DIR}/translations.qrc) endif() add_custom_target(lupdate - COMMAND lupdate ${QV2RAY_SOURCES} -ts ${TRANSLATIONS_TS} -locations none + COMMAND lupdate ${QV2RAY_UI_SOURCES} ${QV2RAY_SOURCES_NO_MAIN} -ts ${TRANSLATIONS_TS} -locations none WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) set_target_properties(lupdate PROPERTIES EXCLUDE_FROM_ALL TRUE) @@ -338,7 +334,6 @@ set(QT_LIBRARY ) add_library(${PROJECT_NAME}-lib STATIC - ${GUI_TYPE} ${QV2RAY_SOURCES_NO_MAIN} ${QNODEEDITOR_SOURCES} ${QNODEEDITOR_QRC_RESOURCES} @@ -353,11 +348,10 @@ add_library(${PROJECT_NAME}-lib STATIC ) add_executable(${PROJECT_NAME} - src/main.cpp + ${GUI_TYPE} + ${QV2RAY_UI_SOURCES} ) - - target_link_libraries(${PROJECT_NAME} ${PROJECT_NAME}-lib ) @@ -396,7 +390,7 @@ target_include_directories(${PROJECT_NAME}-lib PUBLIC ) if (BUILD_TESTING) - add_subdirectory(test) + add_subdirectory(test) endif() if(APPLE) diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index c5485a4b..45288809 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5436 \ No newline at end of file +5444 From c55d60ab21c007d00523bf879cab070298199042 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Fri, 15 May 2020 22:40:21 +0800 Subject: [PATCH 106/385] refactor: rearranged header include --- CMakeLists.txt | 1 - makespec/BUILDVERSION | 2 +- src/components/latency/QvTCPing.hpp | 1 - src/core/CoreSafeTypes.hpp | 25 ------------------ src/core/CoreUtils.hpp | 5 +--- src/core/connection/ConnectionIO.hpp | 1 - src/core/connection/Serialization.hpp | 1 - src/core/handler/ConfigHandler.hpp | 1 - src/core/handler/KernelInstanceHandler.hpp | 1 - src/core/kernel/V2rayKernelInteractions.hpp | 3 +-- src/ui/editors/w_InboundEditor.hpp | 3 +-- src/ui/editors/w_JsonEditor.hpp | 1 - src/ui/editors/w_OutboundEditor.cpp | 3 +-- src/ui/w_GroupManager.hpp | 1 - src/ui/w_ImportConfig.hpp | 4 +-- src/ui/w_MainWindow.cpp | 1 + src/ui/w_MainWindow.hpp | 1 - src/ui/w_PreferencesWindow.hpp | 2 +- src/ui/widgets/ConnectionItemWidget.cpp | 22 ++++++++++++++++ src/ui/widgets/ConnectionItemWidget.hpp | 28 +++------------------ src/ui/widgets/StreamSettingsWidget.hpp | 3 ++- 21 files changed, 36 insertions(+), 74 deletions(-) delete mode 100644 src/core/CoreSafeTypes.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index cd28e798..36d9f0d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -172,7 +172,6 @@ set(QV2RAY_SOURCES_NO_MAIN 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 diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 45288809..406727db 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5444 +5445 diff --git a/src/components/latency/QvTCPing.hpp b/src/components/latency/QvTCPing.hpp index 98bb6411..3a341bf8 100644 --- a/src/components/latency/QvTCPing.hpp +++ b/src/components/latency/QvTCPing.hpp @@ -1,6 +1,5 @@ #pragma once #include "base/Qv2rayBase.hpp" -#include "core/CoreSafeTypes.hpp" namespace Qv2ray::components::tcping { diff --git a/src/core/CoreSafeTypes.hpp b/src/core/CoreSafeTypes.hpp deleted file mode 100644 index bd4860cd..00000000 --- a/src/core/CoreSafeTypes.hpp +++ /dev/null @@ -1,25 +0,0 @@ -//#pragma once -//#include "base/models/QvConfigIdentifier.hpp" - -////#include - -//// namespace Qv2ray::core -////{ -//// template -//// QList StringsToIdList(const QList &strings) -//// { -//// QList list; -//// for (const auto &str : strings) list << IDType(str); -//// return list; -//// } - -//// template -//// QList IdListToStrings(const QList &ids) -//// { -//// QList list; -//// for (const auto &id : ids) list << id.toString(); -//// return list; -//// } -////} // namespace Qv2ray::core - -//// using namespace Qv2ray::core; diff --git a/src/core/CoreUtils.hpp b/src/core/CoreUtils.hpp index 9ca73751..81bc2226 100644 --- a/src/core/CoreUtils.hpp +++ b/src/core/CoreUtils.hpp @@ -1,10 +1,7 @@ -#pragma once +#pragma once #include "base/models/CoreObjectModels.hpp" #include "base/models/QvConfigIdentifier.hpp" #include "base/models/QvSafeType.hpp" -#include "core/CoreSafeTypes.hpp" - -#include namespace Qv2ray::core { diff --git a/src/core/connection/ConnectionIO.hpp b/src/core/connection/ConnectionIO.hpp index 0bb01a4f..09e9c866 100644 --- a/src/core/connection/ConnectionIO.hpp +++ b/src/core/connection/ConnectionIO.hpp @@ -1,6 +1,5 @@ #pragma once #include "base/Qv2rayBase.hpp" -#include "core/CoreSafeTypes.hpp" namespace Qv2ray::core::connection { namespace ConnectionIO diff --git a/src/core/connection/Serialization.hpp b/src/core/connection/Serialization.hpp index 883095be..5803decb 100644 --- a/src/core/connection/Serialization.hpp +++ b/src/core/connection/Serialization.hpp @@ -1,6 +1,5 @@ #pragma once #include "base/Qv2rayBase.hpp" -#include "core/CoreSafeTypes.hpp" namespace Qv2ray::core::connection { diff --git a/src/core/handler/ConfigHandler.hpp b/src/core/handler/ConfigHandler.hpp index 6766bf77..9aa2f6e6 100644 --- a/src/core/handler/ConfigHandler.hpp +++ b/src/core/handler/ConfigHandler.hpp @@ -3,7 +3,6 @@ #include "base/Qv2rayBase.hpp" #include "common/HTTPRequestHelper.hpp" #include "components/latency/QvTCPing.hpp" -#include "core/CoreSafeTypes.hpp" #include "core/CoreUtils.hpp" #include "core/connection/ConnectionIO.hpp" #include "core/handler/KernelInstanceHandler.hpp" diff --git a/src/core/handler/KernelInstanceHandler.hpp b/src/core/handler/KernelInstanceHandler.hpp index 504edfbe..0d430193 100644 --- a/src/core/handler/KernelInstanceHandler.hpp +++ b/src/core/handler/KernelInstanceHandler.hpp @@ -1,6 +1,5 @@ #pragma once #include "components/plugins/QvPluginHost.hpp" -#include "core/CoreSafeTypes.hpp" #include "core/kernel/V2rayKernelInteractions.hpp" #include diff --git a/src/core/kernel/V2rayKernelInteractions.hpp b/src/core/kernel/V2rayKernelInteractions.hpp index bd286772..b1f9eced 100644 --- a/src/core/kernel/V2rayKernelInteractions.hpp +++ b/src/core/kernel/V2rayKernelInteractions.hpp @@ -1,6 +1,5 @@ -#pragma once +#pragma once #include "base/Qv2rayBase.hpp" -#include "core/CoreSafeTypes.hpp" #include "core/kernel/QvKernelABIChecker.hpp" #include diff --git a/src/ui/editors/w_InboundEditor.hpp b/src/ui/editors/w_InboundEditor.hpp index 9e6a8333..8c06b5c9 100644 --- a/src/ui/editors/w_InboundEditor.hpp +++ b/src/ui/editors/w_InboundEditor.hpp @@ -1,11 +1,10 @@ -#pragma once +#pragma once #include "base/Qv2rayBase.hpp" #include "ui/messaging/QvMessageBus.hpp" #include "ui_w_InboundEditor.h" #include -#include #include class InboundEditor diff --git a/src/ui/editors/w_JsonEditor.hpp b/src/ui/editors/w_JsonEditor.hpp index 9d314380..23550397 100644 --- a/src/ui/editors/w_JsonEditor.hpp +++ b/src/ui/editors/w_JsonEditor.hpp @@ -7,7 +7,6 @@ #include "ui_w_JsonEditor.h" #include -#include class JsonEditor : public QDialog diff --git a/src/ui/editors/w_OutboundEditor.cpp b/src/ui/editors/w_OutboundEditor.cpp index 6beeaea5..1ffb94a1 100644 --- a/src/ui/editors/w_OutboundEditor.cpp +++ b/src/ui/editors/w_OutboundEditor.cpp @@ -1,9 +1,8 @@ -#include "w_OutboundEditor.hpp" +#include "w_OutboundEditor.hpp" #include "core/connection/Generation.hpp" #include "ui/editors/w_JsonEditor.hpp" #include "ui/editors/w_RoutesEditor.hpp" -#include "ui/w_MainWindow.hpp" #include #include diff --git a/src/ui/w_GroupManager.hpp b/src/ui/w_GroupManager.hpp index 9b012339..225319e9 100644 --- a/src/ui/w_GroupManager.hpp +++ b/src/ui/w_GroupManager.hpp @@ -1,7 +1,6 @@ #pragma once #include "base/Qv2rayBase.hpp" -#include "core/CoreSafeTypes.hpp" #include "ui/messaging/QvMessageBus.hpp" #include "ui_w_GroupManager.h" diff --git a/src/ui/w_ImportConfig.hpp b/src/ui/w_ImportConfig.hpp index 44273438..e662448c 100644 --- a/src/ui/w_ImportConfig.hpp +++ b/src/ui/w_ImportConfig.hpp @@ -1,12 +1,10 @@ -#pragma once +#pragma once #include "base/Qv2rayBase.hpp" #include "ui/messaging/QvMessageBus.hpp" #include "ui_w_ImportConfig.h" #include -#include -#include class ImportConfigWindow : public QDialog diff --git a/src/ui/w_MainWindow.cpp b/src/ui/w_MainWindow.cpp index acb5e68a..e110b47e 100644 --- a/src/ui/w_MainWindow.cpp +++ b/src/ui/w_MainWindow.cpp @@ -3,6 +3,7 @@ #include "components/plugins/QvPluginHost.hpp" #include "components/proxy/QvProxyConfigurator.hpp" #include "components/update/UpdateChecker.hpp" +#include "core/handler/ConfigHandler.hpp" #include "core/settings/SettingsBackend.hpp" #include "ui/editors/w_JsonEditor.hpp" #include "ui/editors/w_OutboundEditor.hpp" diff --git a/src/ui/w_MainWindow.hpp b/src/ui/w_MainWindow.hpp index 6b2d0780..fc404d95 100644 --- a/src/ui/w_MainWindow.hpp +++ b/src/ui/w_MainWindow.hpp @@ -3,7 +3,6 @@ #include "common/HTTPRequestHelper.hpp" #include "common/LogHighlighter.hpp" #include "components/speedchart/speedwidget.hpp" -#include "core/handler/ConfigHandler.hpp" #include "ui/messaging/QvMessageBus.hpp" #include "ui_w_MainWindow.h" diff --git a/src/ui/w_PreferencesWindow.hpp b/src/ui/w_PreferencesWindow.hpp index 5a8cfa58..9888ab24 100644 --- a/src/ui/w_PreferencesWindow.hpp +++ b/src/ui/w_PreferencesWindow.hpp @@ -2,9 +2,9 @@ #include "base/Qv2rayBase.hpp" #include "ui/messaging/QvMessageBus.hpp" +#include "ui_w_PreferencesWindow.h" #include -#include class RouteSettingsMatrixWidget; diff --git a/src/ui/widgets/ConnectionItemWidget.cpp b/src/ui/widgets/ConnectionItemWidget.cpp index e3ad9e4d..83b008e5 100644 --- a/src/ui/widgets/ConnectionItemWidget.cpp +++ b/src/ui/widgets/ConnectionItemWidget.cpp @@ -1,6 +1,7 @@ #include "ConnectionItemWidget.hpp" #include "common/QvHelpers.hpp" +#include "core/handler/ConfigHandler.hpp" #include @@ -89,6 +90,27 @@ void ConnectionItemWidget::BeginConnection() } } +bool ConnectionItemWidget::NameMatched(const QString &arg) const +{ + auto searchString = arg.toLower(); + auto headerMatched = GetDisplayName(groupId).toLower().contains(arg); + + if (itemType != NODE_ITEM) + { + return headerMatched; + } + else + { + return headerMatched || GetDisplayName(connectionId).toLower().contains(searchString); + } +} + +void ConnectionItemWidget::RecalculateConnectionsCount() +{ + auto connectionCount = ConnectionManager->Connections(groupId).count(); + latencyLabel->setText(QSTRN(connectionCount) + " " + (connectionCount < 2 ? tr("connection") : tr("connections"))); +} + void ConnectionItemWidget::OnConnected(const ConnectionGroupPair &id) { if (id == ConnectionGroupPair{ connectionId, groupId }) diff --git a/src/ui/widgets/ConnectionItemWidget.hpp b/src/ui/widgets/ConnectionItemWidget.hpp index 4683b0c7..cb886ff7 100644 --- a/src/ui/widgets/ConnectionItemWidget.hpp +++ b/src/ui/widgets/ConnectionItemWidget.hpp @@ -1,6 +1,6 @@ #pragma once -#include "core/handler/ConfigHandler.hpp" +#include "base/models/QvConfigIdentifier.hpp" #include "ui_ConnectionItemWidget.h" #include @@ -25,26 +25,10 @@ class ConnectionItemWidget // void BeginRename(); void CancelRename(); - inline bool NameMatched(const QString &arg) - { - auto searchString = arg.toLower(); - auto headerMatched = GetDisplayName(groupId).toLower().contains(arg); - - if (itemType != NODE_ITEM) - { - return headerMatched; - } - else - { - return headerMatched || GetDisplayName(connectionId).toLower().contains(searchString); - } - } + bool NameMatched(const QString &arg) const; inline const ConnectionGroupPair Identifier() const { - ConnectionGroupPair i; - i.connectionId = this->connectionId; - i.groupId = this->groupId; - return i; + return { this->connectionId, this->groupId }; } inline bool IsRenaming() const { @@ -63,11 +47,7 @@ class ConnectionItemWidget void OnLatencyTestStart(const ConnectionId &id); void OnConnectionModified(const ConnectionId &id); void OnLatencyTestFinished(const ConnectionId &id, const uint average); - inline void RecalculateConnectionsCount() - { - auto connectionCount = ConnectionManager->Connections(groupId).count(); - latencyLabel->setText(QSTRN(connectionCount) + " " + (connectionCount < 2 ? tr("connection") : tr("connections"))); - } + void RecalculateConnectionsCount(); void OnConnectionItemRenamed(const ConnectionId &id, const QString &, const QString &newName); void OnGroupItemRenamed(const GroupId &id, const QString &, const QString &newName); void on_doRenameBtn_clicked(); diff --git a/src/ui/widgets/StreamSettingsWidget.hpp b/src/ui/widgets/StreamSettingsWidget.hpp index 8393a9a4..172d89c3 100644 --- a/src/ui/widgets/StreamSettingsWidget.hpp +++ b/src/ui/widgets/StreamSettingsWidget.hpp @@ -1,10 +1,11 @@ #pragma once -#include "QWidget" #include "base/Qv2rayBase.hpp" #include "ui/messaging/QvMessageBus.hpp" #include "ui_StreamSettingsWidget.h" +#include + class StreamSettingsWidget : public QWidget , private Ui::StreamSettingsWidget From d4d39a5180c14829427efa75e8e8a4e6bfdd2ae9 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Fri, 15 May 2020 23:39:23 +0800 Subject: [PATCH 107/385] refactor: now use QJsonIO in tests --- libs/QJsonStruct | 2 +- makespec/BUILDVERSION | 2 +- test/src/core/connection/TestParseSS.cpp | 23 +++++++++++++---------- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/libs/QJsonStruct b/libs/QJsonStruct index 9cd669e1..d2c82df0 160000 --- a/libs/QJsonStruct +++ b/libs/QJsonStruct @@ -1 +1 @@ -Subproject commit 9cd669e1d1f5e36d2e7bd249ea8a2811a5de1ee6 +Subproject commit d2c82df0a82b58c8b1466bb2457e7e2e2138256c diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 406727db..bb2a84ff 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5445 +5446 diff --git a/test/src/core/connection/TestParseSS.cpp b/test/src/core/connection/TestParseSS.cpp index 4517e25b..a26aa256 100644 --- a/test/src/core/connection/TestParseSS.cpp +++ b/test/src/core/connection/TestParseSS.cpp @@ -1,20 +1,23 @@ -#include -#include "src/core/connection/Serialization.hpp" #include "libs/QJsonStruct/QJsonIO.hpp" +#include "src/core/connection/Serialization.hpp" -class ParseSSUrl: public QObject { +#include + +class ParseSSUrl : public QObject +{ Q_OBJECT -private slots: - void t1() { + private slots: + void t1() + { using namespace Qv2ray::core::connection::Serialization; QString err; - QString alias="ssurl1"; - auto c=ss::Deserialize("ss://YmYtY2ZiOnRlc3RAMTkyLjE2OC4xMDAuMTo4ODg4", &alias, &err); - auto s=ShadowSocksServerObject::fromJson(c["outbounds"].toArray().first().toObject()["settings"].toObject()["servers"].toArray().first().toObject()); + QString alias = "ssurl1"; + auto c = ss::Deserialize("ss://YmYtY2ZiOnRlc3RAMTkyLjE2OC4xMDAuMTo4ODg4", &alias, &err); + auto s = ShadowSocksServerObject::fromJson(QJsonIO::GetValue(c, "outbounds", 0, "settings", "servers", 0)); QVERIFY(s.address == "192.168.100.1"); QVERIFY(s.port == 8888); - QVERIFY(s.password=="test"); - QVERIFY(s.method=="bf-cfb"); + QVERIFY(s.password == "test"); + QVERIFY(s.method == "bf-cfb"); } }; From 82e74f44fc1e42fd43581c92e7f10fb9b54d2558 Mon Sep 17 00:00:00 2001 From: Qv2ray-dev <59914293+Qv2ray-dev@users.noreply.github.com> Date: Fri, 15 May 2020 23:55:46 +0800 Subject: [PATCH 108/385] fix: fixed UI assets missing --- CMakeLists.txt | 4 ++-- makespec/BUILDVERSION | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 36d9f0d9..a13b0be9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -335,20 +335,20 @@ set(QT_LIBRARY add_library(${PROJECT_NAME}-lib STATIC ${QV2RAY_SOURCES_NO_MAIN} ${QNODEEDITOR_SOURCES} - ${QNODEEDITOR_QRC_RESOURCES} ${SINGLEAPPLICATION_SOURCES} ${ZXING_SOURCES} ${PROTO_SRCS} ${PROTO_HDRS} ${API_GRPC_SRCS} ${API_PROTO_SRCS} - ${QRC_RESOURCES} ${QM_FILES} ) add_executable(${PROJECT_NAME} ${GUI_TYPE} ${QV2RAY_UI_SOURCES} + ${QRC_RESOURCES} + ${QNODEEDITOR_QRC_RESOURCES} ) target_link_libraries(${PROJECT_NAME} diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index bb2a84ff..9b0ab450 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5446 +5447 From 3c1236d851fb2664d9d67e25b7c25136418cc6b8 Mon Sep 17 00:00:00 2001 From: ymshenyu Date: Sat, 16 May 2020 09:51:30 +0800 Subject: [PATCH 109/385] enable unit test for debian --- debian/rules | 2 -- libs/QJsonStruct | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/debian/rules b/debian/rules index c9cc8f3e..8af92b0b 100755 --- a/debian/rules +++ b/debian/rules @@ -17,5 +17,3 @@ export QT_SELECT := 5 override_dh_auto_configure: dh_auto_configure -- -DEMBED_TRANSLATIONS=ON - -override_dh_auto_test: diff --git a/libs/QJsonStruct b/libs/QJsonStruct index d2c82df0..9cd669e1 160000 --- a/libs/QJsonStruct +++ b/libs/QJsonStruct @@ -1 +1 @@ -Subproject commit d2c82df0a82b58c8b1466bb2457e7e2e2138256c +Subproject commit 9cd669e1d1f5e36d2e7bd249ea8a2811a5de1ee6 From 66368a46aec0f20a87b67b3a7729de04d9455908 Mon Sep 17 00:00:00 2001 From: ymshenyu Date: Sat, 16 May 2020 10:01:47 +0800 Subject: [PATCH 110/385] enable unit test for debian - 1 --- CMakeLists.txt | 1 + debian/rules | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a13b0be9..58dae71c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -117,6 +117,7 @@ set(QV2RAY_DEFAULT_VASSETS_PATH "unset" CACHE STRING "v2ray assets path") 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") +set(BUILD_TESTING OFF CACHE BOOL "Build test") 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") else() diff --git a/debian/rules b/debian/rules index 8af92b0b..8186d3aa 100755 --- a/debian/rules +++ b/debian/rules @@ -16,4 +16,4 @@ export QT_SELECT := 5 dh $@ --buildsystem=cmake+ninja override_dh_auto_configure: - dh_auto_configure -- -DEMBED_TRANSLATIONS=ON + dh_auto_configure -- -DEMBED_TRANSLATIONS=ON BUILD_TESTING=ON From f5d86d46c1378c0ea49c2e719a15a562af1b7a81 Mon Sep 17 00:00:00 2001 From: ymshenyu Date: Sat, 16 May 2020 10:15:53 +0800 Subject: [PATCH 111/385] fix typo --- debian/rules | 2 +- test/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/debian/rules b/debian/rules index 8186d3aa..4d9457f4 100755 --- a/debian/rules +++ b/debian/rules @@ -16,4 +16,4 @@ export QT_SELECT := 5 dh $@ --buildsystem=cmake+ninja override_dh_auto_configure: - dh_auto_configure -- -DEMBED_TRANSLATIONS=ON BUILD_TESTING=ON + dh_auto_configure -- -DEMBED_TRANSLATIONS=ON -DBUILD_TESTING=ON diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a7102339..9185ff43 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,7 +1,7 @@ find_package(Qt5Test REQUIRED) set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOMOC ON) -enable_testing(true) +enable_testing() set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) function(ADD_QV2RAY_TEST TEST_NAME TEST_SOURCE) From ad58cf6b5d4ece03086190305c6a022e532bd3c2 Mon Sep 17 00:00:00 2001 From: DuckVador Date: Sat, 16 May 2020 10:17:24 +0800 Subject: [PATCH 112/385] more unit test --- test/CMakeLists.txt | 1 + test/src/core/connection/TestParseSS.cpp | 25 +++++++++++++++++++++ test/src/core/connection/TestParseVmess.cpp | 17 ++++++++++++++ 3 files changed, 43 insertions(+) create mode 100644 test/src/core/connection/TestParseVmess.cpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a7102339..578df2a5 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -21,3 +21,4 @@ add_test(NAME QV2RAY_TEST_${TEST_NAME} COMMAND $) endfunction() ADD_QV2RAY_TEST(parse_ss_url src/core/connection/TestParseSS.cpp) +ADD_QV2RAY_TEST(parse_vmess_url src/core/connection/TestParseVmess.cpp) diff --git a/test/src/core/connection/TestParseSS.cpp b/test/src/core/connection/TestParseSS.cpp index a26aa256..125bf35c 100644 --- a/test/src/core/connection/TestParseSS.cpp +++ b/test/src/core/connection/TestParseSS.cpp @@ -19,6 +19,31 @@ class ParseSSUrl : public QObject QVERIFY(s.password == "test"); QVERIFY(s.method == "bf-cfb"); } + void t2() + { + using namespace Qv2ray::core::connection::Serialization; + QString err; + QString alias = "ssurl1"; + auto c = ss::Deserialize("ss://YmYtY2ZiOnRlc3RAMTkyLjE2OC4xLjE6ODM4OA==", &alias, &err); + auto s = ShadowSocksServerObject::fromJson(QJsonIO::GetValue(c, "outbounds", 0, "settings", "servers", 0)); + QVERIFY(s.address == "192.168.1.1"); + QVERIFY(s.port == 8388); + QVERIFY(s.password == "test"); + QVERIFY(s.method == "bf-cfb"); + } + void t3() + { + using namespace Qv2ray::core::connection::Serialization; + QString err; + QString alias = "ssurl1"; + auto c = ss::Deserialize(",ss://YmYtY2ZiOnRlc3RAMTkyLjE2OC4xMDAuMTo4ODg4#example-server",&alias, &err); + auto s = ShadowSocksServerObject::fromJson(QJsonIO::GetValue(c, "outbounds", 0, "settings", "servers", 0)); + qDebug()< + +class ParseVmessUrl : public QObject +{ + Q_OBJECT + private slots: + void t1() + { + QVERIFY(true); + } +}; + +QTEST_MAIN(ParseVmessUrl) +#include "TestParseVmess.moc" From 3c7c5c0114bbafcac7ee3471116adebd013aec5c Mon Sep 17 00:00:00 2001 From: ymshenyu Date: Sat, 16 May 2020 10:31:35 +0800 Subject: [PATCH 113/385] include CTest --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 58dae71c..e818d1f9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,8 @@ endif() set(QV2RAY_VERSION_STRING "${QV2RAY_VERSION}${QV2RAY_VERSION_SUFFIX}") project(qv2ray) +include(CTest) + set(VERSION_LIST ${QV2RAY_VERSION}) string(REPLACE "." ";" VERSION_LIST ${VERSION_LIST}) separate_arguments(VERSION_LIST) From c6590aeda46c95336aecf87690f7d303abd216d8 Mon Sep 17 00:00:00 2001 From: DuckVador Date: Sat, 16 May 2020 14:59:30 +0800 Subject: [PATCH 114/385] use catch2 --- test/CMakeLists.txt | 4 +- test/catch.hpp | 17698 ++++++++++++++++++ test/src/core/connection/TestGeneration.cpp | 4 + test/src/core/connection/TestParseSS.cpp | 84 +- test/src/core/connection/TestParseVmess.cpp | 17 +- 5 files changed, 17744 insertions(+), 63 deletions(-) create mode 100644 test/catch.hpp create mode 100644 test/src/core/connection/TestGeneration.cpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9234a682..a068c81f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,6 +1,4 @@ -find_package(Qt5Test REQUIRED) set(CMAKE_INCLUDE_CURRENT_DIR ON) -set(CMAKE_AUTOMOC ON) enable_testing() set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -14,7 +12,6 @@ function(ADD_QV2RAY_TEST TEST_NAME TEST_SOURCE) ${TEST_NAME} PRIVATE $<$:${PROJECT_NAME}-lib> - Qt5::Test ) add_test(NAME QV2RAY_TEST_${TEST_NAME} COMMAND $) @@ -22,3 +19,4 @@ endfunction() ADD_QV2RAY_TEST(parse_ss_url src/core/connection/TestParseSS.cpp) ADD_QV2RAY_TEST(parse_vmess_url src/core/connection/TestParseVmess.cpp) +ADD_QV2RAY_TEST(generation src/core/connection/TestGeneration.cpp) diff --git a/test/catch.hpp b/test/catch.hpp new file mode 100644 index 00000000..6beb0ead --- /dev/null +++ b/test/catch.hpp @@ -0,0 +1,17698 @@ +/* + * Catch v2.12.1 + * Generated: 2020-04-21 19:29:20.964532 + * ---------------------------------------------------------- + * This file has been merged from multiple headers. Please don't edit it directly + * Copyright (c) 2020 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +// start catch.hpp + + +#define CATCH_VERSION_MAJOR 2 +#define CATCH_VERSION_MINOR 12 +#define CATCH_VERSION_PATCH 1 + +#ifdef __clang__ +# pragma clang system_header +#elif defined __GNUC__ +# pragma GCC system_header +#endif + +// start catch_suppress_warnings.h + +#ifdef __clang__ +# ifdef __ICC // icpc defines the __clang__ macro +# pragma warning(push) +# pragma warning(disable: 161 1682) +# else // __ICC +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wpadded" +# pragma clang diagnostic ignored "-Wswitch-enum" +# pragma clang diagnostic ignored "-Wcovered-switch-default" +# endif +#elif defined __GNUC__ + // Because REQUIREs trigger GCC's -Wparentheses, and because still + // supported version of g++ have only buggy support for _Pragmas, + // Wparentheses have to be suppressed globally. +# pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details + +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-variable" +# pragma GCC diagnostic ignored "-Wpadded" +#endif +// end catch_suppress_warnings.h +#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) +# define CATCH_IMPL +# define CATCH_CONFIG_ALL_PARTS +#endif + +// In the impl file, we want to have access to all parts of the headers +// Can also be used to sanely support PCHs +#if defined(CATCH_CONFIG_ALL_PARTS) +# define CATCH_CONFIG_EXTERNAL_INTERFACES +# if defined(CATCH_CONFIG_DISABLE_MATCHERS) +# undef CATCH_CONFIG_DISABLE_MATCHERS +# endif +# if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +# endif +#endif + +#if !defined(CATCH_CONFIG_IMPL_ONLY) +// start catch_platform.h + +#ifdef __APPLE__ +# include +# if TARGET_OS_OSX == 1 +# define CATCH_PLATFORM_MAC +# elif TARGET_OS_IPHONE == 1 +# define CATCH_PLATFORM_IPHONE +# endif + +#elif defined(linux) || defined(__linux) || defined(__linux__) +# define CATCH_PLATFORM_LINUX + +#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__) +# define CATCH_PLATFORM_WINDOWS +#endif + +// end catch_platform.h + +#ifdef CATCH_IMPL +# ifndef CLARA_CONFIG_MAIN +# define CLARA_CONFIG_MAIN_NOT_DEFINED +# define CLARA_CONFIG_MAIN +# endif +#endif + +// start catch_user_interfaces.h + +namespace Catch { + unsigned int rngSeed(); +} + +// end catch_user_interfaces.h +// start catch_tag_alias_autoregistrar.h + +// start catch_common.h + +// start catch_compiler_capabilities.h + +// Detect a number of compiler features - by compiler +// The following features are defined: +// +// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? +// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? +// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? +// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled? +// **************** +// Note to maintainers: if new toggles are added please document them +// in configuration.md, too +// **************** + +// In general each macro has a _NO_ form +// (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature. +// Many features, at point of detection, define an _INTERNAL_ macro, so they +// can be combined, en-mass, with the _NO_ forms later. + +#ifdef __cplusplus + +# if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) +# define CATCH_CPP14_OR_GREATER +# endif + +# if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +# define CATCH_CPP17_OR_GREATER +# endif + +#endif + +#if defined(__cpp_lib_uncaught_exceptions) +# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +#endif + +// We have to avoid both ICC and Clang, because they try to mask themselves +// as gcc, and we want only GCC in this block +#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" ) + +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) + +#endif + +#if defined(__clang__) + +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" ) + +// As of this writing, IBM XL's implementation of __builtin_constant_p has a bug +// which results in calls to destructors being emitted for each temporary, +// without a matching initialization. In practice, this can result in something +// like `std::string::~string` being called on an uninitialized value. +// +// For example, this code will likely segfault under IBM XL: +// ``` +// REQUIRE(std::string("12") + "34" == "1234") +// ``` +// +// Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented. +# if !defined(__ibmxl__) +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg) */ +# endif + +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ + _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") + +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) + +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) + +# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" ) + +# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wunused-template\"" ) + +#endif // __clang__ + +//////////////////////////////////////////////////////////////////////////////// +// Assume that non-Windows platforms support posix signals by default +#if !defined(CATCH_PLATFORM_WINDOWS) + #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS +#endif + +//////////////////////////////////////////////////////////////////////////////// +// We know some environments not to support full POSIX signals +#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__) + #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +#endif + +#ifdef __OS400__ +# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +# define CATCH_CONFIG_COLOUR_NONE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Android somehow still does not support std::to_string +#if defined(__ANDROID__) +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING +# define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Not all Windows environments support SEH properly +#if defined(__MINGW32__) +# define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH +#endif + +//////////////////////////////////////////////////////////////////////////////// +// PS4 +#if defined(__ORBIS__) +# define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Cygwin +#ifdef __CYGWIN__ + +// Required for some versions of Cygwin to declare gettimeofday +// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin +# define _BSD_SOURCE +// some versions of cygwin (most) do not support std::to_string. Use the libstd check. +// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813 +# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \ + && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) + +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING + +# endif +#endif // __CYGWIN__ + +//////////////////////////////////////////////////////////////////////////////// +// Visual C++ +#if defined(_MSC_VER) + +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma( warning(pop) ) + +# if _MSC_VER >= 1900 // Visual Studio 2015 or newer +# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +# endif + +// Universal Windows platform does not support SEH +// Or console colours (or console at all...) +# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) +# define CATCH_CONFIG_COLOUR_NONE +# else +# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH +# endif + +// MSVC traditional preprocessor needs some workaround for __VA_ARGS__ +// _MSVC_TRADITIONAL == 0 means new conformant preprocessor +// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor +# if !defined(__clang__) // Handle Clang masquerading for msvc +# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) +# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +# endif // MSVC_TRADITIONAL +# endif // __clang__ + +#endif // _MSC_VER + +#if defined(_REENTRANT) || defined(_MSC_VER) +// Enable async processing, as -pthread is specified or no additional linking is required +# define CATCH_INTERNAL_CONFIG_USE_ASYNC +#endif // _MSC_VER + +//////////////////////////////////////////////////////////////////////////////// +// Check if we are compiled with -fno-exceptions or equivalent +#if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND) +# define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED +#endif + +//////////////////////////////////////////////////////////////////////////////// +// DJGPP +#ifdef __DJGPP__ +# define CATCH_INTERNAL_CONFIG_NO_WCHAR +#endif // __DJGPP__ + +//////////////////////////////////////////////////////////////////////////////// +// Embarcadero C++Build +#if defined(__BORLANDC__) + #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN +#endif + +//////////////////////////////////////////////////////////////////////////////// + +// Use of __COUNTER__ is suppressed during code analysis in +// CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly +// handled by it. +// Otherwise all supported compilers support COUNTER macro, +// but user still might want to turn it off +#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) + #define CATCH_INTERNAL_CONFIG_COUNTER +#endif + +//////////////////////////////////////////////////////////////////////////////// + +// RTX is a special version of Windows that is real time. +// This means that it is detected as Windows, but does not provide +// the same set of capabilities as real Windows does. +#if defined(UNDER_RTSS) || defined(RTX64_BUILD) + #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH + #define CATCH_INTERNAL_CONFIG_NO_ASYNC + #define CATCH_CONFIG_COLOUR_NONE +#endif + +#if !defined(_GLIBCXX_USE_C99_MATH_TR1) +#define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER +#endif + +// Various stdlib support checks that require __has_include +#if defined(__has_include) + // Check if string_view is available and usable + #if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW + #endif + + // Check if optional is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) + + // Check if byte is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # define CATCH_INTERNAL_CONFIG_CPP17_BYTE + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) + + // Check if variant is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # if defined(__clang__) && (__clang_major__ < 8) + // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 + // fix should be in clang 8, workaround in libstdc++ 8.2 + # include + # if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) + # define CATCH_CONFIG_NO_CPP17_VARIANT + # else + # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT + # endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) + # else + # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT + # endif // defined(__clang__) && (__clang_major__ < 8) + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) +#endif // defined(__has_include) + +#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) +# define CATCH_CONFIG_COUNTER +#endif +#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH) +# define CATCH_CONFIG_WINDOWS_SEH +#endif +// This is set by default, because we assume that unix compilers are posix-signal-compatible by default. +#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) +# define CATCH_CONFIG_POSIX_SIGNALS +#endif +// This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions. +#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR) +# define CATCH_CONFIG_WCHAR +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING) +# define CATCH_CONFIG_CPP11_TO_STRING +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL) +# define CATCH_CONFIG_CPP17_OPTIONAL +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) +# define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW) +# define CATCH_CONFIG_CPP17_STRING_VIEW +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT) +# define CATCH_CONFIG_CPP17_VARIANT +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE) +# define CATCH_CONFIG_CPP17_BYTE +#endif + +#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) +# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE +#endif + +#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE) +# define CATCH_CONFIG_NEW_CAPTURE +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +# define CATCH_CONFIG_DISABLE_EXCEPTIONS +#endif + +#if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN) +# define CATCH_CONFIG_POLYFILL_ISNAN +#endif + +#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC) +# define CATCH_CONFIG_USE_ASYNC +#endif + +#if defined(CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_NO_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_ANDROID_LOGWRITE) +# define CATCH_CONFIG_ANDROID_LOGWRITE +#endif + +#if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) +# define CATCH_CONFIG_GLOBAL_NEXTAFTER +#endif + +// Even if we do not think the compiler has that warning, we still have +// to provide a macro that can be used by the code. +#if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION) +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION +#endif +#if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS +#endif + +// The goal of this macro is to avoid evaluation of the arguments, but +// still have the compiler warn on problems inside... +#if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN) +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) +#endif + +#if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10) +# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#elif defined(__clang__) && (__clang_major__ < 5) +# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#endif + +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#endif + +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +#define CATCH_TRY if ((true)) +#define CATCH_CATCH_ALL if ((false)) +#define CATCH_CATCH_ANON(type) if ((false)) +#else +#define CATCH_TRY try +#define CATCH_CATCH_ALL catch (...) +#define CATCH_CATCH_ANON(type) catch (type) +#endif + +#if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) +#define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#endif + +// end catch_compiler_capabilities.h +#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line +#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) +#ifdef CATCH_CONFIG_COUNTER +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) +#else +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) +#endif + +#include +#include +#include + +// We need a dummy global operator<< so we can bring it into Catch namespace later +struct Catch_global_namespace_dummy {}; +std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); + +namespace Catch { + + struct CaseSensitive { enum Choice { + Yes, + No + }; }; + + class NonCopyable { + NonCopyable( NonCopyable const& ) = delete; + NonCopyable( NonCopyable && ) = delete; + NonCopyable& operator = ( NonCopyable const& ) = delete; + NonCopyable& operator = ( NonCopyable && ) = delete; + + protected: + NonCopyable(); + virtual ~NonCopyable(); + }; + + struct SourceLineInfo { + + SourceLineInfo() = delete; + SourceLineInfo( char const* _file, std::size_t _line ) noexcept + : file( _file ), + line( _line ) + {} + + SourceLineInfo( SourceLineInfo const& other ) = default; + SourceLineInfo& operator = ( SourceLineInfo const& ) = default; + SourceLineInfo( SourceLineInfo&& ) noexcept = default; + SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default; + + bool empty() const noexcept { return file[0] == '\0'; } + bool operator == ( SourceLineInfo const& other ) const noexcept; + bool operator < ( SourceLineInfo const& other ) const noexcept; + + char const* file; + std::size_t line; + }; + + std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); + + // Bring in operator<< from global namespace into Catch namespace + // This is necessary because the overload of operator<< above makes + // lookup stop at namespace Catch + using ::operator<<; + + // Use this in variadic streaming macros to allow + // >> +StreamEndStop + // as well as + // >> stuff +StreamEndStop + struct StreamEndStop { + std::string operator+() const; + }; + template + T const& operator + ( T const& value, StreamEndStop ) { + return value; + } +} + +#define CATCH_INTERNAL_LINEINFO \ + ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) + +// end catch_common.h +namespace Catch { + + struct RegistrarForTagAliases { + RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); + }; + +} // end namespace Catch + +#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + +// end catch_tag_alias_autoregistrar.h +// start catch_test_registry.h + +// start catch_interfaces_testcase.h + +#include + +namespace Catch { + + class TestSpec; + + struct ITestInvoker { + virtual void invoke () const = 0; + virtual ~ITestInvoker(); + }; + + class TestCase; + struct IConfig; + + struct ITestCaseRegistry { + virtual ~ITestCaseRegistry(); + virtual std::vector const& getAllTests() const = 0; + virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; + }; + + bool isThrowSafe( TestCase const& testCase, IConfig const& config ); + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); + std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); + std::vector const& getAllTestCasesSorted( IConfig const& config ); + +} + +// end catch_interfaces_testcase.h +// start catch_stringref.h + +#include +#include +#include +#include + +namespace Catch { + + /// A non-owning string class (similar to the forthcoming std::string_view) + /// Note that, because a StringRef may be a substring of another string, + /// it may not be null terminated. + class StringRef { + public: + using size_type = std::size_t; + using const_iterator = const char*; + + private: + static constexpr char const* const s_empty = ""; + + char const* m_start = s_empty; + size_type m_size = 0; + + public: // construction + constexpr StringRef() noexcept = default; + + StringRef( char const* rawChars ) noexcept; + + constexpr StringRef( char const* rawChars, size_type size ) noexcept + : m_start( rawChars ), + m_size( size ) + {} + + StringRef( std::string const& stdString ) noexcept + : m_start( stdString.c_str() ), + m_size( stdString.size() ) + {} + + explicit operator std::string() const { + return std::string(m_start, m_size); + } + + public: // operators + auto operator == ( StringRef const& other ) const noexcept -> bool; + auto operator != (StringRef const& other) const noexcept -> bool { + return !(*this == other); + } + + auto operator[] ( size_type index ) const noexcept -> char { + assert(index < m_size); + return m_start[index]; + } + + public: // named queries + constexpr auto empty() const noexcept -> bool { + return m_size == 0; + } + constexpr auto size() const noexcept -> size_type { + return m_size; + } + + // Returns the current start pointer. If the StringRef is not + // null-terminated, throws std::domain_exception + auto c_str() const -> char const*; + + public: // substrings and searches + // Returns a substring of [start, start + length). + // If start + length > size(), then the substring is [start, size()). + // If start > size(), then the substring is empty. + auto substr( size_type start, size_type length ) const noexcept -> StringRef; + + // Returns the current start pointer. May not be null-terminated. + auto data() const noexcept -> char const*; + + constexpr auto isNullTerminated() const noexcept -> bool { + return m_start[m_size] == '\0'; + } + + public: // iterators + constexpr const_iterator begin() const { return m_start; } + constexpr const_iterator end() const { return m_start + m_size; } + }; + + auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&; + auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; + + constexpr auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { + return StringRef( rawChars, size ); + } +} // namespace Catch + +constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { + return Catch::StringRef( rawChars, size ); +} + +// end catch_stringref.h +// start catch_preprocessor.hpp + + +#define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__ +#define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL3(...) CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL4(...) CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL5(...) CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__))) + +#ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__ +// MSVC needs more evaluations +#define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__))) +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__)) +#else +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__) +#endif + +#define CATCH_REC_END(...) +#define CATCH_REC_OUT + +#define CATCH_EMPTY() +#define CATCH_DEFER(id) id CATCH_EMPTY() + +#define CATCH_REC_GET_END2() 0, CATCH_REC_END +#define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2 +#define CATCH_REC_GET_END(...) CATCH_REC_GET_END1 +#define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT +#define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0) +#define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next) + +#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST2(f, x, peek, ...) f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) + +#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) + +// Applies the function macro `f` to each of the remaining parameters, inserts commas between the results, +// and passes userdata as the first parameter to each invocation, +// e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c) +#define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param) +#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__ +#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__ +#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__) +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) +#else +// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__) +#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1) +#endif + +#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__ +#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name) + +#define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__) + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper()) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)) +#else +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper())) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) +#endif + +#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\ + CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__) + +#define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0) +#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1) +#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2) +#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3) +#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4) +#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5) +#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _4, _5, _6) +#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7) +#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8) +#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9) +#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) + +#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N + +#define INTERNAL_CATCH_TYPE_GEN\ + template struct TypeList {};\ + template\ + constexpr auto get_wrapper() noexcept -> TypeList { return {}; }\ + template class...> struct TemplateTypeList{};\ + template class...Cs>\ + constexpr auto get_wrapper() noexcept -> TemplateTypeList { return {}; }\ + template\ + struct append;\ + template\ + struct rewrap;\ + template class, typename...>\ + struct create;\ + template class, typename>\ + struct convert;\ + \ + template \ + struct append { using type = T; };\ + template< template class L1, typename...E1, template class L2, typename...E2, typename...Rest>\ + struct append, L2, Rest...> { using type = typename append, Rest...>::type; };\ + template< template class L1, typename...E1, typename...Rest>\ + struct append, TypeList, Rest...> { using type = L1; };\ + \ + template< template class Container, template class List, typename...elems>\ + struct rewrap, List> { using type = TypeList>; };\ + template< template class Container, template class List, class...Elems, typename...Elements>\ + struct rewrap, List, Elements...> { using type = typename append>, typename rewrap, Elements...>::type>::type; };\ + \ + template