mirror of
https://github.com/Qv2ray/Qv2ray.git
synced 2025-05-20 02:40:20 +08:00
wip: add: added some pre-connection checks
This commit is contained in:
parent
0750e20a3b
commit
17e302fd96
@ -90,6 +90,7 @@ Qv2rayAddSource(core, handler, ConnectionHandler, cpp, hpp)
|
|||||||
Qv2rayAddSource(core, handler, SubscriptionHandler, cpp)
|
Qv2rayAddSource(core, handler, SubscriptionHandler, cpp)
|
||||||
Qv2rayAddSource(core, handler, V2rayInstanceHandler, cpp, hpp)
|
Qv2rayAddSource(core, handler, V2rayInstanceHandler, cpp, hpp)
|
||||||
Qv2rayAddSource(core, _, CoreUtils, cpp, hpp)
|
Qv2rayAddSource(core, _, CoreUtils, cpp, hpp)
|
||||||
|
Qv2rayAddSource(core, _, CoreSafeTypes, hpp)
|
||||||
Qv2rayAddSource(ui, editors, w_InboundEditor, cpp, hpp, ui)
|
Qv2rayAddSource(ui, editors, w_InboundEditor, cpp, hpp, ui)
|
||||||
Qv2rayAddSource(ui, editors, w_JsonEditor, cpp, hpp, ui)
|
Qv2rayAddSource(ui, editors, w_JsonEditor, cpp, hpp, ui)
|
||||||
Qv2rayAddSource(ui, editors, w_OutboundEditor, cpp, hpp, ui)
|
Qv2rayAddSource(ui, editors, w_OutboundEditor, cpp, hpp, ui)
|
||||||
|
43
src/core/CoreSafeTypes.hpp
Normal file
43
src/core/CoreSafeTypes.hpp
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QHash>
|
||||||
|
#include <QHashFunctions>
|
||||||
|
|
||||||
|
namespace Qv2ray::core
|
||||||
|
{
|
||||||
|
template <typename T>
|
||||||
|
class IDType
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
IDType(const QString &id): m_id(id) {}
|
||||||
|
friend bool operator==(const IDType<T> &lhs, const IDType<T> &rhs)
|
||||||
|
{
|
||||||
|
return lhs.m_id == rhs.m_id;
|
||||||
|
}
|
||||||
|
const QString &toString() const
|
||||||
|
{
|
||||||
|
return m_id;
|
||||||
|
}
|
||||||
|
uint qHash(uint seed) const
|
||||||
|
{
|
||||||
|
return ::qHash(m_id, seed);
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
QString m_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> uint qHash(const IDType<T> &key, uint seed = 0)
|
||||||
|
{
|
||||||
|
return key.qHash(seed);
|
||||||
|
}
|
||||||
|
//
|
||||||
|
class __QvGroup;
|
||||||
|
class __QvConnection;
|
||||||
|
class __QvSubscription;
|
||||||
|
typedef IDType<__QvGroup> GroupId;
|
||||||
|
typedef IDType<__QvConnection> ConnectionId;
|
||||||
|
typedef IDType<__QvSubscription> SubscriptionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
using namespace Qv2ray::core;
|
@ -1,28 +1,28 @@
|
|||||||
#include "base/Qv2rayBase.hpp"
|
#include "base/Qv2rayBase.hpp"
|
||||||
|
#include "core/CoreSafeTypes.hpp"
|
||||||
|
|
||||||
namespace Qv2ray::core::connection
|
namespace Qv2ray::core::connection
|
||||||
{
|
{
|
||||||
namespace ConnectionIO
|
namespace ConnectionIO
|
||||||
{
|
{
|
||||||
QMap<QString, CONFIGROOT> GetRegularConnections(QStringList connections);
|
QMap<ConnectionId, CONFIGROOT> GetGroupConnections(const GroupId &group);
|
||||||
QMap<QString, CONFIGROOT> GetSubscriptionConnection(QString subscription);
|
QMap<ConnectionId, CONFIGROOT> GetSubscriptionConnections(const SubscriptionId &subscription);
|
||||||
QMap<QString, QMap<QString, CONFIGROOT>> GetSubscriptionConnections(QStringList subscriptions);
|
QMap<SubscriptionId, QMap<ConnectionId, CONFIGROOT>> GetSubscriptionConnections(const QList<SubscriptionId> &subscriptions);
|
||||||
//
|
//
|
||||||
// Save Connection Config
|
// Save Connection Config
|
||||||
bool SaveConnectionConfig(CONFIGROOT obj, QString *alias, bool canOverrideExisting);
|
bool SaveConnectionConfig(CONFIGROOT obj, const ConnectionId &id, bool canOverrideExisting);
|
||||||
bool SaveSubscriptionConfig(CONFIGROOT obj, const QString &subscription, QString *name);
|
bool SaveSubscriptionConfig(CONFIGROOT obj, const SubscriptionId &subscription, const ConnectionId &name);
|
||||||
//
|
//
|
||||||
bool RemoveConnection(const QString &alias);
|
bool RemoveConnection(const SubscriptionId &id);
|
||||||
bool RemoveSubscriptionConnection(const QString &subsName, const QString &name);
|
bool RemoveSubscriptionConnection(const SubscriptionId &id, const ConnectionId &name);
|
||||||
//
|
//
|
||||||
bool RenameConnection(const QString &originalName, const QString &newName);
|
bool RenameConnection(const ConnectionId &id, const QString &newName);
|
||||||
bool RenameSubscription(const QString &originalName, const QString &newName);
|
bool RenameSubscription(const SubscriptionId &id, const QString &newName);
|
||||||
|
|
||||||
// File Protocol
|
// File Protocol
|
||||||
CONFIGROOT ConvertConfigFromFile(QString sourceFilePath, bool importComplex);
|
CONFIGROOT ConvertConfigFromFile(QString sourceFilePath, bool importComplex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
using namespace Qv2ray::core;
|
|
||||||
using namespace Qv2ray::core::connection;
|
using namespace Qv2ray::core::connection;
|
||||||
using namespace Qv2ray::core::connection::ConnectionIO;
|
using namespace Qv2ray::core::connection::ConnectionIO;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include "ConnectionHandler.hpp"
|
#include "ConnectionHandler.hpp"
|
||||||
|
#include "core/connection/ConnectionIO.hpp"
|
||||||
|
|
||||||
namespace Qv2ray::core::handlers
|
namespace Qv2ray::core::handlers
|
||||||
{
|
{
|
||||||
@ -14,7 +15,7 @@ namespace Qv2ray::core::handlers
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
ConnectionHandler::ConnectionHandler()
|
QvConnectionHandler::QvConnectionHandler()
|
||||||
{
|
{
|
||||||
DEBUG(MODULE_CORE_HANDLER, "ConnectionHandler Constructor.")
|
DEBUG(MODULE_CORE_HANDLER, "ConnectionHandler Constructor.")
|
||||||
|
|
||||||
@ -34,28 +35,28 @@ namespace Qv2ray::core::handlers
|
|||||||
saveTimerId = startTimer(60000);
|
saveTimerId = startTimer(60000);
|
||||||
}
|
}
|
||||||
|
|
||||||
const QList<GroupId> ConnectionHandler::Groups() const
|
const QList<GroupId> QvConnectionHandler::Groups() const
|
||||||
{
|
{
|
||||||
return groups.keys();
|
return groups.keys();
|
||||||
}
|
}
|
||||||
const QList<SubscriptionId> ConnectionHandler::Subscriptions() const
|
const QList<SubscriptionId> QvConnectionHandler::Subscriptions() const
|
||||||
{
|
{
|
||||||
return subscriptions.keys();
|
return subscriptions.keys();
|
||||||
}
|
}
|
||||||
const QList<ConnectionId> ConnectionHandler::Connections() const
|
const QList<ConnectionId> QvConnectionHandler::Connections() const
|
||||||
{
|
{
|
||||||
return connections.keys();
|
return connections.keys();
|
||||||
}
|
}
|
||||||
const QList<ConnectionId> ConnectionHandler::Connections(const GroupId &groupId) const
|
const QList<ConnectionId> QvConnectionHandler::Connections(const GroupId &groupId) const
|
||||||
{
|
{
|
||||||
return StringsToIdList<ConnectionId>(groups[groupId].connections);
|
return StringsToIdList<ConnectionId>(groups[groupId].connections);
|
||||||
}
|
}
|
||||||
const QList<ConnectionId> ConnectionHandler::Connections(const SubscriptionId &subscriptionId) const
|
const QList<ConnectionId> QvConnectionHandler::Connections(const SubscriptionId &subscriptionId) const
|
||||||
{
|
{
|
||||||
return StringsToIdList<ConnectionId>(subscriptions[subscriptionId].connections);
|
return StringsToIdList<ConnectionId>(subscriptions[subscriptionId].connections);
|
||||||
}
|
}
|
||||||
|
|
||||||
const QvConnectionObject &ConnectionHandler::GetConnection(const ConnectionId &id)
|
const QvConnectionObject &QvConnectionHandler::GetConnection(const ConnectionId &id)
|
||||||
{
|
{
|
||||||
if (!connections.contains(id)) {
|
if (!connections.contains(id)) {
|
||||||
LOG(MODULE_CORE_HANDLER, "Cannot find id: " + id.toString());
|
LOG(MODULE_CORE_HANDLER, "Cannot find id: " + id.toString());
|
||||||
@ -64,16 +65,44 @@ namespace Qv2ray::core::handlers
|
|||||||
return connections[id];
|
return connections[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
const QvGroupObject &ConnectionHandler::GetGroup(const GroupId &id)
|
const QvGroupObject &QvConnectionHandler::GetGroup(const GroupId &id)
|
||||||
{
|
{
|
||||||
return groups[id];
|
return groups[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
const QvSubscriptionObject &ConnectionHandler::GetSubscription(const SubscriptionId &id)
|
const QvSubscriptionObject &QvConnectionHandler::GetSubscription(const SubscriptionId &id)
|
||||||
{
|
{
|
||||||
return subscriptions[id];
|
return subscriptions[id];
|
||||||
}
|
}
|
||||||
ConnectionHandler::~ConnectionHandler()
|
optional<QString> QvConnectionHandler::StartConnection(const SubscriptionId &group, const ConnectionId &id)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
optional<QString> QvConnectionHandler::StartConnection(const GroupId &group, const ConnectionId &id)
|
||||||
|
{
|
||||||
|
//if (!kernelInstance->KernelStarted) {
|
||||||
|
// // Check Selection
|
||||||
|
// if (!connections.contains(id)) {
|
||||||
|
// return tr("No connection selected!") + NEWLINE + tr("Please select a config from the list.");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// bool startFlag = CHTryStartConnection();
|
||||||
|
//
|
||||||
|
// if (startFlag) {
|
||||||
|
// MWTryPingConnection(name);
|
||||||
|
// speedTimerId = startTimer(1000);
|
||||||
|
// pingTimerId = startTimer(60000);
|
||||||
|
// this->hTray.showMessage("Qv2ray", tr("Connected: ") + name, this->windowIcon());
|
||||||
|
// hTray.setToolTip(TRAY_TOOLTIP_PREFIX NEWLINE + tr("Connected: ") + name);
|
||||||
|
// statusLabel->setText(tr("Connected: ") + name);
|
||||||
|
// }
|
||||||
|
//} else {
|
||||||
|
// this->hTray.showMessage("Qv2ray", tr("Already connected to: ") + CurrentConnectionIdentifier.IdentifierString(), this->windowIcon());
|
||||||
|
//}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
QvConnectionHandler::~QvConnectionHandler()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,61 +2,27 @@
|
|||||||
|
|
||||||
#include "base/Qv2rayBase.hpp"
|
#include "base/Qv2rayBase.hpp"
|
||||||
#include "core/kernel/KernelInteractions.hpp"
|
#include "core/kernel/KernelInteractions.hpp"
|
||||||
#include <QHash>
|
#include "core/CoreSafeTypes.hpp"
|
||||||
#include <QHashFunctions>
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
class IDType
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
IDType(const QString &id): m_id(id) {}
|
|
||||||
friend bool operator==(const IDType<T> &lhs, const IDType<T> &rhs)
|
|
||||||
{
|
|
||||||
return lhs.m_id == rhs.m_id;
|
|
||||||
}
|
|
||||||
const QString &toString() const
|
|
||||||
{
|
|
||||||
return m_id;
|
|
||||||
}
|
|
||||||
uint qHash(uint seed) const
|
|
||||||
{
|
|
||||||
return ::qHash(m_id, seed);
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
QString m_id;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T> uint qHash(const IDType<T> &key, uint seed = 0)
|
|
||||||
{
|
|
||||||
return key.qHash(seed);
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Qv2ray::core::handlers
|
namespace Qv2ray::core::handlers
|
||||||
{
|
{
|
||||||
//
|
class QvConnectionHandler : public QObject
|
||||||
class __QvGroup;
|
|
||||||
class __QvConnection;
|
|
||||||
class __QvSubscription;
|
|
||||||
typedef IDType<__QvGroup> GroupId;
|
|
||||||
typedef IDType<__QvConnection> ConnectionId;
|
|
||||||
typedef IDType<__QvSubscription> SubscriptionId;
|
|
||||||
|
|
||||||
class ConnectionHandler : public QObject
|
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit ConnectionHandler();
|
explicit QvConnectionHandler();
|
||||||
~ConnectionHandler();
|
~QvConnectionHandler();
|
||||||
//
|
//
|
||||||
const QList<GroupId> Groups() const;
|
const QList<GroupId> Groups() const;
|
||||||
const QList<ConnectionId> Connections() const;
|
const QList<ConnectionId> Connections() const;
|
||||||
|
const QList<SubscriptionId> Subscriptions() const;
|
||||||
const QList<ConnectionId> Connections(const GroupId &groupId) const;
|
const QList<ConnectionId> Connections(const GroupId &groupId) const;
|
||||||
const QList<ConnectionId> Connections(const SubscriptionId &subscriptionId) const;
|
const QList<ConnectionId> Connections(const SubscriptionId &subscriptionId) const;
|
||||||
const QList<SubscriptionId> Subscriptions() const;
|
|
||||||
public:
|
|
||||||
//
|
//
|
||||||
optional<QString> StartConnection(const ConnectionId &id);
|
optional<QString> StartConnection(const GroupId &group, const ConnectionId &id);
|
||||||
|
optional<QString> StartConnection(const SubscriptionId &subscription, const ConnectionId &id);
|
||||||
optional<QString> StopConnection(const ConnectionId &id);
|
optional<QString> StopConnection(const ConnectionId &id);
|
||||||
|
public:
|
||||||
//
|
//
|
||||||
// Connection Operations.
|
// Connection Operations.
|
||||||
const QvConnectionObject &GetConnection(const ConnectionId &id);
|
const QvConnectionObject &GetConnection(const ConnectionId &id);
|
||||||
@ -70,6 +36,7 @@ namespace Qv2ray::core::handlers
|
|||||||
// Misc Connection Operations
|
// Misc Connection Operations
|
||||||
optional<QString> TestLatency(const ConnectionId &id);
|
optional<QString> TestLatency(const ConnectionId &id);
|
||||||
optional<QString> TestLatency(const GroupId &id);
|
optional<QString> TestLatency(const GroupId &id);
|
||||||
|
optional<QString> TestLatency(const SubscriptionId &id);
|
||||||
optional<QString> TestAllLatency();
|
optional<QString> TestAllLatency();
|
||||||
//
|
//
|
||||||
// Group Operations
|
// Group Operations
|
||||||
@ -110,17 +77,20 @@ namespace Qv2ray::core::handlers
|
|||||||
QHash<ConnectionId, QvConnectionObject> connections;
|
QHash<ConnectionId, QvConnectionObject> connections;
|
||||||
QHash<SubscriptionId, QvSubscriptionObject> subscriptions;
|
QHash<SubscriptionId, QvSubscriptionObject> subscriptions;
|
||||||
//
|
//
|
||||||
QHash<ConnectionId, V2rayKernelInstance> kernelInstances;
|
unique_ptr<V2rayKernelInstance> kernelInstance = make_unique<V2rayKernelInstance>();
|
||||||
|
// We only support one cuncurrent connection currently.
|
||||||
|
//QHash<ConnectionId, V2rayKernelInstance> kernelInstances;
|
||||||
|
//
|
||||||
|
optional<QString> _CHTryStartConnection(const ConnectionId &id);
|
||||||
};
|
};
|
||||||
//
|
//
|
||||||
inline unique_ptr<Qv2ray::core::handlers::ConnectionHandler> connectionHandler = nullptr;
|
inline unique_ptr<QvConnectionHandler> ConnectionHandler = nullptr;
|
||||||
//
|
//
|
||||||
inline void InitialiseConnectionHandler()
|
inline void InitialiseConnectionHandler()
|
||||||
{
|
{
|
||||||
LOG(MODULE_CORE_HANDLER, "Initializing ConnectionHandler...")
|
LOG(MODULE_CORE_HANDLER, "Initializing ConnectionHandler...")
|
||||||
connectionHandler = make_unique<ConnectionHandler>();
|
ConnectionHandler = make_unique<QvConnectionHandler>();
|
||||||
}
|
}
|
||||||
//
|
|
||||||
}
|
}
|
||||||
|
|
||||||
using namespace Qv2ray::core::handlers;
|
using namespace Qv2ray::core::handlers;
|
||||||
|
@ -0,0 +1,56 @@
|
|||||||
|
#include "ConnectionHandler.hpp"
|
||||||
|
#include "core/connection/Generation.hpp"
|
||||||
|
|
||||||
|
optional<QString> QvConnectionHandler::_CHTryStartConnection(const ConnectionId &id)
|
||||||
|
{
|
||||||
|
auto connection = connections[id];
|
||||||
|
//currentFullConfig = GenerateRuntimeConfig(connection);
|
||||||
|
//bool startFlag = this->vinstance->StartConnection(currentFullConfig);
|
||||||
|
//if (startFlag) {
|
||||||
|
// bool usePAC = GlobalConfig.inboundConfig.pacConfig.enablePAC;
|
||||||
|
// bool pacUseSocks = GlobalConfig.inboundConfig.pacConfig.useSocksProxy;
|
||||||
|
// bool httpEnabled = GlobalConfig.inboundConfig.useHTTP;
|
||||||
|
// bool socksEnabled = GlobalConfig.inboundConfig.useSocks;
|
||||||
|
//
|
||||||
|
// if (usePAC) {
|
||||||
|
// bool canStartPAC = true;
|
||||||
|
// QString pacProxyString; // Something like this --> SOCKS5 127.0.0.1:1080; SOCKS 127.0.0.1:1080; DIRECT; http://proxy:8080
|
||||||
|
// auto pacIP = GlobalConfig.inboundConfig.pacConfig.localIP;
|
||||||
|
//
|
||||||
|
// if (pacIP.isEmpty()) {
|
||||||
|
// LOG(MODULE_PROXY, "PAC Local IP is empty, default to 127.0.0.1")
|
||||||
|
// pacIP = "127.0.0.1";
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (pacUseSocks) {
|
||||||
|
// if (socksEnabled) {
|
||||||
|
// pacProxyString = "SOCKS5 " + pacIP + ":" + QSTRN(GlobalConfig.inboundConfig.socks_port);
|
||||||
|
// } else {
|
||||||
|
// LOG(MODULE_UI, "PAC is using SOCKS, but it is not enabled")
|
||||||
|
// QvMessageBoxWarn(this, tr("Configuring PAC"), tr("Could not start PAC server as it is configured to use SOCKS, but it is not enabled"));
|
||||||
|
// canStartPAC = false;
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// if (httpEnabled) {
|
||||||
|
// pacProxyString = "PROXY " + pacIP + ":" + QSTRN(GlobalConfig.inboundConfig.http_port);
|
||||||
|
// } else {
|
||||||
|
// LOG(MODULE_UI, "PAC is using HTTP, but it is not enabled")
|
||||||
|
// QvMessageBoxWarn(this, tr("Configuring PAC"), tr("Could not start PAC server as it is configured to use HTTP, but it is not enabled"));
|
||||||
|
// canStartPAC = false;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (canStartPAC) {
|
||||||
|
// pacServer.SetProxyString(pacProxyString);
|
||||||
|
// pacServer.StartListen();
|
||||||
|
// } else {
|
||||||
|
// LOG(MODULE_PROXY, "Not starting PAC due to previous error.")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (GlobalConfig.inboundConfig.setSystemProxy) {
|
||||||
|
// MWSetSystemProxy();
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
return "startFlag";
|
||||||
|
}
|
@ -407,7 +407,7 @@ int main(int argc, char *argv[])
|
|||||||
LOG(UI, "Using built-in theme.")
|
LOG(UI, "Using built-in theme.")
|
||||||
|
|
||||||
if (confObject.uiConfig.useDarkTheme) {
|
if (confObject.uiConfig.useDarkTheme) {
|
||||||
LOG(UI, " --> Using built-in dark theme.")
|
LOG(MODULE_UI, " --> Using built-in dark theme.")
|
||||||
// From https://forum.qt.io/topic/101391/windows-10-dark-theme/4
|
// From https://forum.qt.io/topic/101391/windows-10-dark-theme/4
|
||||||
_qApp.setStyle("Fusion");
|
_qApp.setStyle("Fusion");
|
||||||
QPalette darkPalette;
|
QPalette darkPalette;
|
||||||
@ -482,7 +482,7 @@ int main(int argc, char *argv[])
|
|||||||
#ifndef QT_DEBUG
|
#ifndef QT_DEBUG
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
QvMessageBoxWarn(nullptr, "ERROR", "There's something wrong happened and Qv2ray will quit now.");
|
QvMessageBoxWarn(nullptr, "ERROR", "There's something wrong happened and Qv2ray will quit now.");
|
||||||
LOG(INIT, "EXCEPTION THROWN: " __FILE__)
|
LOG(MODULE_INIT, "EXCEPTION THROWN: " __FILE__)
|
||||||
return -99;
|
return -99;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,9 +34,7 @@
|
|||||||
|
|
||||||
#define TRAY_TOOLTIP_PREFIX "Qv2ray " QV2RAY_VERSION_STRING
|
#define TRAY_TOOLTIP_PREFIX "Qv2ray " QV2RAY_VERSION_STRING
|
||||||
//
|
//
|
||||||
#define vCoreLogBrowser this->logTextBrowsers[0]
|
#define GetItemWidget(item) (static_cast<ConnectionWidget*>(connectionListWidget->itemWidget(item, 0)))
|
||||||
#define qvAppLogBrowser this->logTextBrowsers[1]
|
|
||||||
#define currentLogBrowser this->logTextBrowsers[currentLogBrowserId]
|
|
||||||
//
|
//
|
||||||
//#define ItemConnectionIdentifier(__item__) (__item__->data(0, Qt::UserRole).value<ConnectionIdentifier>())
|
//#define ItemConnectionIdentifier(__item__) (__item__->data(0, Qt::UserRole).value<ConnectionIdentifier>())
|
||||||
//
|
//
|
||||||
@ -106,20 +104,7 @@ MainWindow::MainWindow(QWidget *parent): QMainWindow(parent)//, vinstance(), hTr
|
|||||||
setupUi(this);
|
setupUi(this);
|
||||||
QvMessageBusConnect(MainWindow);
|
QvMessageBusConnect(MainWindow);
|
||||||
//
|
//
|
||||||
// Two browsers
|
vCoreLogHighlighter = new SyntaxHighlighter(GlobalConfig.uiConfig.useDarkTheme, masterLogBrowser->document());
|
||||||
logTextBrowsers.append(new QTextBrowser());
|
|
||||||
logTextBrowsers.append(new QTextBrowser());
|
|
||||||
vCoreLogBrowser->setFontPointSize(8);
|
|
||||||
vCoreLogBrowser->setReadOnly(true);
|
|
||||||
vCoreLogBrowser->setLineWrapMode(QTextBrowser::LineWrapMode::NoWrap);
|
|
||||||
qvAppLogBrowser->setFontPointSize(8);
|
|
||||||
qvAppLogBrowser->setReadOnly(true);
|
|
||||||
qvAppLogBrowser->setLineWrapMode(QTextBrowser::LineWrapMode::NoWrap);
|
|
||||||
//
|
|
||||||
vCoreLogHighlighter = new SyntaxHighlighter(GlobalConfig.uiConfig.useDarkTheme, vCoreLogBrowser->document());
|
|
||||||
qvAppLogHighlighter = new SyntaxHighlighter(GlobalConfig.uiConfig.useDarkTheme, qvAppLogBrowser->document());
|
|
||||||
currentLogBrowserId = 0;
|
|
||||||
masterLogBrowser->setDocument(currentLogBrowser->document());
|
|
||||||
masterLogBrowser->document()->setDocumentMargin(8);
|
masterLogBrowser->document()->setDocumentMargin(8);
|
||||||
masterLogBrowser->document()->adjustSize();
|
masterLogBrowser->document()->adjustSize();
|
||||||
masterLogBrowser->setLineWrapMode(QTextBrowser::LineWrapMode::NoWrap);
|
masterLogBrowser->setLineWrapMode(QTextBrowser::LineWrapMode::NoWrap);
|
||||||
@ -332,33 +317,33 @@ void MainWindow::VersionUpdate(QByteArray &data)
|
|||||||
void MainWindow::OnConfigListChanged(bool need_restart)
|
void MainWindow::OnConfigListChanged(bool need_restart)
|
||||||
{
|
{
|
||||||
LOG(MODULE_UI, "Loading data...")
|
LOG(MODULE_UI, "Loading data...")
|
||||||
auto groups = connectionHandler->Groups();
|
auto groups = ConnectionHandler->Groups();
|
||||||
|
|
||||||
for (auto group : groups) {
|
for (auto group : groups) {
|
||||||
auto groupItem = new QTreeWidgetItem();
|
auto groupItem = new QTreeWidgetItem();
|
||||||
connectionListWidget->addTopLevelItem(groupItem);
|
connectionListWidget->addTopLevelItem(groupItem);
|
||||||
connectionListWidget->setItemWidget(groupItem, 0, new ConnectionWidget(group, connectionListWidget));
|
connectionListWidget->setItemWidget(groupItem, 0, new ConnectionWidget(group, connectionListWidget));
|
||||||
auto connections = connectionHandler->Connections(group);
|
auto connections = ConnectionHandler->Connections(group);
|
||||||
|
|
||||||
for (auto connection : connections) {
|
for (auto connection : connections) {
|
||||||
auto connectionItem = new QTreeWidgetItem();
|
auto connectionItem = new QTreeWidgetItem();
|
||||||
groupItem->addChild(connectionItem);
|
groupItem->addChild(connectionItem);
|
||||||
connectionListWidget->setItemWidget(connectionItem, 0, new ConnectionWidget(connection, connectionListWidget));
|
connectionListWidget->setItemWidget(connectionItem, 0, new ConnectionWidget(group, connection, connectionListWidget));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto subscriptions = connectionHandler->Subscriptions();
|
auto subscriptions = ConnectionHandler->Subscriptions();
|
||||||
|
|
||||||
for (auto subscription : subscriptions) {
|
for (auto subscription : subscriptions) {
|
||||||
auto subscriptionItem = new QTreeWidgetItem();
|
auto subscriptionItem = new QTreeWidgetItem();
|
||||||
connectionListWidget->addTopLevelItem(subscriptionItem);
|
connectionListWidget->addTopLevelItem(subscriptionItem);
|
||||||
connectionListWidget->setItemWidget(subscriptionItem, 0, new ConnectionWidget(subscription, connectionListWidget));
|
connectionListWidget->setItemWidget(subscriptionItem, 0, new ConnectionWidget(subscription, connectionListWidget));
|
||||||
auto connections = connectionHandler->Connections(subscription);
|
auto connections = ConnectionHandler->Connections(subscription);
|
||||||
|
|
||||||
for (auto connection : connections) {
|
for (auto connection : connections) {
|
||||||
auto connectionItem = new QTreeWidgetItem();
|
auto connectionItem = new QTreeWidgetItem();
|
||||||
subscriptionItem->addChild(connectionItem);
|
subscriptionItem->addChild(connectionItem);
|
||||||
connectionListWidget->setItemWidget(connectionItem, 0, new ConnectionWidget(connection, connectionListWidget));
|
connectionListWidget->setItemWidget(connectionItem, 0, new ConnectionWidget(subscription, connection, connectionListWidget));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -441,12 +426,8 @@ MainWindow::~MainWindow()
|
|||||||
}
|
}
|
||||||
void MainWindow::UpdateVCoreLog(const QString &log)
|
void MainWindow::UpdateVCoreLog(const QString &log)
|
||||||
{
|
{
|
||||||
vCoreLogBrowser->append(log);
|
masterLogBrowser->append(log);
|
||||||
CleanUpLogs(vCoreLogBrowser)
|
CleanUpLogs(masterLogBrowser)
|
||||||
setMasterLogHBar();
|
|
||||||
}
|
|
||||||
void MainWindow::setMasterLogHBar()
|
|
||||||
{
|
|
||||||
auto bar = masterLogBrowser->verticalScrollBar();
|
auto bar = masterLogBrowser->verticalScrollBar();
|
||||||
auto max = bar->maximum();
|
auto max = bar->maximum();
|
||||||
auto val = bar->value();
|
auto val = bar->value();
|
||||||
@ -456,44 +437,6 @@ void MainWindow::setMasterLogHBar()
|
|||||||
}
|
}
|
||||||
void MainWindow::on_startButton_clicked()
|
void MainWindow::on_startButton_clicked()
|
||||||
{
|
{
|
||||||
//if (!vinstance->KernelStarted) {
|
|
||||||
// vCoreLogBrowser->clear();
|
|
||||||
// speedChartView->Clear();
|
|
||||||
//
|
|
||||||
// // Check Selection
|
|
||||||
// if (CurrentConnectionIdentifier.isEmpty()) {
|
|
||||||
// QvMessageBoxWarn(this, tr("No connection selected!"), tr("Please select a config from the list."));
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// auto name = CurrentConnectionIdentifier.IdentifierString();
|
|
||||||
// LOG(VCORE, "Connecting to: " + name)
|
|
||||||
// vCoreLogBrowser->clear();
|
|
||||||
// bool startFlag = MWtryStartConnection();
|
|
||||||
//
|
|
||||||
// if (startFlag) {
|
|
||||||
// MWTryPingConnection(name);
|
|
||||||
// speedTimerId = startTimer(1000);
|
|
||||||
// pingTimerId = startTimer(60000);
|
|
||||||
// this->hTray.showMessage("Qv2ray", tr("Connected: ") + name, this->windowIcon());
|
|
||||||
// hTray.setToolTip(TRAY_TOOLTIP_PREFIX NEWLINE + tr("Connected: ") + name);
|
|
||||||
// statusLabel->setText(tr("Connected: ") + name);
|
|
||||||
// } else {
|
|
||||||
// // If failed, show mainwindow
|
|
||||||
// this->show();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // Menu actions
|
|
||||||
// action_Tray_Start->setEnabled(!startFlag);
|
|
||||||
// action_Tray_Stop->setEnabled(startFlag);
|
|
||||||
// action_Tray_Reconnect->setEnabled(startFlag);
|
|
||||||
// tray_SystemProxyMenu->setEnabled(startFlag);
|
|
||||||
// // Buttons
|
|
||||||
// startButton->setEnabled(!startFlag);
|
|
||||||
// stopButton->setEnabled(startFlag);
|
|
||||||
//} else {
|
|
||||||
// this->hTray.showMessage("Qv2ray", tr("Already connected to: ") + CurrentConnectionIdentifier.IdentifierString(), this->windowIcon());
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_stopButton_clicked()
|
void MainWindow::on_stopButton_clicked()
|
||||||
@ -646,7 +589,7 @@ void MainWindow::on_connectionListWidget_doubleClicked(const QModelIndex &index)
|
|||||||
}
|
}
|
||||||
void MainWindow::on_clearlogButton_clicked()
|
void MainWindow::on_clearlogButton_clicked()
|
||||||
{
|
{
|
||||||
vCoreLogBrowser->clear();
|
masterLogBrowser->clear();
|
||||||
}
|
}
|
||||||
void MainWindow::on_connectionListWidget_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous)
|
void MainWindow::on_connectionListWidget_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous)
|
||||||
{
|
{
|
||||||
@ -1101,3 +1044,13 @@ void MainWindow::on_connectionListWidget_itemSelectionChanged()
|
|||||||
// }
|
// }
|
||||||
//}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::on_connectionListWidget_itemDoubleClicked(QTreeWidgetItem *item, int column)
|
||||||
|
{
|
||||||
|
Q_UNUSED(column)
|
||||||
|
auto widget = GetItemWidget(item);
|
||||||
|
|
||||||
|
if (widget->IsConnection()) {
|
||||||
|
widget->BeginConnection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -59,7 +59,6 @@ class MainWindow : public QMainWindow, Ui::MainWindow
|
|||||||
void on_subsButton_clicked();
|
void on_subsButton_clicked();
|
||||||
//
|
//
|
||||||
void ToggleVisibility();
|
void ToggleVisibility();
|
||||||
void setMasterLogHBar();
|
|
||||||
void VersionUpdate(QByteArray &data);
|
void VersionUpdate(QByteArray &data);
|
||||||
void quit();
|
void quit();
|
||||||
|
|
||||||
@ -80,6 +79,8 @@ class MainWindow : public QMainWindow, Ui::MainWindow
|
|||||||
void on_action_RCM_RenameConnection_triggered();
|
void on_action_RCM_RenameConnection_triggered();
|
||||||
void on_connectionListWidget_itemSelectionChanged();
|
void on_connectionListWidget_itemSelectionChanged();
|
||||||
|
|
||||||
|
void on_connectionListWidget_itemDoubleClicked(QTreeWidgetItem *item, int column);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
//
|
//
|
||||||
void SetEditWidgetEnable(bool enabled);
|
void SetEditWidgetEnable(bool enabled);
|
||||||
@ -106,10 +107,6 @@ class MainWindow : public QMainWindow, Ui::MainWindow
|
|||||||
//PACServer pacServer;
|
//PACServer pacServer;
|
||||||
//QvTCPingModel tcpingHelper;
|
//QvTCPingModel tcpingHelper;
|
||||||
SyntaxHighlighter *vCoreLogHighlighter;
|
SyntaxHighlighter *vCoreLogHighlighter;
|
||||||
SyntaxHighlighter *qvAppLogHighlighter;
|
|
||||||
//
|
|
||||||
QList<QTextBrowser *> logTextBrowsers;
|
|
||||||
int currentLogBrowserId = 0;
|
|
||||||
QTreeWidgetItem *CurrentSelectedItemPtr;
|
QTreeWidgetItem *CurrentSelectedItemPtr;
|
||||||
//
|
//
|
||||||
// Actions in the system tray menu
|
// Actions in the system tray menu
|
||||||
@ -130,7 +127,6 @@ class MainWindow : public QMainWindow, Ui::MainWindow
|
|||||||
// ----------------------------------- Extra Headers For w_MainWindow_extra.cpp Handling V2ray Connectivities.
|
// ----------------------------------- Extra Headers For w_MainWindow_extra.cpp Handling V2ray Connectivities.
|
||||||
bool systemProxyEnabled;
|
bool systemProxyEnabled;
|
||||||
void MWFindAndStartAutoConfig();
|
void MWFindAndStartAutoConfig();
|
||||||
bool MWtryStartConnection();
|
|
||||||
void MWStopConnection();
|
void MWStopConnection();
|
||||||
void MWSetSystemProxy();
|
void MWSetSystemProxy();
|
||||||
void MWClearSystemProxy(bool);
|
void MWClearSystemProxy(bool);
|
||||||
|
@ -132,63 +132,6 @@ void MainWindow::MWSetSystemProxy()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MainWindow::MWtryStartConnection()
|
|
||||||
{
|
|
||||||
//auto connectionRoot = connections[CurrentConnectionIdentifier].config;
|
|
||||||
//currentFullConfig = GenerateRuntimeConfig(connectionRoot);
|
|
||||||
//bool startFlag = this->vinstance->StartConnection(currentFullConfig);
|
|
||||||
//
|
|
||||||
//if (startFlag) {
|
|
||||||
// bool usePAC = GlobalConfig.inboundConfig.pacConfig.enablePAC;
|
|
||||||
// bool pacUseSocks = GlobalConfig.inboundConfig.pacConfig.useSocksProxy;
|
|
||||||
// bool httpEnabled = GlobalConfig.inboundConfig.useHTTP;
|
|
||||||
// bool socksEnabled = GlobalConfig.inboundConfig.useSocks;
|
|
||||||
//
|
|
||||||
// if (usePAC) {
|
|
||||||
// bool canStartPAC = true;
|
|
||||||
// QString pacProxyString; // Something like this --> SOCKS5 127.0.0.1:1080; SOCKS 127.0.0.1:1080; DIRECT; http://proxy:8080
|
|
||||||
// auto pacIP = GlobalConfig.inboundConfig.pacConfig.localIP;
|
|
||||||
//
|
|
||||||
// if (pacIP.isEmpty()) {
|
|
||||||
// LOG(PROXY, "PAC Local IP is empty, default to 127.0.0.1")
|
|
||||||
// pacIP = "127.0.0.1";
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (pacUseSocks) {
|
|
||||||
// if (socksEnabled) {
|
|
||||||
// pacProxyString = "SOCKS5 " + pacIP + ":" + QSTRN(GlobalConfig.inboundConfig.socks_port);
|
|
||||||
// } else {
|
|
||||||
// LOG(UI, "PAC is using SOCKS, but it is not enabled")
|
|
||||||
// QvMessageBoxWarn(this, tr("Configuring PAC"), tr("Could not start PAC server as it is configured to use SOCKS, but it is not enabled"));
|
|
||||||
// canStartPAC = false;
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// if (httpEnabled) {
|
|
||||||
// pacProxyString = "PROXY " + pacIP + ":" + QSTRN(GlobalConfig.inboundConfig.http_port);
|
|
||||||
// } else {
|
|
||||||
// LOG(UI, "PAC is using HTTP, but it is not enabled")
|
|
||||||
// QvMessageBoxWarn(this, tr("Configuring PAC"), tr("Could not start PAC server as it is configured to use HTTP, but it is not enabled"));
|
|
||||||
// canStartPAC = false;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (canStartPAC) {
|
|
||||||
// pacServer.SetProxyString(pacProxyString);
|
|
||||||
// pacServer.StartListen();
|
|
||||||
// } else {
|
|
||||||
// LOG(PROXY, "Not starting PAC due to previous error.")
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (GlobalConfig.inboundConfig.setSystemProxy) {
|
|
||||||
// MWSetSystemProxy();
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//return startFlag;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::MWStopConnection()
|
void MainWindow::MWStopConnection()
|
||||||
{
|
{
|
||||||
//if (systemProxyEnabled) {
|
//if (systemProxyEnabled) {
|
||||||
|
@ -1,7 +1 @@
|
|||||||
#include "ConnectionWidget.hpp"
|
#include "ConnectionWidget.hpp"
|
||||||
#include "QMessageBox"
|
|
||||||
|
|
||||||
ConnectionWidget::~ConnectionWidget()
|
|
||||||
{
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
#include "core/handler/ConnectionHandler.hpp"
|
#include "core/handler/ConnectionHandler.hpp"
|
||||||
|
|
||||||
enum CONNECTION_ITEM_TYPE {
|
enum CONNECTION_ITEM_TYPE {
|
||||||
CONNECTION,
|
INVALID,
|
||||||
GROUP,
|
GROUP,
|
||||||
SUBSCRIPTION
|
SUBSCRIPTION
|
||||||
};
|
};
|
||||||
@ -14,29 +14,58 @@ class ConnectionWidget : public QWidget, private Ui::ConnectionWidget
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
CONNECTION_ITEM_TYPE type;
|
//
|
||||||
explicit ConnectionWidget(const ConnectionId &id, QWidget *parent = nullptr): ConnectionWidget(parent)
|
// ======================================= Initialisation for connection nodes.
|
||||||
{
|
//
|
||||||
type = CONNECTION;
|
explicit ConnectionWidget(const GroupId &group, const ConnectionId &id, QWidget *parent = nullptr): ConnectionWidget(parent)
|
||||||
connectionId = id;
|
|
||||||
//
|
|
||||||
auto connection = connectionHandler->GetConnection(id);
|
|
||||||
connNameLabel->setText(connection.displayName);
|
|
||||||
latencyLabel->setText(tr("Latency: ") + QSTRN(connection.latency) + " " + tr("ms"));
|
|
||||||
}
|
|
||||||
explicit ConnectionWidget(const GroupId &id, QWidget *parent = nullptr) : ConnectionWidget(parent)
|
|
||||||
{
|
{
|
||||||
type = GROUP;
|
type = GROUP;
|
||||||
|
connectionId = id;
|
||||||
|
groupId = group;
|
||||||
|
InitialiseForConnection(id);
|
||||||
|
}
|
||||||
|
explicit ConnectionWidget(const SubscriptionId &group, const ConnectionId &id, QWidget *parent = nullptr): ConnectionWidget(parent)
|
||||||
|
{
|
||||||
|
type = SUBSCRIPTION;
|
||||||
|
connectionId = id;
|
||||||
|
subscriptionId = group;
|
||||||
|
InitialiseForConnection(id);
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// ======================================= Initialisation for root nodes.
|
||||||
|
//
|
||||||
|
explicit ConnectionWidget(const GroupId &id, QWidget *parent = nullptr) : ConnectionWidget(parent)
|
||||||
|
{
|
||||||
|
type = INVALID;
|
||||||
groupId = id;
|
groupId = id;
|
||||||
InitialiseForGroups(connectionHandler->GetGroup(id).displayName, connectionHandler->Connections(id).count());
|
InitialiseForGroup(ConnectionHandler->GetGroup(id).displayName, ConnectionHandler->Connections(id).count());
|
||||||
}
|
}
|
||||||
explicit ConnectionWidget(const SubscriptionId &id, QWidget *parent = nullptr) : ConnectionWidget(parent)
|
explicit ConnectionWidget(const SubscriptionId &id, QWidget *parent = nullptr) : ConnectionWidget(parent)
|
||||||
{
|
{
|
||||||
type = SUBSCRIPTION;
|
type = INVALID;
|
||||||
subscriptionId = id;
|
subscriptionId = id;
|
||||||
InitialiseForGroups(connectionHandler->GetSubscription(id).displayName, connectionHandler->Connections(id).count());
|
InitialiseForGroup(ConnectionHandler->GetSubscription(id).displayName, ConnectionHandler->Connections(id).count());
|
||||||
|
}
|
||||||
|
inline bool IsConnection() const
|
||||||
|
{
|
||||||
|
return isConnectionItem;
|
||||||
|
}
|
||||||
|
void BeginConnection()
|
||||||
|
{
|
||||||
|
if (isConnectionItem) {
|
||||||
|
if (type == GROUP) {
|
||||||
|
ConnectionHandler->StartConnection(groupId, connectionId);
|
||||||
|
} else {
|
||||||
|
ConnectionHandler->StartConnection(subscriptionId, connectionId);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOG(MODULE_UI, "Trying to start a non-connection entry, this call is illegal.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
~ConnectionWidget()
|
||||||
|
{
|
||||||
|
//
|
||||||
}
|
}
|
||||||
~ConnectionWidget();
|
|
||||||
Q_DISABLE_COPY_MOVE(ConnectionWidget)
|
Q_DISABLE_COPY_MOVE(ConnectionWidget)
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -44,9 +73,18 @@ class ConnectionWidget : public QWidget, private Ui::ConnectionWidget
|
|||||||
{
|
{
|
||||||
setupUi(this);
|
setupUi(this);
|
||||||
}
|
}
|
||||||
void InitialiseForGroups(const QString &displayName, int connectionCount)
|
void InitialiseForConnection(const ConnectionId &id)
|
||||||
{
|
{
|
||||||
connNameLabel->setText(displayName);
|
isConnectionItem = true;
|
||||||
|
auto connection = ConnectionHandler->GetConnection(id);
|
||||||
|
rawDisplayName = connection.displayName;
|
||||||
|
connNameLabel->setText(rawDisplayName);
|
||||||
|
latencyLabel->setText(tr("Latency: ") + QSTRN(connection.latency) + " " + tr("ms"));
|
||||||
|
}
|
||||||
|
void InitialiseForGroup(const QString &displayName, int connectionCount)
|
||||||
|
{
|
||||||
|
rawDisplayName = displayName;
|
||||||
|
connNameLabel->setText(rawDisplayName);
|
||||||
latencyLabel->setText(QSTRN(connectionCount) + " " + (connectionCount < 2 ? tr("connection") : tr("connections")));
|
latencyLabel->setText(QSTRN(connectionCount) + " " + (connectionCount < 2 ? tr("connection") : tr("connections")));
|
||||||
//
|
//
|
||||||
layout()->removeWidget(connTypeLabel);
|
layout()->removeWidget(connTypeLabel);
|
||||||
@ -54,6 +92,9 @@ class ConnectionWidget : public QWidget, private Ui::ConnectionWidget
|
|||||||
layout()->removeWidget(dataLabel);
|
layout()->removeWidget(dataLabel);
|
||||||
delete dataLabel;
|
delete dataLabel;
|
||||||
}
|
}
|
||||||
|
CONNECTION_ITEM_TYPE type;
|
||||||
|
bool isConnectionItem = false;
|
||||||
|
QString rawDisplayName;
|
||||||
//
|
//
|
||||||
ConnectionId connectionId;
|
ConnectionId connectionId;
|
||||||
GroupId groupId;
|
GroupId groupId;
|
||||||
|
Loading…
Reference in New Issue
Block a user