package config import ( "encoding/json" "os" "github.com/rs/zerolog/log" ) // girc.Config can't be marshalled by JSON so we have to recreate our own type IRCConfig struct { Server string ServerPass string Port int Nick string User string Name string SSL bool AllowFlood bool // Not part of GIRC config Channel string } type DomainConfig struct { Domain string IRCEnabled bool IRC string } type SMTPConfig struct { Domain string PlainEnabled bool PlainBind string TLSEnabled bool TLSBind string TLSCert string TLSKey string } type MailholeConfig struct { IRC map[string]*IRCConfig Domains []*DomainConfig SMTP *SMTPConfig WebUIBase string } const CONFIG_PATH = `/etc/mailhole/mailhole.json` func LoadConfig() *MailholeConfig { // We do the error handling inside the config loader so we don't have to // do it for each subcommand if _, err := os.Stat(CONFIG_PATH); os.IsNotExist(err) { log.Error().Msg("You need to create a config file for Mailhole to read.") log.Error().Msg("Get an example: ./mailhole firstrun > /etc/mailhole/mailhole.json") log.Fatal().Stack().Err(err).Msg("Config file does not exist") } else { var configBytes []byte if configBytes, err = os.ReadFile(CONFIG_PATH); err != nil { log.Fatal().Stack().Err(err).Msg("Could not open config file") } uC := &MailholeConfig{} if err := json.Unmarshal(configBytes, uC); err != nil { log.Fatal().Err(err).Msg("Could not unmarshal config file") } // Sanity checks // 1. Do we have any domains with IRC servers that don't exist for _, v := range uC.Domains { if !v.IRCEnabled { continue } if _, ok := uC.IRC[v.IRC]; !ok { log.Fatal(). Str("domain", v.Domain). Str("irc", v.IRC). Msg("Domain failed sanity check. Named IRC does not exist in config.") } } return uC } return nil } func ExampleConfig() []byte { t := MailholeConfig{ IRC: map[string]*IRCConfig{ "ircname": { Server: "irc.yourirc.com", ServerPass: "Password", Port: 6697, Nick: "mailhole", User: "mailhole", Name: "mailhole", SSL: true, AllowFlood: true, Channel: "#mailhole", }, }, Domains: []*DomainConfig{ { Domain: "yourmaildomain.com", IRC: "ircname", IRCEnabled: true, }, { Domain: "yoursecondmaildomain.com", IRC: "ircname", IRCEnabled: true, }, }, SMTP: &SMTPConfig{ Domain: "mail.yourmaildomain.com", PlainEnabled: true, PlainBind: "127.0.0.1:25", TLSEnabled: true, TLSBind: "127.0.0.1:587", TLSCert: "/etc/letsencrypt/live/mail.yourmaildomain.com/fullchain.pem", TLSKey: "/etc/letsencrypt/live/mail.yourmaildomain.com/privkey.pem", }, WebUIBase: "http://127.0.0.1:8000", } if b, err := json.MarshalIndent(t, "", " "); err != nil { log.Error().Stack().Err(err).Msg("Failed to marshal config") return []byte(``) } else { return b } }