github.com/v2fly/v2ray-core/v5@v5.16.2-0.20240507031116-8191faa6e095/app/subscription/entries/nonnative/nonnative.go (about) 1 package nonnative 2 3 import ( 4 "encoding/base64" 5 "encoding/json" 6 "net/url" 7 "regexp" 8 "strings" 9 ) 10 11 //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen 12 13 func ExtractAllValuesFromBytes(bytes []byte) AbstractNonNativeLink { 14 link := AbstractNonNativeLink{} 15 link.fromBytes(bytes) 16 return link 17 } 18 19 type jsonDocument map[string]json.RawMessage 20 21 type AbstractNonNativeLink struct { 22 Values map[string]string 23 } 24 25 func (a *AbstractNonNativeLink) fromBytes(bytes []byte) { 26 a.Values = make(map[string]string) 27 content := string(bytes) 28 content = strings.Trim(content, " \n\t\r") 29 a.extractValue(content, "root") 30 } 31 32 func (a *AbstractNonNativeLink) extractValue(content, prefix string) { 33 { 34 // check if the content is a link 35 match, err := regexp.Match("[a-zA-Z0-9]+:((\\/\\/)|\\?)", []byte(content)) 36 if err != nil { 37 panic(err) 38 } 39 if match { 40 // if so, parse as link 41 parsedURL, err := url.Parse(content) 42 // if process is successful, then continue to parse every element of the link 43 if err == nil { 44 a.Values[prefix+"_!kind"] = "link" 45 a.extractLink(parsedURL, prefix) 46 return 47 } 48 } 49 } 50 { 51 // check if it is base64 52 content = strings.Trim(content, "=") 53 decoded, err := base64.RawStdEncoding.DecodeString(content) 54 if err == nil { 55 a.Values[prefix+"_!kind"] = "base64" 56 a.extractValue(string(decoded), prefix+"_!base64") 57 return 58 } 59 } 60 { 61 // check if it is base64url 62 content = strings.Trim(content, "=") 63 decoded, err := base64.RawURLEncoding.DecodeString(content) 64 if err == nil { 65 a.Values[prefix+"_!kind"] = "base64url" 66 a.extractValue(string(decoded), prefix+"_!base64") 67 return 68 } 69 } 70 { 71 // check if it is json 72 var doc jsonDocument 73 if err := json.Unmarshal([]byte(content), &doc); err == nil { 74 a.Values[prefix+"_!kind"] = "json" 75 a.extractJSON(&doc, prefix) 76 return 77 } 78 } 79 } 80 81 func (a *AbstractNonNativeLink) extractLink(content *url.URL, prefix string) { 82 a.Values[prefix+"_!link"] = content.String() 83 a.Values[prefix+"_!link_protocol"] = content.Scheme 84 a.Values[prefix+"_!link_host"] = content.Host 85 a.extractValue(content.Host, prefix+"_!link_host") 86 a.Values[prefix+"_!link_path"] = content.Path 87 a.Values[prefix+"_!link_query"] = content.RawQuery 88 a.Values[prefix+"_!link_fragment"] = content.Fragment 89 a.Values[prefix+"_!link_userinfo"] = content.User.String() 90 a.Values[prefix+"_!link_opaque"] = content.Opaque 91 } 92 93 func (a *AbstractNonNativeLink) extractJSON(content *jsonDocument, prefix string) { 94 for key, value := range *content { 95 switch value[0] { 96 case '{': 97 a.extractValue(string(value), prefix+"_!json_"+key) 98 case '"': 99 var unquoted string 100 if err := json.Unmarshal(value, &unquoted); err == nil { 101 a.Values[prefix+"_!json_"+key+"_!unquoted"] = unquoted 102 } 103 fallthrough 104 default: 105 a.Values[prefix+"_!json_"+key] = string(value) 106 } 107 } 108 }