|
package main |
|
|
|
import ( |
|
"encoding/json" |
|
"flag" |
|
"fmt" |
|
"io/ioutil" |
|
"os" |
|
"reflect" |
|
"strconv" |
|
"strings" |
|
) |
|
|
|
type server_setting struct { |
|
ip string `json:"server"` |
|
port int `json:"server_port"` |
|
password string `json:"password"` |
|
method string `json:"method"` |
|
remarks string `json:"remarks"` |
|
} |
|
type configStruct struct { |
|
server_configs []server_setting `json:"configs"` |
|
strategy string `json:"strategy"` |
|
index int `json:"index"` |
|
global bool `json:"global"` |
|
enabled bool `json:"enabled"` |
|
shareOverLan bool `json:"shareOverLan"` |
|
isDefault bool `json:"isDefault"` |
|
localPort int `json:"localPort"` |
|
pacUrl string `json:"pacUrl"` |
|
useOnlinePac bool `json:"useOnlinePac"` |
|
} |
|
|
|
func (s *server_setting) MarshalJSON() ([]byte, error) { |
|
if s.ip != "" && s.port > 0 { |
|
return json.Marshal(map[string]interface{}{ |
|
"server": s.ip, |
|
"server_port": s.port, |
|
"password": s.password, |
|
"method": s.method, |
|
"remarks": s.remarks, |
|
}) |
|
} |
|
return json.Marshal(nil) |
|
} |
|
|
|
func (c *configStruct) MarshalJSON() ([]byte, error) { |
|
if len(c.server_configs) > 0 { |
|
return json.Marshal(map[string]interface{}{ |
|
"configs": c.server_configs, |
|
"strategy": c.strategy, |
|
"index": c.index, |
|
"global": c.global, |
|
"enabled": c.enabled, |
|
"shareOverLan": c.shareOverLan, |
|
"isDefault": c.isDefault, |
|
"localPort": c.localPort, |
|
"pacUrl": c.pacUrl, |
|
"useOnlinePac": c.useOnlinePac, |
|
}) |
|
} |
|
return json.Marshal(nil) |
|
} |
|
|
|
// type csAlias configStruct |
|
|
|
func (c *configStruct) UnmarshalJSON(b []byte) (err error) { |
|
j := make(map[string]interface{}) |
|
if err = json.Unmarshal(b, &j); err == nil { |
|
if *debug { |
|
fmt.Printf("\n---UNMARSHALLED---\n%v\n---\n", j) |
|
} |
|
var found bool |
|
grp := j["configs"].([]interface{}) |
|
var old_servers []server_setting |
|
for _, g := range grp { |
|
var s server_setting |
|
d := g.(map[string]interface{}) |
|
s.ip = d["server"].(string) |
|
s.port = int(d["server_port"].(float64)) |
|
s.password = d["password"].(string) |
|
s.method = d["method"].(string) |
|
s.remarks = d["remarks"].(string) |
|
old_servers = append(old_servers, s) |
|
} |
|
c.server_configs = old_servers |
|
c.strategy, found = j["strategy"].(string) |
|
if !found { |
|
c.strategy = "" |
|
} |
|
c.index = int(j["index"].(float64)) |
|
c.global = j["global"].(bool) |
|
c.enabled = j["enabled"].(bool) |
|
c.shareOverLan = j["shareOverLan"].(bool) |
|
c.isDefault = j["isDefault"].(bool) |
|
c.localPort = int(j["localPort"].(float64)) |
|
c.pacUrl = j["pacUrl"].(string) |
|
c.useOnlinePac = j["useOnlinePac"].(bool) |
|
if *debug { |
|
fmt.Printf("\n---ASSERTED---\n%v\n---\n", j) |
|
} |
|
return |
|
} |
|
if *debug { |
|
fmt.Printf("\n---FAILED---\n%v\n---\n", j) |
|
} |
|
return |
|
} |
|
|
|
func fill_server(server string, port_range string, prefix string) []server_setting { |
|
if len(server) == 0 || len(port_range) == 0 || len(prefix) == 0 { |
|
return nil |
|
} |
|
var isvalid bool |
|
var start, end int |
|
if isvalid, start, end = parse(port_range); !isvalid { |
|
return nil |
|
} |
|
var result []server_setting = make([]server_setting, end-start+1) |
|
for i := 0; i <= end-start; i++ { |
|
result[i] = server_setting{ |
|
server, |
|
start + i, |
|
"zhujiceping.com", |
|
"aes-256-cfb", |
|
prefix, /* + "//" + server + ":" + strconv.Itoa(start+i)*/ |
|
} |
|
} |
|
return result |
|
} |
|
|
|
func parse(port_range string) (isvalid bool, start int, end int) { |
|
if len(port_range) == 0 { |
|
return false, 0, 0 |
|
} |
|
isvalid = false |
|
var e error |
|
if strings.Contains(port_range, "-") { |
|
parts := strings.SplitN(port_range, "-", 2) |
|
start, e = strconv.Atoi(parts[0]) |
|
if start < 1 || e != nil { |
|
return false, 0, 0 |
|
} |
|
end, e = strconv.Atoi(parts[1]) |
|
if end < 1 || end < start || e != nil { |
|
return false, 0, 0 |
|
} |
|
isvalid = true |
|
} else if start, e = strconv.Atoi(port_range); e == nil && start > 0 { |
|
isvalid = true |
|
end = start |
|
} else { |
|
return false, 0, 0 |
|
} |
|
return isvalid, start, end |
|
} |
|
|
|
func fill_server_group(servers []string, port_range string, prefix string) [][]server_setting { |
|
var result [][]server_setting |
|
for _, s := range servers { |
|
result = append(result, fill_server(s, port_range, prefix)) |
|
} |
|
return result |
|
} |
|
|
|
var debug *bool |
|
|
|
var usages string = ` |
|
usage: |
|
|
|
go run add_servers.go -s SERVER_IP -p SERVER_PORT -c COMMENT |
|
|
|
|
|
-s server or server group. example: 1.2.3.4 or 1.2.3.4-20 |
|
-p port or port range. example: 80 or 8000-8080 |
|
-c comment for server, i.e. server name |
|
` |
|
|
|
func main() { |
|
debug = flag.Bool("d", false, "debug") |
|
server := flag.String("s", "", "1.2.3.4 or 1.2.3.4-20") |
|
ports := flag.String("p", "", "80 or 80-8000") |
|
comment := flag.String("c", "", "comment for this server (group)") |
|
flag.Parse() |
|
if len(*server) == 0 || len(*ports) == 0 || len(*comment) == 0 { |
|
if *debug { |
|
fmt.Printf("s:", *server) |
|
fmt.Printf("p:", *ports) |
|
fmt.Printf("c:", *comment) |
|
} |
|
fmt.Println(usages) |
|
os.Exit(1) |
|
} |
|
|
|
data, err := ioutil.ReadFile("gui-config.json") |
|
if err != nil { |
|
fmt.Println("error reading gui-config.json: %v", err) |
|
} |
|
if *debug { |
|
fmt.Printf("read file %v bytes\n", len(data)) |
|
} |
|
var guiConfig configStruct |
|
|
|
if err := guiConfig.UnmarshalJSON(data); err != nil { |
|
panic(err) |
|
} |
|
|
|
if *debug { |
|
fmt.Printf("loaded %d configs\n", len(guiConfig.server_configs)) |
|
fmt.Println("--------- this is what i've read ------------") |
|
fmt.Printf("%v\n\n\n\n", guiConfig) |
|
} |
|
|
|
if strings.Contains(*server, "-") { |
|
var bundles []string |
|
parts := strings.SplitN(*server, ".", 4) |
|
if *debug { |
|
fmt.Printf("parts are: %v\n", parts) |
|
} |
|
if ok, s, e := parse(parts[3]); ok { |
|
for i := 0; i <= e-s; i++ { |
|
bundles = append(bundles, join_ip(parts[0], parts[1], parts[2], strconv.Itoa(s+i))) |
|
} |
|
} |
|
if *debug { |
|
fmt.Printf("bundles are: %v \n", bundles) |
|
} |
|
server_groups := fill_server_group(bundles, *ports, *comment) |
|
for _, s := range server_groups { |
|
guiConfig.server_configs = append(guiConfig.server_configs, s...) |
|
} |
|
if *debug { |
|
fmt.Printf("now configs are %d \n", len(guiConfig.server_configs)) |
|
} |
|
} else { |
|
guiConfig.server_configs = append(guiConfig.server_configs, fill_server(*server, *ports, *comment)...) |
|
} |
|
|
|
file, err := os.OpenFile("gui-config.json", os.O_RDWR, 0666) |
|
if err != nil { |
|
panic(err) |
|
} |
|
var output []byte |
|
if *debug { |
|
output, _ = guiConfig.server_configs[0].MarshalJSON() |
|
fmt.Printf("debug marshalling server_configs[0]: the original object is %v (of %q); and marshaled object is %v. \n", guiConfig.server_configs[0], reflect.TypeOf(guiConfig.server_configs[0]), string(output)) |
|
output, _ = json.Marshal(guiConfig.server_configs) |
|
fmt.Printf("debug marshalling []server_configs: %v\n", string(output)) |
|
} |
|
output, _ = guiConfig.MarshalJSON() |
|
if *debug { |
|
fmt.Printf("ready to write %d bytes \n", len(output)) |
|
fmt.Printf("\n\n the intended output is \n\n %v \n", string(output)) |
|
} |
|
if len(output) < len(guiConfig.server_configs) { |
|
panic("there is no way that the length of marshalled output is even smalller than number of configs in the struct") |
|
} |
|
// need to clear file content first, to avoid under-patching |
|
|
|
n, err := file.Write(output) |
|
if err != nil { |
|
fmt.Printf("error on write: %v", err) |
|
os.Exit(1) |
|
} |
|
if n < len(output) { |
|
fmt.Printf("why only %d written? \n", n) |
|
} |
|
} |
|
|
|
func join_ip(a, b, c, d string) string { |
|
sep := "." |
|
return a + sep + b + sep + c + sep + d |
|
} |