package config import ( "bufio" "fmt" "os" "strconv" "strings" ) type AccountConfig struct { Username string Password string Role string Default string None []string Read []string Write []string Delete []string } type Config struct { Path []string Port int Allow string Rules []string Log string FileRemove int ImagePreview bool FolderDownload string SessionTimeout int HTMLTitle string HTMLNotice string UserFile string UserHome string SSLCert string SSLKey string Accounts []AccountConfig } func Default() Config { return Config{Path: []string{"."}, Port: 80, FileRemove: 1, ImagePreview: true, FolderDownload: "enable", SessionTimeout: 30, UserHome: "users"} } func Load(path string) (Config, error) { c := Default() f, err := os.Open(path) if err != nil { return c, err } defer f.Close() s := bufio.NewScanner(f) var section *AccountConfig for s.Scan() { line := strings.TrimSpace(s.Text()) if line == "" || strings.HasPrefix(line, ";") || strings.HasPrefix(line, "#") { continue } if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") { name := strings.TrimSpace(line[1 : len(line)-1]) if name != "" { c.Accounts = append(c.Accounts, AccountConfig{Username: name}) section = &c.Accounts[len(c.Accounts)-1] } else { section = nil } continue } i := strings.IndexByte(line, '=') if i < 0 { continue } key, val := strings.TrimSpace(line[:i]), strings.TrimSpace(line[i+1:]) val = strings.Trim(val, "\\\"") if section != nil { switch strings.ToLower(key) { case "password": section.Password = val case "role": section.Role = strings.ToLower(val) case "rule.default": section.Default = val case "rule.none": section.None = appendNonEmpty(section.None, val) case "rule.r": section.Read = appendNonEmpty(section.Read, val) case "rule.w": section.Write = appendNonEmpty(section.Write, val) case "rule.d": section.Delete = appendNonEmpty(section.Delete, val) } continue } switch strings.ToLower(key) { case "path": c.Path = splitPath(val) case "port": if n, e := strconv.Atoi(val); e == nil && n > 0 && n < 65536 { c.Port = n } case "allow": c.Allow = val case "rule": if val != "" { c.Rules = append(c.Rules, val) } case "log": c.Log = val case "file.remove": if n, e := strconv.Atoi(val); e == nil { c.FileRemove = n } case "image.preview": c.ImagePreview = parseBool(val, c.ImagePreview) case "folder.download": c.FolderDownload = strings.ToLower(val) case "folder.leaf.download": if parseBool(val, false) { c.FolderDownload = "leaf" } case "session.timeout": if n, e := strconv.Atoi(val); e == nil && n > 0 { c.SessionTimeout = n } case "html.title": c.HTMLTitle = val case "html.notice": c.HTMLNotice = val case "user.file": c.UserFile = val case "user.home": c.UserHome = strings.Trim(val, "/") case "ssl.cert": c.SSLCert = val case "ssl.key": c.SSLKey = val } } if err := s.Err(); err != nil { return c, err } return c, nil } func splitPath(v string) []string { var out []string for _, p := range strings.Split(v, "|") { p = strings.TrimSpace(strings.Trim(p, "\\\"")) if p != "" { out = append(out, p) } } if len(out) == 0 { return []string{"."} } return out } func parseBool(v string, d bool) bool { switch strings.ToLower(strings.TrimSpace(v)) { case "true", "1", "yes", "on": return true case "false", "0", "no", "off": return false } return d } func (c Config) Validate() error { if len(c.Path) == 0 { return fmt.Errorf("path is empty") } if c.Port < 1 || c.Port > 65535 { return fmt.Errorf("invalid port") } return nil } func appendNonEmpty(dst []string, value string) []string { for _, item := range strings.Split(value, "|") { item = strings.TrimSpace(item) if item != "" { dst = append(dst, item) } } return dst }