From bb11c5afc6783d137201c0a1ff415ba77a08de31 Mon Sep 17 00:00:00 2001
From: Darien Raymond <admin@v2ray.com>
Date: Sat, 1 Dec 2018 21:06:45 +0100
Subject: [PATCH] support domain property

---
 main.go | 90 +++++++++++++++++++++++++++++++++++++++++++++------------
 1 file changed, 71 insertions(+), 19 deletions(-)

diff --git a/main.go b/main.go
index ad709033..acc3ca4b 100644
--- a/main.go
+++ b/main.go
@@ -7,6 +7,7 @@ import (
 	"io/ioutil"
 	"os"
 	"path/filepath"
+	"strconv"
 	"strings"
 
 	"github.com/gogo/protobuf/proto"
@@ -16,6 +17,7 @@ import (
 type Entry struct {
 	Type  string
 	Value string
+	Attrs []*router.Domain_Attribute
 }
 
 type List struct {
@@ -37,23 +39,27 @@ func (l *ParsedList) toProto() (*router.GeoSite, error) {
 		switch entry.Type {
 		case "domain":
 			site.Domain = append(site.Domain, &router.Domain{
-				Type:  router.Domain_Domain,
-				Value: entry.Value,
+				Type:      router.Domain_Domain,
+				Value:     entry.Value,
+				Attribute: entry.Attrs,
 			})
 		case "regex":
 			site.Domain = append(site.Domain, &router.Domain{
-				Type:  router.Domain_Regex,
-				Value: entry.Value,
+				Type:      router.Domain_Regex,
+				Value:     entry.Value,
+				Attribute: entry.Attrs,
 			})
 		case "keyword":
 			site.Domain = append(site.Domain, &router.Domain{
-				Type:  router.Domain_Plain,
-				Value: entry.Value,
+				Type:      router.Domain_Plain,
+				Value:     entry.Value,
+				Attribute: entry.Attrs,
 			})
 		case "full":
 			site.Domain = append(site.Domain, &router.Domain{
-				Type:  router.Domain_Full,
-				Value: entry.Value,
+				Type:      router.Domain_Full,
+				Value:     entry.Value,
+				Attribute: entry.Attrs,
 			})
 		default:
 			return nil, errors.New("unknown domain type: " + entry.Type)
@@ -70,21 +76,67 @@ func removeComment(line string) string {
 	return strings.TrimSpace(line[:idx])
 }
 
-func parseEntry(line string) (Entry, error) {
-	kv := strings.Split(line, ":")
+func parseDomain(domain string, entry *Entry) error {
+	kv := strings.Split(domain, ":")
 	if len(kv) == 1 {
-		return Entry{
-			Type:  "domain",
-			Value: kv[0],
-		}, nil
+		entry.Type = "domain"
+		entry.Value = strings.ToLower(kv[0])
+		return nil
 	}
+
 	if len(kv) == 2 {
-		return Entry{
-			Type:  strings.ToLower(kv[0]),
-			Value: strings.ToLower(kv[1]),
-		}, nil
+		entry.Type = strings.ToLower(kv[0])
+		entry.Value = strings.ToLower(kv[1])
+		return nil
 	}
-	return Entry{}, errors.New("Invalid format: " + line)
+
+	return errors.New("Invalid format: " + domain)
+}
+
+func parseAttribute(attr string) (router.Domain_Attribute, error) {
+	var attribute router.Domain_Attribute
+	if len(attr) == 0 || attr[0] != '@' {
+		return attribute, errors.New("invalid attribute: " + attr)
+	}
+
+	attr = attr[0:]
+	parts := strings.Split(attr, "=")
+	if len(parts) == 1 {
+		attribute.Key = strings.ToLower(parts[0])
+		attribute.TypedValue = &router.Domain_Attribute_BoolValue{BoolValue: true}
+	} else {
+		attribute.Key = strings.ToLower(parts[0])
+		intv, err := strconv.Atoi(parts[1])
+		if err != nil {
+			return attribute, errors.New("invalid attribute: " + attr + ": " + err.Error())
+		}
+		attribute.TypedValue = &router.Domain_Attribute_IntValue{IntValue: int64(intv)}
+	}
+	return attribute, nil
+}
+
+func parseEntry(line string) (Entry, error) {
+	line = strings.TrimSpace(line)
+	parts := strings.Split(line, " ")
+
+	var entry Entry
+	if len(parts) == 0 {
+		return entry, errors.New("empty entry")
+	}
+
+	if err := parseDomain(parts[0], &entry); err != nil {
+		return entry, err
+	}
+
+	for i := 1; i < len(parts); i++ {
+		attr, err := parseAttribute(parts[i])
+		if err != nil {
+			return entry, err
+		}
+		entry.Attrs = append(entry.Attrs, &attr)
+	}
+
+	return entry, nil
 }
 
 func DetectPath(path string) (string, error) {