From f112667190e535e1f203775b00b5b30e584e3ceb Mon Sep 17 00:00:00 2001 From: Shelikhoo Date: Sat, 25 Nov 2023 16:53:05 +0000 Subject: [PATCH] Add data URL Link support to subscription --- .../containers/dataurlsingle/dataurl.go | 44 +++++++++++++++++++ .../dataurlsingle/errors.generated.go | 9 ++++ .../documentfetcher/dataurlfetcher/dataurl.go | 16 ++++++- .../dataurlfetcher/errors.generated.go | 9 ++++ .../subscriptionmanager/subdocupdater.go | 7 ++- go.mod | 1 + go.sum | 2 + .../commands/all/engineering/encodedataurl.go | 32 ++++++++++++++ main/commands/all/engineering/engineering.go | 1 + main/distro/all/all.go | 2 + 10 files changed, 121 insertions(+), 2 deletions(-) create mode 100644 app/subscription/containers/dataurlsingle/dataurl.go create mode 100644 app/subscription/containers/dataurlsingle/errors.generated.go create mode 100644 app/subscription/documentfetcher/dataurlfetcher/errors.generated.go create mode 100644 main/commands/all/engineering/encodedataurl.go diff --git a/app/subscription/containers/dataurlsingle/dataurl.go b/app/subscription/containers/dataurlsingle/dataurl.go new file mode 100644 index 000000000..6e289b6e2 --- /dev/null +++ b/app/subscription/containers/dataurlsingle/dataurl.go @@ -0,0 +1,44 @@ +package dataurlsingle + +import ( + "bytes" + "strings" + + "github.com/vincent-petithory/dataurl" + + "github.com/v2fly/v2ray-core/v5/app/subscription/containers" + "github.com/v2fly/v2ray-core/v5/common" +) + +//go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen + +func newSingularDataURLParser() containers.SubscriptionContainerDocumentParser { + return &parser{} +} + +type parser struct{} + +func (p parser) ParseSubscriptionContainerDocument(rawConfig []byte) (*containers.Container, error) { + dataURL, err := dataurl.Decode(bytes.NewReader(rawConfig)) + if err != nil { + return nil, newError("unable to decode dataURL").Base(err) + } + if dataURL.MediaType.Type != "application" { + return nil, newError("unsupported media type: ", dataURL.MediaType.Type) + } + if !strings.HasPrefix(dataURL.MediaType.Subtype, "vnd.v2ray.subscription-singular") { + return nil, newError("unsupported media subtype: ", dataURL.MediaType.Subtype) + } + result := &containers.Container{} + result.Kind = "DataURLSingle" + result.Metadata = make(map[string]string) + result.ServerSpecs = append(result.ServerSpecs, containers.UnparsedServerConf{ + KindHint: "", + Content: dataURL.Data, + }) + return result, nil +} + +func init() { + common.Must(containers.RegisterParser("DataURLSingle", newSingularDataURLParser())) +} diff --git a/app/subscription/containers/dataurlsingle/errors.generated.go b/app/subscription/containers/dataurlsingle/errors.generated.go new file mode 100644 index 000000000..4db46411c --- /dev/null +++ b/app/subscription/containers/dataurlsingle/errors.generated.go @@ -0,0 +1,9 @@ +package dataurlsingle + +import "github.com/v2fly/v2ray-core/v5/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/app/subscription/documentfetcher/dataurlfetcher/dataurl.go b/app/subscription/documentfetcher/dataurlfetcher/dataurl.go index 8acf7afb2..6f6ebb4bd 100644 --- a/app/subscription/documentfetcher/dataurlfetcher/dataurl.go +++ b/app/subscription/documentfetcher/dataurlfetcher/dataurl.go @@ -2,6 +2,10 @@ package dataurlfetcher import ( "context" + "strings" + + "github.com/vincent-petithory/dataurl" + "github.com/v2fly/v2ray-core/v5/app/subscription" "github.com/v2fly/v2ray-core/v5/app/subscription/documentfetcher" "github.com/v2fly/v2ray-core/v5/common" @@ -20,5 +24,15 @@ func init() { type dataURLFetcher struct{} func (d *dataURLFetcher) DownloadDocument(ctx context.Context, source *subscription.ImportSource, opts ...documentfetcher.FetcherOptions) ([]byte, error) { - panic("implement me") + dataURL, err := dataurl.DecodeString(source.Url) + if err != nil { + return nil, newError("unable to decode dataURL").Base(err) + } + if dataURL.MediaType.Type != "application" { + return nil, newError("unsupported media type: ", dataURL.MediaType.Type) + } + if !strings.HasPrefix(dataURL.MediaType.Subtype, "vnd.v2ray.subscription") { + return nil, newError("unsupported media subtype: ", dataURL.MediaType.Subtype) + } + return []byte(source.Url), nil } diff --git a/app/subscription/documentfetcher/dataurlfetcher/errors.generated.go b/app/subscription/documentfetcher/dataurlfetcher/errors.generated.go new file mode 100644 index 000000000..0dc4c9e45 --- /dev/null +++ b/app/subscription/documentfetcher/dataurlfetcher/errors.generated.go @@ -0,0 +1,9 @@ +package dataurlfetcher + +import "github.com/v2fly/v2ray-core/v5/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/app/subscription/subscriptionmanager/subdocupdater.go b/app/subscription/subscriptionmanager/subdocupdater.go index d6cd2f74b..19b798b58 100644 --- a/app/subscription/subscriptionmanager/subdocupdater.go +++ b/app/subscription/subscriptionmanager/subdocupdater.go @@ -21,11 +21,16 @@ func (s *SubscriptionManagerImpl) updateSubscription(subscriptionName string) er trackedSub = trackedSubFound } importSource := trackedSub.importSource - docFetcher, err := documentfetcher.GetFetcher("http") if err != nil { return newError("failed to get fetcher: ", err) } + if strings.HasPrefix(importSource.Url, "data:") { + docFetcher, err = documentfetcher.GetFetcher("dataurl") + if err != nil { + return newError("failed to get fetcher: ", err) + } + } downloadedDocument, err := docFetcher.DownloadDocument(s.ctx, importSource) if err != nil { diff --git a/go.mod b/go.mod index a9da1119b..63c73082d 100644 --- a/go.mod +++ b/go.mod @@ -26,6 +26,7 @@ require ( github.com/v2fly/BrowserBridge v0.0.0-20210430233438-0570fc1d7d08 github.com/v2fly/VSign v0.0.0-20201108000810-e2adc24bf848 github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e + github.com/vincent-petithory/dataurl v1.0.0 github.com/xiaokangwang/VLite v0.0.0-20220418190619-cff95160a432 go.starlark.net v0.0.0-20230612165344-9532f5667272 go4.org/netipx v0.0.0-20230303233057-f1b76eb4bb35 diff --git a/go.sum b/go.sum index 529c94982..c5598ab51 100644 --- a/go.sum +++ b/go.sum @@ -325,6 +325,8 @@ github.com/v2fly/VSign v0.0.0-20201108000810-e2adc24bf848 h1:p1UzXK6VAutXFFQMnre github.com/v2fly/VSign v0.0.0-20201108000810-e2adc24bf848/go.mod h1:p80Bv154ZtrGpXMN15slDCqc9UGmfBuUzheDFBYaW/M= github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e h1:5QefA066A1tF8gHIiADmOVOV5LS43gt3ONnlEl3xkwI= github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e/go.mod h1:5t19P9LBIrNamL6AcMQOncg/r10y3Pc01AbHeMhwlpU= +github.com/vincent-petithory/dataurl v1.0.0 h1:cXw+kPto8NLuJtlMsI152irrVw9fRDX8AbShPRpg2CI= +github.com/vincent-petithory/dataurl v1.0.0/go.mod h1:FHafX5vmDzyP+1CQATJn7WFKc9CvnvxyvZy6I1MrG/U= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xiaokangwang/VLite v0.0.0-20220418190619-cff95160a432 h1:I/ATawgO2RerCq9ACwL0wBB8xNXZdE3J+93MCEHReRs= github.com/xiaokangwang/VLite v0.0.0-20220418190619-cff95160a432/go.mod h1:QN7Go2ftTVfx0aCTh9RXHV8pkpi0FtmbwQw40dy61wQ= diff --git a/main/commands/all/engineering/encodedataurl.go b/main/commands/all/engineering/encodedataurl.go new file mode 100644 index 000000000..af8e76a64 --- /dev/null +++ b/main/commands/all/engineering/encodedataurl.go @@ -0,0 +1,32 @@ +package engineering + +import ( + "flag" + "io" + "os" + + "github.com/vincent-petithory/dataurl" + + "github.com/v2fly/v2ray-core/v5/main/commands/base" +) + +var cmdEncodeDataURLContentType *string + +var cmdEncodeDataURL = &base.Command{ + UsageLine: "{{.Exec}} engineering encodeDataURL", + Flag: func() flag.FlagSet { + fs := flag.NewFlagSet("", flag.ExitOnError) + cmdEncodeDataURLContentType = fs.String("type", "application/vnd.v2ray.subscription-singular", "") + return *fs + }(), + Run: func(cmd *base.Command, args []string) { + cmd.Flag.Parse(args) + + content, err := io.ReadAll(os.Stdin) + if err != nil { + base.Fatalf("%s", err) + } + dataURL := dataurl.New(content, *cmdEncodeDataURLContentType) + dataURL.WriteTo(os.Stdout) + }, +} diff --git a/main/commands/all/engineering/engineering.go b/main/commands/all/engineering/engineering.go index fb31d8349..d7b62d707 100644 --- a/main/commands/all/engineering/engineering.go +++ b/main/commands/all/engineering/engineering.go @@ -12,6 +12,7 @@ var cmdEngineering = &base.Command{ cmdNonNativeLinkExtract, cmdNonNativeLinkExec, cmdSubscriptionEntriesExtract, + cmdEncodeDataURL, }, } diff --git a/main/distro/all/all.go b/main/distro/all/all.go index ccf4fed00..200f12283 100644 --- a/main/distro/all/all.go +++ b/main/distro/all/all.go @@ -118,10 +118,12 @@ import ( // Subscription Containers: general purpose _ "github.com/v2fly/v2ray-core/v5/app/subscription/containers/base64urlline" + _ "github.com/v2fly/v2ray-core/v5/app/subscription/containers/dataurlsingle" _ "github.com/v2fly/v2ray-core/v5/app/subscription/containers/jsonfieldarray" _ "github.com/v2fly/v2ray-core/v5/app/subscription/containers/jsonfieldarray/jsonified" // Subscription Fetchers + _ "github.com/v2fly/v2ray-core/v5/app/subscription/documentfetcher/dataurlfetcher" _ "github.com/v2fly/v2ray-core/v5/app/subscription/documentfetcher/httpfetcher" // Subscription Entries Converters