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