mirror of
https://github.com/Qv2ray/Qv2ray.git
synced 2025-05-20 10:50:23 +08:00
add: support Plugin Serialization
This commit is contained in:
parent
3020df5726
commit
059bad8d4b
@ -216,6 +216,74 @@ namespace Qv2ray::components::plugins
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QList<QvPluginEditor *> QvPluginHost::GetOutboundEditorWidgets() const
|
||||||
|
{
|
||||||
|
QList<QvPluginEditor *> data;
|
||||||
|
for (const auto &plugin : plugins)
|
||||||
|
{
|
||||||
|
if (!plugin.isLoaded)
|
||||||
|
continue;
|
||||||
|
auto editor = plugin.pluginInterface->GetEditorWidget(UI_TYPE::UI_TYPE_OUTBOUND_EDITOR);
|
||||||
|
if (editor)
|
||||||
|
{
|
||||||
|
data.append(editor.release());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QMultiHash<QString, QPair<QString, QJsonObject>> QvPluginHost::TryDeserializeShareLink(const QString &sharelink, //
|
||||||
|
QString *prefix, //
|
||||||
|
QString *errMessage, //
|
||||||
|
QString *newGroupName, //
|
||||||
|
bool *status) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(newGroupName)
|
||||||
|
QMultiHash<QString, QPair<QString, QJsonObject>> data;
|
||||||
|
*status = true;
|
||||||
|
for (const auto &plugin : plugins)
|
||||||
|
{
|
||||||
|
if (plugin.isLoaded && plugin.metadata.SpecialPluginType.contains(SPECIAL_TYPE_SERIALIZOR))
|
||||||
|
{
|
||||||
|
auto serializer = plugin.pluginInterface->GetSerializer();
|
||||||
|
bool thisPluginCanHandle = false;
|
||||||
|
for (const auto &prefix : serializer->ShareLinkPrefixes())
|
||||||
|
{
|
||||||
|
thisPluginCanHandle = thisPluginCanHandle || sharelink.startsWith(prefix);
|
||||||
|
}
|
||||||
|
if (thisPluginCanHandle)
|
||||||
|
{
|
||||||
|
auto [protocol, outboundSettings] = serializer->DeserializeOutbound(sharelink, prefix, errMessage);
|
||||||
|
*status = *status && errMessage->isEmpty();
|
||||||
|
data.insert(*prefix, { protocol, outboundSettings });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
const QString QvPluginHost::TrySerializeShareLink(const QString &protocol, //
|
||||||
|
const QJsonObject &outboundSettings, //
|
||||||
|
const QString &alias, //
|
||||||
|
const QString &groupName, //
|
||||||
|
bool *status) const
|
||||||
|
{
|
||||||
|
*status = false;
|
||||||
|
for (const auto &plugin : plugins)
|
||||||
|
{
|
||||||
|
if (plugin.isLoaded && plugin.metadata.SpecialPluginType.contains(SPECIAL_TYPE_SERIALIZOR))
|
||||||
|
{
|
||||||
|
auto serializer = plugin.pluginInterface->GetSerializer();
|
||||||
|
if (serializer->OutboundProtocols().contains(protocol))
|
||||||
|
{
|
||||||
|
auto link = serializer->SerializeOutbound(protocol, alias, groupName, outboundSettings);
|
||||||
|
*status = true;
|
||||||
|
return link;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
const QString GetPluginTypeString(const SPECIAL_TYPE_FLAGS &types)
|
const QString GetPluginTypeString(const SPECIAL_TYPE_FLAGS &types)
|
||||||
{
|
{
|
||||||
QStringList typesList;
|
QStringList typesList;
|
||||||
|
@ -46,10 +46,6 @@ namespace Qv2ray::components::plugins
|
|||||||
{
|
{
|
||||||
return plugins.value(internalName).pluginInterface->GetSettingsWidget();
|
return plugins.value(internalName).pluginInterface->GetSettingsWidget();
|
||||||
}
|
}
|
||||||
inline std::unique_ptr<QvPluginEditor> GetPluginEditorWidget(const QString &internalName, UI_TYPE type) const
|
|
||||||
{
|
|
||||||
return plugins.value(internalName).pluginInterface->GetEditorWidget(type);
|
|
||||||
}
|
|
||||||
const inline QJsonObject GetPluginSettings(const QString &internalName) const
|
const inline QJsonObject GetPluginSettings(const QString &internalName) const
|
||||||
{
|
{
|
||||||
return plugins.value(internalName).pluginInterface->GetSettngs();
|
return plugins.value(internalName).pluginInterface->GetSettngs();
|
||||||
@ -62,23 +58,19 @@ namespace Qv2ray::components::plugins
|
|||||||
{
|
{
|
||||||
return plugins.value(internalName).metadata;
|
return plugins.value(internalName).metadata;
|
||||||
}
|
}
|
||||||
const QList<QPair<QvPluginOutboundObject, QString>> GetOutboundEditorWidgets() const
|
//
|
||||||
{
|
const QMultiHash<QString, QPair<QString, QJsonObject>> TryDeserializeShareLink(const QString &sharelink, //
|
||||||
QList<QPair<QvPluginOutboundObject, QString>> data;
|
QString *prefix, //
|
||||||
for (const auto &plugin : plugins)
|
QString *errMessage, //
|
||||||
{
|
QString *newGroupName, //
|
||||||
if (!plugin.isLoaded)
|
bool *status) const;
|
||||||
continue;
|
//
|
||||||
if (auto editor = plugin.pluginInterface->GetEditorWidget(UI_TYPE::UI_TYPE_OUTBOUND_EDITOR); editor)
|
const QString TrySerializeShareLink(const QString &protocol, //
|
||||||
{
|
const QJsonObject &outboundSettings, //
|
||||||
for (const auto &cap : editor->OutboundCapabilities())
|
const QString &alias, //
|
||||||
{
|
const QString &groupName, //
|
||||||
data.append({ cap, plugin.metadata.InternalName });
|
bool *status) const;
|
||||||
}
|
const QList<QvPluginEditor *> GetOutboundEditorWidgets() const;
|
||||||
}
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
//
|
//
|
||||||
void Send_ConnectionStatsEvent(const QvConnectionStatsEventObject &object);
|
void Send_ConnectionStatsEvent(const QvConnectionStatsEventObject &object);
|
||||||
void Send_ConnectivityEvent(const QvConnectivityEventObject &object);
|
void Send_ConnectivityEvent(const QvConnectivityEventObject &object);
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit 7df6621bcdfcddadd94bdfd253306e1680035941
|
Subproject commit 71dab9339510d7e766bff00c89159f0407cff910
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "Generation.hpp"
|
#include "Generation.hpp"
|
||||||
#include "common/QvHelpers.hpp"
|
#include "common/QvHelpers.hpp"
|
||||||
|
#include "components/plugins/QvPluginHost.hpp"
|
||||||
#include "core/CoreUtils.hpp"
|
#include "core/CoreUtils.hpp"
|
||||||
#include "core/handler/ConfigHandler.hpp"
|
#include "core/handler/ConfigHandler.hpp"
|
||||||
|
|
||||||
@ -11,29 +12,45 @@ namespace Qv2ray::core::connection
|
|||||||
{
|
{
|
||||||
QMultiHash<QString, CONFIGROOT> ConvertConfigFromString(const QString &link, QString *prefix, QString *errMessage, QString *newGroupName)
|
QMultiHash<QString, CONFIGROOT> ConvertConfigFromString(const QString &link, QString *prefix, QString *errMessage, QString *newGroupName)
|
||||||
{
|
{
|
||||||
QMultiHash<QString, CONFIGROOT> config;
|
QMultiHash<QString, CONFIGROOT> connectionConf;
|
||||||
if (link.startsWith("vmess://"))
|
if (link.startsWith("vmess://"))
|
||||||
{
|
{
|
||||||
auto conf = ConvertConfigFromVMessString(link, prefix, errMessage);
|
auto conf = ConvertConfigFromVMessString(link, prefix, errMessage);
|
||||||
config.insert(*prefix, conf);
|
connectionConf.insert(*prefix, conf);
|
||||||
}
|
}
|
||||||
else if (link.startsWith("ss://"))
|
else if (link.startsWith("ss://"))
|
||||||
{
|
{
|
||||||
auto conf = ConvertConfigFromSSString(link, prefix, errMessage);
|
auto conf = ConvertConfigFromSSString(link, prefix, errMessage);
|
||||||
config.insert(*prefix, conf);
|
connectionConf.insert(*prefix, conf);
|
||||||
}
|
}
|
||||||
else if (link.startsWith("ssd://"))
|
else if (link.startsWith("ssd://"))
|
||||||
{
|
{
|
||||||
QStringList errMessageList;
|
QStringList errMessageList;
|
||||||
config = ConvertConfigFromSSDString(link, newGroupName, &errMessageList);
|
connectionConf = ConvertConfigFromSSDString(link, newGroupName, &errMessageList);
|
||||||
*errMessage = errMessageList.join(NEWLINE);
|
*errMessage = errMessageList.join(NEWLINE);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
bool ok = false;
|
||||||
|
auto configs = PluginHost->TryDeserializeShareLink(link, prefix, errMessage, newGroupName, &ok);
|
||||||
|
for (const auto &key : configs.keys())
|
||||||
|
{
|
||||||
|
auto vals = configs.values(key);
|
||||||
|
for (const auto &val : vals)
|
||||||
|
{
|
||||||
|
CONFIGROOT root;
|
||||||
|
auto outbound = GenerateOutboundEntry(val.first, OUTBOUNDSETTING(val.second), {});
|
||||||
|
root.insert("outbounds", QJsonArray{ outbound });
|
||||||
|
connectionConf.insert(key, root);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!ok)
|
||||||
{
|
{
|
||||||
*errMessage = QObject::tr("Unsupported share link format.");
|
*errMessage = QObject::tr("Unsupported share link format.");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return config;
|
return connectionConf;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString ConvertConfigToString(const ConnectionId &id, bool isSip002)
|
const QString ConvertConfigToString(const ConnectionId &id, bool isSip002)
|
||||||
@ -45,51 +62,51 @@ namespace Qv2ray::core::connection
|
|||||||
return QV2RAY_SERIALIZATION_COMPLEX_CONFIG_PLACEHOLDER;
|
return QV2RAY_SERIALIZATION_COMPLEX_CONFIG_PLACEHOLDER;
|
||||||
}
|
}
|
||||||
auto server = ConnectionManager->GetConnectionRoot(id);
|
auto server = ConnectionManager->GetConnectionRoot(id);
|
||||||
return ConvertConfigToString(alias, server, isSip002);
|
return ConvertConfigToString(alias, GetDisplayName(GetConnectionGroupId(id)), server, isSip002);
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString ConvertConfigToString(const QString &alias, const CONFIGROOT &server, bool isSip002)
|
const QString ConvertConfigToString(const QString &alias, const QString &groupName, const CONFIGROOT &server, bool isSip002)
|
||||||
{
|
{
|
||||||
OUTBOUND outbound = OUTBOUND(server["outbounds"].toArray().first().toObject());
|
const auto outbound = OUTBOUND(server["outbounds"].toArray().first().toObject());
|
||||||
auto type = outbound["protocol"].toString();
|
const auto type = outbound["protocol"].toString();
|
||||||
|
const auto &settings = outbound["settings"].toObject();
|
||||||
QString sharelink = "";
|
QString sharelink = "";
|
||||||
|
|
||||||
if (type == "vmess")
|
if (type == "vmess")
|
||||||
{
|
{
|
||||||
auto vmessServer =
|
auto vmessServer = StructFromJsonString<VMessServerObject>(JsonToString(settings["vnext"].toArray().first().toObject()));
|
||||||
StructFromJsonString<VMessServerObject>(JsonToString(outbound["settings"].toObject()["vnext"].toArray().first().toObject()));
|
|
||||||
auto transport = StructFromJsonString<StreamSettingsObject>(JsonToString(outbound["streamSettings"].toObject()));
|
auto transport = StructFromJsonString<StreamSettingsObject>(JsonToString(outbound["streamSettings"].toObject()));
|
||||||
sharelink = vmess::ConvertConfigToVMessString(transport, vmessServer, alias);
|
sharelink = vmess::ConvertConfigToVMessString(transport, vmessServer, alias);
|
||||||
}
|
}
|
||||||
else if (type == "shadowsocks")
|
else if (type == "shadowsocks")
|
||||||
{
|
{
|
||||||
auto ssServer = StructFromJsonString<ShadowSocksServerObject>(
|
auto ssServer = StructFromJsonString<ShadowSocksServerObject>(JsonToString(settings["servers"].toArray().first().toObject()));
|
||||||
JsonToString(outbound["settings"].toObject()["servers"].toArray().first().toObject()));
|
|
||||||
sharelink = ss::ConvertConfigToSSString(ssServer, alias, isSip002);
|
sharelink = ss::ConvertConfigToSSString(ssServer, alias, isSip002);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!type.isEmpty())
|
if (type.isEmpty())
|
||||||
{
|
{
|
||||||
// DEBUG(MODULE_CONNECTION, "WARNING: Unsupported outbound type: " + type)
|
DEBUG(MODULE_CONNECTION, "WARNING: Empty outbound type.")
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
DEBUG(MODULE_CONNECTION, "WARNING: Empty outbound type.")
|
bool ok = false;
|
||||||
|
sharelink = PluginHost->TrySerializeShareLink(type, settings, alias, groupName, &ok);
|
||||||
|
Q_UNUSED(ok)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return sharelink;
|
return sharelink;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString DecodeSubscriptionString(QByteArray arr)
|
QString DecodeSubscriptionString(const QByteArray &arr)
|
||||||
{
|
{
|
||||||
// String may start with: vmess:// and ss://
|
// String may start with: vmess:// and ss://
|
||||||
// We only process vmess:// here
|
// We only process vmess:// here
|
||||||
// Some subscription providers may use plain vmess:// saperated by
|
// Some subscription providers may use plain vmess:// saperated by
|
||||||
// lines But others may use base64 of above.
|
// lines But others may use base64 of above.
|
||||||
auto result = QString::fromUtf8(arr).trimmed();
|
auto result = QString::fromUtf8(arr).trimmed();
|
||||||
return result.startsWith("vmess://") ? result : Base64Decode(result);
|
return result.contains("://") ? result : Base64Decode(result);
|
||||||
}
|
}
|
||||||
} // namespace Serialization
|
} // namespace Serialization
|
||||||
} // namespace Qv2ray::core::connection
|
} // namespace Qv2ray::core::connection
|
||||||
|
@ -15,11 +15,11 @@ namespace Qv2ray::core::connection
|
|||||||
inline auto QV2RAY_SSD_DEFAULT_NAME_PATTERN = QObject::tr("%1 - %2 (rate %3)");
|
inline auto QV2RAY_SSD_DEFAULT_NAME_PATTERN = QObject::tr("%1 - %2 (rate %3)");
|
||||||
//
|
//
|
||||||
// General
|
// General
|
||||||
QString DecodeSubscriptionString(QByteArray arr);
|
QString DecodeSubscriptionString(const QByteArray &arr);
|
||||||
QMultiHash<QString, CONFIGROOT> ConvertConfigFromString(const QString &link, QString *aliasPrefix, QString *errMessage,
|
QMultiHash<QString, CONFIGROOT> ConvertConfigFromString(const QString &link, QString *aliasPrefix, QString *errMessage,
|
||||||
QString *newGroupName = nullptr);
|
QString *newGroupName = nullptr);
|
||||||
const QString ConvertConfigToString(const ConnectionId &id, bool isSip002 = false);
|
const QString ConvertConfigToString(const ConnectionId &id, bool isSip002 = false);
|
||||||
const QString ConvertConfigToString(const QString &alias, const CONFIGROOT &server, bool isSip002);
|
const QString ConvertConfigToString(const QString &alias, const QString &groupName, const CONFIGROOT &server, bool isSip002);
|
||||||
//
|
//
|
||||||
// VMess URI Protocol
|
// VMess URI Protocol
|
||||||
namespace vmess
|
namespace vmess
|
||||||
|
@ -24,10 +24,12 @@ OutboundEditor::OutboundEditor(QWidget *parent) : QDialog(parent), tag(OUTBOUND_
|
|||||||
auto pluginEditorWidgetsInfo = PluginHost->GetOutboundEditorWidgets();
|
auto pluginEditorWidgetsInfo = PluginHost->GetOutboundEditorWidgets();
|
||||||
for (const auto &plugin : pluginEditorWidgetsInfo)
|
for (const auto &plugin : pluginEditorWidgetsInfo)
|
||||||
{
|
{
|
||||||
outBoundTypeCombo->addItem(plugin.first.displayName, plugin.first.protocol);
|
for (const auto &_d : plugin->OutboundCapabilities())
|
||||||
auto widget = PluginHost->GetPluginEditorWidget(plugin.second, UI_TYPE::UI_TYPE_OUTBOUND_EDITOR).release();
|
{
|
||||||
auto index = outboundTypeStackView->addWidget(widget);
|
outBoundTypeCombo->addItem(_d.displayName, _d.protocol);
|
||||||
pluginWidgets.insert(index, { plugin.first, plugin.second, widget });
|
auto index = outboundTypeStackView->addWidget(plugin);
|
||||||
|
pluginWidgets.insert(index, { _d, plugin });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
outboundType = "vmess";
|
outboundType = "vmess";
|
||||||
@ -117,10 +119,10 @@ OUTBOUND OutboundEditor::GenerateConnectionJson()
|
|||||||
bool processed = false;
|
bool processed = false;
|
||||||
for (const auto &plugin : pluginWidgets)
|
for (const auto &plugin : pluginWidgets)
|
||||||
{
|
{
|
||||||
if (get<0>(plugin).protocol == outboundType)
|
if (plugin.first.protocol == outboundType)
|
||||||
{
|
{
|
||||||
get<2>(plugin)->SetHostInfo(address, port);
|
plugin.second->SetHostInfo(address, port);
|
||||||
settings = OUTBOUNDSETTING(get<2>(plugin)->GetContent());
|
settings = OUTBOUNDSETTING(plugin.second->GetContent());
|
||||||
processed = true;
|
processed = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -193,11 +195,11 @@ void OutboundEditor::ReloadGUI()
|
|||||||
for (const auto &index : pluginWidgets.keys())
|
for (const auto &index : pluginWidgets.keys())
|
||||||
{
|
{
|
||||||
const auto &plugin = pluginWidgets.value(index);
|
const auto &plugin = pluginWidgets.value(index);
|
||||||
if (get<0>(plugin).protocol == outboundType)
|
if (plugin.first.protocol == outboundType)
|
||||||
{
|
{
|
||||||
get<2>(plugin)->SetContent(settings);
|
plugin.second->SetContent(settings);
|
||||||
outBoundTypeCombo->setCurrentIndex(index);
|
outBoundTypeCombo->setCurrentIndex(index);
|
||||||
auto [_address, _port] = get<2>(plugin)->GetHostInfo();
|
auto [_address, _port] = plugin.second->GetHostInfo();
|
||||||
address = _address;
|
address = _address;
|
||||||
port = _port;
|
port = _port;
|
||||||
processed = true;
|
processed = true;
|
||||||
@ -284,7 +286,7 @@ void OutboundEditor::on_outBoundTypeCombo_currentIndexChanged(int index)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
outboundType = get<0>(pluginWidgets.value(index)).protocol;
|
outboundType = pluginWidgets.value(index).first.protocol;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,5 +64,5 @@ class OutboundEditor
|
|||||||
//
|
//
|
||||||
StreamSettingsWidget *streamSettingsWidget;
|
StreamSettingsWidget *streamSettingsWidget;
|
||||||
//
|
//
|
||||||
QMap<int, std::tuple<QvPluginOutboundObject, QString, QvPluginEditor *>> pluginWidgets;
|
QMap<int, QPair<QvPluginOutboundObject, QvPluginEditor *>> pluginWidgets;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user