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  }