227 lines
5 KiB
Go
227 lines
5 KiB
Go
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"forge.lightcrystal.systems/nilix/quartzgun/cookie"
|
|
)
|
|
|
|
type Config struct {
|
|
Port int
|
|
Uploads string
|
|
UploadMaxMB int
|
|
MongoURI string
|
|
RegistrationSecret string
|
|
RegistrationSalt []byte
|
|
}
|
|
|
|
func GetConfigLocation() string {
|
|
home := os.Getenv("HOME")
|
|
appdata := os.Getenv("APPDATA")
|
|
switch runtime.GOOS {
|
|
case "windows":
|
|
return filepath.Join(appdata, "felt")
|
|
case "darwin":
|
|
return filepath.Join(home, "Library", "Application Support", "felt")
|
|
case "plan9":
|
|
return filepath.Join(home, "lib", "felt")
|
|
default:
|
|
return filepath.Join(home, ".config", "felt")
|
|
}
|
|
}
|
|
|
|
func ensureConfigLocationExists() {
|
|
fileInfo, err := os.Stat(GetConfigLocation())
|
|
|
|
if os.IsNotExist(err) {
|
|
os.MkdirAll(GetConfigLocation(), os.ModePerm)
|
|
} else if !fileInfo.IsDir() {
|
|
panic("Config location is not a directory!")
|
|
}
|
|
}
|
|
|
|
func ReadConfig() *Config {
|
|
ensureConfigLocationExists()
|
|
self := parseConfig(filepath.Join(GetConfigLocation(), "felt.conf"))
|
|
bytes, err := getSecrets(filepath.Join(GetConfigLocation(), "enclave"))
|
|
if err == nil {
|
|
self.RegistrationSalt = bytes[0:16]
|
|
self.RegistrationSecret = string(bytes[16:48])
|
|
} else {
|
|
fmt.Println(err.Error())
|
|
}
|
|
return self
|
|
}
|
|
|
|
func (self *Config) Write() error {
|
|
ensureConfigLocationExists()
|
|
return writeConfig(self, filepath.Join(GetConfigLocation(), "felt.conf"))
|
|
}
|
|
|
|
func (self *Config) IsNull() bool {
|
|
return self.Port == 0 || self.UploadMaxMB == 0 || self.Uploads == "" || len(self.RegistrationSalt) != 16 || len(self.RegistrationSecret) != 32
|
|
}
|
|
|
|
func (self *Config) RunWizard() {
|
|
fmt.Printf("All options are required.\n")
|
|
defer func(cfg *Config) {
|
|
if r := recover(); r != nil {
|
|
fmt.Printf("Invalid selection, starting over...")
|
|
cfg.RunWizard()
|
|
}
|
|
}(self)
|
|
inputBuf := ""
|
|
|
|
fmt.Printf("MongoDB URI? ")
|
|
ensureNonEmptyOption(&inputBuf)
|
|
self.MongoURI = inputBuf
|
|
|
|
inputBuf = ""
|
|
fmt.Printf("TCP port? ")
|
|
self.Port = ensurePortOption(&inputBuf)
|
|
|
|
fmt.Printf("file uploads location? ")
|
|
ensureNonEmptyOption(&inputBuf)
|
|
self.Uploads = inputBuf
|
|
|
|
inputBuf = ""
|
|
fmt.Printf("Max file upload size (MB)? ")
|
|
self.UploadMaxMB = ensureNumberOption(&inputBuf)
|
|
|
|
setSecrets([]byte(cookie.GenToken(48)), filepath.Join(GetConfigLocation(), "enclave"))
|
|
|
|
fmt.Printf("Configuration complete! Registration secrets have been updated as well!\n")
|
|
self.Write()
|
|
}
|
|
|
|
func ensureNonEmptyOption(buffer *string) {
|
|
for {
|
|
fmt.Scanln(buffer)
|
|
if len(strings.TrimSpace(*buffer)) != 0 {
|
|
break
|
|
}
|
|
fmt.Println("Please enter a nonempty value")
|
|
}
|
|
}
|
|
|
|
func ensureBooleanOption(buffer *string) bool {
|
|
for {
|
|
fmt.Scanln(buffer)
|
|
trimmedBuf := strings.TrimSpace(*buffer)
|
|
v, err := strconv.ParseBool(trimmedBuf)
|
|
if err == nil {
|
|
return v
|
|
}
|
|
fmt.Println("Please enter a true or false value")
|
|
}
|
|
}
|
|
|
|
func ensureNumberOption(buffer *string) int {
|
|
for {
|
|
fmt.Scanln(buffer)
|
|
trimmedBuf := strings.TrimSpace(*buffer)
|
|
v, err := strconv.ParseInt(trimmedBuf, 10, 32)
|
|
if err == nil && v > 0 {
|
|
return int(v)
|
|
}
|
|
fmt.Println("Please enter a positive integer")
|
|
}
|
|
}
|
|
|
|
func ensurePortOption(buffer *string) int {
|
|
for {
|
|
fmt.Scanln(buffer)
|
|
trimmedBuf := strings.TrimSpace(*buffer)
|
|
v, err := strconv.ParseInt(trimmedBuf, 10, 32)
|
|
if err == nil && v > 0 && v <= 65536 {
|
|
return int(v)
|
|
}
|
|
fmt.Println("Please enter a valid port [1, 65536]")
|
|
}
|
|
}
|
|
|
|
func writeConfig(cfg *Config, configFile string) error {
|
|
f, err := os.Create(configFile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
f.WriteString("mongoURI=" + cfg.MongoURI + "\n")
|
|
f.WriteString("uploads=" + cfg.Uploads + "\n")
|
|
f.WriteString("uploadMaxMB=" + strconv.FormatInt(int64(cfg.UploadMaxMB), 10) + "\n")
|
|
f.WriteString("port=" + strconv.FormatInt(int64(cfg.Port), 10) + "\n")
|
|
return nil
|
|
}
|
|
|
|
func setSecrets(secret []byte, secretFile string) error {
|
|
f, err := os.Create(secretFile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
f.Write(secret)
|
|
return nil
|
|
}
|
|
|
|
func getSecrets(secretFile string) ([]byte, error) {
|
|
f, err := os.ReadFile(secretFile)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(f) != 48 {
|
|
return nil, err
|
|
}
|
|
|
|
return f[:], nil
|
|
}
|
|
|
|
func parseConfig(configFile string) *Config {
|
|
f, err := os.ReadFile(configFile)
|
|
cfg := &Config{}
|
|
if err != nil {
|
|
return cfg
|
|
}
|
|
|
|
fileData := string(f[:])
|
|
|
|
lines := strings.Split(fileData, "\n")
|
|
|
|
for _, l := range lines {
|
|
if len(l) == 0 {
|
|
continue
|
|
}
|
|
if !strings.Contains(l, "=") {
|
|
panic("Malformed config not in INI format")
|
|
}
|
|
|
|
kvp := strings.Split(l, "=")
|
|
k := strings.TrimSpace(kvp[0])
|
|
v := strings.TrimSpace(kvp[1])
|
|
switch k {
|
|
case "registrationSecret":
|
|
cfg.RegistrationSecret = v
|
|
case "mongoURI":
|
|
cfg.MongoURI = v
|
|
case "uploads":
|
|
cfg.Uploads = v
|
|
case "port":
|
|
port, _ := strconv.ParseInt(v, 10, 32)
|
|
cfg.Port = int(port)
|
|
case "uploadMaxMB":
|
|
maxUpload, _ := strconv.ParseInt(v, 10, 32)
|
|
cfg.UploadMaxMB = int(maxUpload)
|
|
default:
|
|
panic("Unrecognized config option: " + k)
|
|
}
|
|
}
|
|
return cfg
|
|
}
|