From eb2a1284bce60ce9f183e16b85ff7170345bed24 Mon Sep 17 00:00:00 2001 From: QxQ <59914293+U-v-U@users.noreply.github.com> Date: Fri, 25 Sep 2020 19:38:24 +0800 Subject: [PATCH] add: added SIP008 subscription support --- makespec/BUILDVERSION | 2 +- .../core/SubscriptionAdapter.cpp | 76 +++++++++++++++++++ .../core/SubscriptionAdapter.hpp | 48 +++++++----- 3 files changed, 104 insertions(+), 22 deletions(-) diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index a7aff7aa..5b66e48a 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5939 +5940 diff --git a/src/plugins/subscription-adapters/core/SubscriptionAdapter.cpp b/src/plugins/subscription-adapters/core/SubscriptionAdapter.cpp index db759fd8..a7d8e3b1 100644 --- a/src/plugins/subscription-adapters/core/SubscriptionAdapter.cpp +++ b/src/plugins/subscription-adapters/core/SubscriptionAdapter.cpp @@ -1 +1,77 @@ #include "SubscriptionAdapter.hpp" + +#include +#include +#include +#include +#include +#include + +QString SafeBase64Decode(QString string) +{ + QByteArray ba = string.replace(QChar('-'), QChar('+')).replace(QChar('_'), QChar('/')).toUtf8(); + return QByteArray::fromBase64(ba, QByteArray::Base64Option::OmitTrailingEquals); +} + +QString SafeBase64Encode(const QString &string) +{ + QString base64 = string.toUtf8().toBase64(); + return base64.replace(QChar('+'), QChar('-')).replace(QChar('/'), QChar('_')); +} + +// Simple Base64 Decoder +SubscriptionDecoder::SubscriptionDecodeResult SimpleBase64Decoder::DecodeData(const QByteArray &data) const +{ + auto source = QString::fromUtf8(data).trimmed(); + const auto resultList = source.contains("://") ? source : SafeBase64Decode(source); + // + SubscriptionDecodeResult result; + result.links = SplitLines(resultList); + return result; +} + +// SIP008 Decoder +SubscriptionDecoder::SubscriptionDecodeResult SIP008Decoder::DecodeData(const QByteArray &data) const +{ + const auto root = QJsonDocument::fromJson(data).object(); + // + const auto version = root["version"].toString(); + const auto username = root["username"].toString(); + const auto user_uuid = root["user_uuid"].toString(); + const auto servers = root["servers"].toArray(); + + // ss://Y2hhY2hhMjAtaWV0Zi1wb2x5MTMwNTpwYXNzQGhvc3Q6MTIzNA/?plugin=plugin%3Bopt#sssip003 + + SubscriptionDecodeResult result; +#define GetVal(x) const auto x = serverObj[#x].toString() + for (const auto &servVal : servers) + { + const auto serverObj = servVal.toObject(); + GetVal(server); + GetVal(password); + GetVal(method); + GetVal(plugin); + GetVal(plugin_opts); + GetVal(remarks); + GetVal(id); + const auto server_port = serverObj["server_port"].toInt(); + bool isSIP003 = !plugin.isEmpty(); + const auto userInfo = SafeBase64Encode(method + ":" + password); + // + QUrl link; + link.setScheme("ss"); + link.setUserInfo(userInfo); + link.setHost(server); + link.setPort(server_port); + link.setFragment(remarks); + if (isSIP003) + { + QUrlQuery q; + q.addQueryItem("plugin", QUrl::toPercentEncoding(plugin + ";" + plugin_opts)); + link.setQuery(q); + } + result.links << link.toString(QUrl::FullyEncoded); + } +#undef GetVal + return result; +} diff --git a/src/plugins/subscription-adapters/core/SubscriptionAdapter.hpp b/src/plugins/subscription-adapters/core/SubscriptionAdapter.hpp index cac8d4b6..3f8ee79b 100644 --- a/src/plugins/subscription-adapters/core/SubscriptionAdapter.hpp +++ b/src/plugins/subscription-adapters/core/SubscriptionAdapter.hpp @@ -2,6 +2,8 @@ #include "CommonTypes.hpp" #include "QvPluginProcessor.hpp" +using namespace Qv2rayPlugin; + const inline QStringList SplitLines(const QString &_string) { #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) @@ -13,43 +15,47 @@ const inline QStringList SplitLines(const QString &_string) #endif } -class BuiltinSerializer : public Qv2rayPlugin::SubscriptionDecoder +class SimpleBase64Decoder : public Qv2rayPlugin::SubscriptionDecoder { public: - explicit BuiltinSerializer() : Qv2rayPlugin::SubscriptionDecoder(){}; - SubscriptionDecodeResult DecodeData(const QByteArray &data) const override - { - const static auto SafeBase64Decode = [](QString string) -> QString { - QByteArray ba = string.replace(QChar('-'), QChar('+')).replace(QChar('_'), QChar('/')).toUtf8(); - return QByteArray::fromBase64(ba, QByteArray::Base64Option::OmitTrailingEquals); - }; - - auto source = QString::fromUtf8(data).trimmed(); - const auto resultList = source.contains("://") ? source : SafeBase64Decode(source); - // - SubscriptionDecodeResult result; - result.links = SplitLines(resultList); - return result; - } + explicit SimpleBase64Decoder() : SubscriptionDecoder(){}; + SubscriptionDecodeResult DecodeData(const QByteArray &data) const override; }; -class BuiltinSubscriptionAdapterInterface : public Qv2rayPlugin::SubscriptionInterface +class SIP008Decoder : public Qv2rayPlugin::SubscriptionDecoder { public: - explicit BuiltinSubscriptionAdapterInterface() : Qv2rayPlugin::SubscriptionInterface() + explicit SIP008Decoder() : SubscriptionDecoder(){}; + SubscriptionDecodeResult DecodeData(const QByteArray &data) const override; +}; + +class BuiltinSubscriptionAdapterInterface : public SubscriptionInterface +{ + public: + explicit BuiltinSubscriptionAdapterInterface() : SubscriptionInterface() { - simple_base64 = std::make_shared(); + simple_base64 = std::make_shared(); + sip008 = std::make_shared(); } + QList SupportedSubscriptionTypes() const override { // "simple_base64" = magic value in Qv2ray main application - return { Qv2rayPlugin::ProtocolInfoObject{ "simple_base64", "Basic Base64" } }; + return { // + ProtocolInfoObject{ "simple_base64", "Basic Base64" }, // + ProtocolInfoObject{ "sip008", "SIP008" } + }; } + std::shared_ptr GetSubscriptionDecoder(const QString &type) const override { if (type == "simple_base64") return simple_base64; + if (type == "sip008") + return sip008; return nullptr; } - std::shared_ptr simple_base64; + + std::shared_ptr simple_base64; + std::shared_ptr sip008; };