MovieNight/settings.go
Zorchenhimer f7b362519d Parse X-Forwarded-For header if provided
Parse the X-Forwarded-For header if it exists in the connection.  This
will allow bans to work if the server is sitting behind an Nginx
reverse proxy (that has been configured to add the header).

Also added a safe-guard to disallow bans for localhost so you cannot
accidentally ban everybody from connecting if the server is behind a
reverse proxy.

This should resolve #49
2019-03-20 16:57:29 -04:00

173 lines
3.5 KiB
Go

package main
import (
"crypto/rand"
"encoding/json"
"fmt"
"io/ioutil"
"math/big"
"strings"
"sync"
"time"
)
var settings *Settings
var settingsMtx sync.Mutex
type Settings struct {
filename string
cmdLineKey string // stream key from the command line
MaxMessageCount int
TitleLength int // maximum length of the title that can be set with the /playing
AdminPassword string
Bans []BanInfo
StreamKey string
ListenAddress string
}
type BanInfo struct {
IP string
Names []string
When time.Time
}
func init() {
var err error
settings, err = LoadSettings("settings.json")
if err != nil {
panic("Unable to load settings: " + err.Error())
}
if len(settings.StreamKey) == 0 {
panic("Missing stream key is settings.json")
}
if settings.TitleLength <= 0 {
settings.TitleLength = 50
}
// Save admin password to file
if err = settings.Save(); err != nil {
panic("Unable to save settings: " + err.Error())
}
}
func LoadSettings(filename string) (*Settings, error) {
raw, err := ioutil.ReadFile(filename)
if err != nil {
return nil, fmt.Errorf("error reading file: %s", err)
}
var s *Settings
err = json.Unmarshal(raw, &s)
if err != nil {
return nil, fmt.Errorf("error unmarshaling: %s", err)
}
s.filename = filename
// have a default of 200
if s.MaxMessageCount == 0 {
s.MaxMessageCount = 300
} else if s.MaxMessageCount < 0 {
return s, fmt.Errorf("value for MaxMessageCount must be greater than 0, given %d", s.MaxMessageCount)
}
s.AdminPassword, err = generatePass(time.Now().Unix())
if err != nil {
return nil, fmt.Errorf("unable to generate admin password: %s", err)
}
fmt.Printf("Settings reloaded. New admin password: %s\n", s.AdminPassword)
return s, nil
}
func generatePass(seed int64) (string, error) {
out := ""
for len(out) < 20 {
num, err := rand.Int(rand.Reader, big.NewInt(int64(15)))
if err != nil {
return "", err
}
out = fmt.Sprintf("%s%X", out, num)
}
return out, nil
}
func (s *Settings) Save() error {
marshaled, err := json.MarshalIndent(s, "", "\t")
if err != nil {
return fmt.Errorf("error marshaling: %s", err)
}
err = ioutil.WriteFile(s.filename, marshaled, 0777)
if err != nil {
return fmt.Errorf("error saving: %s", err)
}
return nil
}
func (s *Settings) AddBan(host string, names []string) error {
if host == "127.0.0.1" {
return fmt.Errorf("Cannot add a ban for localhost.")
}
b := BanInfo{
Names: names,
IP: host,
When: time.Now(),
}
settings.Bans = append(settings.Bans, b)
fmt.Printf("[BAN] %q (%s) has been banned.\n", strings.Join(names, ", "), host)
return settings.Save()
}
func (s *Settings) RemoveBan(name string) error {
defer settingsMtx.Unlock()
settingsMtx.Lock()
name = strings.ToLower(name)
newBans := []BanInfo{}
for _, b := range s.Bans {
for _, n := range b.Names {
if n == name {
fmt.Printf("[ban] Removed ban for %s [%s]\n", b.IP, n)
} else {
newBans = append(newBans, b)
}
}
}
s.Bans = newBans
return settings.Save()
}
func (s *Settings) IsBanned(host string) (bool, []string) {
defer settingsMtx.Unlock()
settingsMtx.Lock()
for _, b := range s.Bans {
if b.IP == host {
return true, b.Names
}
}
return false, nil
}
func (s *Settings) SetTempKey(key string) {
defer settingsMtx.Unlock()
settingsMtx.Lock()
s.cmdLineKey = key
}
func (s *Settings) GetStreamKey() string {
defer settingsMtx.Unlock()
settingsMtx.Lock()
if len(s.cmdLineKey) > 0 {
return s.cmdLineKey
}
return s.StreamKey
}