diff --git a/common/link/link.go b/common/link/link.go index e3fcf23f..f80c9a6f 100644 --- a/common/link/link.go +++ b/common/link/link.go @@ -1,7 +1,9 @@ package link import ( + "encoding/base64" "net/url" + "strings" "github.com/sagernet/sing-box/option" E "github.com/sagernet/sing/common/exceptions" @@ -34,3 +36,49 @@ func Parse(u *url.URL) (Link, error) { } return nil, E.Errors(errs...) } + +// ParseCollection parses a links collection string to []Link +func ParseCollection(content string) ([]Link, error) { + content = doBase64DecodeOrNothing(content) + links := make([]Link, 0) + errs := make([]error, 0) + for _, line := range strings.Split(content, "\n") { + line = strings.TrimSpace(line) + if line == "" { + continue + } + u, err := url.Parse(line) + if err != nil { + continue + } + link, err := Parse(u) + if err != nil { + errs = append(errs, err) + continue + } + links = append(links, link) + } + return links, E.Errors(errs...) +} + +func doBase64DecodeOrNothing(s string) string { + b, err := base64Decode(s) + if err != nil { + return s + } + return string(b) +} + +func base64Decode(b64 string) ([]byte, error) { + b64 = strings.TrimSpace(b64) + stdb64 := b64 + if pad := len(b64) % 4; pad != 0 { + stdb64 += strings.Repeat("=", 4-pad) + } + + b, err := base64.StdEncoding.DecodeString(stdb64) + if err != nil { + return base64.URLEncoding.DecodeString(b64) + } + return b, nil +} diff --git a/common/link/ss.go b/common/link/ss.go index 6e63baa1..20abf517 100644 --- a/common/link/ss.go +++ b/common/link/ss.go @@ -1,7 +1,6 @@ package link import ( - "encoding/base64" "fmt" "net/url" "strconv" @@ -87,17 +86,3 @@ func (l *SSLink) Options() *option.Outbound { }, } } - -func base64Decode(b64 string) ([]byte, error) { - b64 = strings.TrimSpace(b64) - stdb64 := b64 - if pad := len(b64) % 4; pad != 0 { - stdb64 += strings.Repeat("=", 4-pad) - } - - b, err := base64.StdEncoding.DecodeString(stdb64) - if err != nil { - return base64.URLEncoding.DecodeString(b64) - } - return b, nil -} diff --git a/service/subscription.go b/service/subscription.go index 52af2ef0..7a37e143 100644 --- a/service/subscription.go +++ b/service/subscription.go @@ -2,12 +2,9 @@ package service import ( "context" - "encoding/base64" "io" "net" "net/http" - "net/url" - "strings" "time" "github.com/sagernet/sing-box/adapter" @@ -144,63 +141,19 @@ func (s *Subscription) fetchProvider(client *http.Client, provider option.Subscr if err != nil { return nil, err } - // try undecoded - content := string(body) - links, err := linksFromContent(content) + links, err := link.ParseCollection(string(body)) if len(links) > 0 { if err != nil { - s.logger.Warn("some links failed", err) + s.logger.Warn("links parsed with error:", err) } return links, nil } - // try decoded - decoded, err := base64Decode(content) - links, err = linksFromContent(string(decoded)) - if len(links) > 0 { - if err != nil { - s.logger.Warn("some links failed", err) - } - return links, nil + if err != nil { + return nil, err } return nil, E.New("no links found") } -func linksFromContent(content string) ([]link.Link, error) { - links := make([]link.Link, 0) - errs := make([]error, 0) - for _, line := range strings.Split(content, "\n") { - line = strings.TrimSpace(line) - if line == "" { - continue - } - u, err := url.Parse(line) - if err != nil { - continue - } - link, err := link.Parse(u) - if err != nil { - errs = append(errs, err) - continue - } - links = append(links, link) - } - return links, E.Errors(errs...) -} - -func base64Decode(b64 string) ([]byte, error) { - b64 = strings.TrimSpace(b64) - stdb64 := b64 - if pad := len(b64) % 4; pad != 0 { - stdb64 += strings.Repeat("=", 4-pad) - } - - b, err := base64.StdEncoding.DecodeString(stdb64) - if err != nil { - return base64.URLEncoding.DecodeString(b64) - } - return b, nil -} - func (s *Subscription) client() (*http.Client, error) { var detour adapter.Outbound if s.options.DownloadDetour != "" {