2016-06-15 13:50:56 +02:00
// Copyright (c) 2012-2014 Jeremy Latt
// Copyright (c) 2014-2015 Edmund Huber
2017-03-27 14:15:02 +02:00
// Copyright (c) 2016-2017 Daniel Oaks <daniel@danieloaks.net>
2016-06-15 13:50:56 +02:00
// released under the MIT license
2014-02-09 16:53:42 +01:00
package irc
import (
2016-04-13 12:45:09 +02:00
"crypto/tls"
2016-10-23 02:47:11 +02:00
"fmt"
2016-04-12 15:00:09 +02:00
"io/ioutil"
2014-02-24 07:21:39 +01:00
"log"
2019-02-05 06:19:03 +01:00
"net"
2018-08-28 19:34:43 +02:00
"os"
2018-04-19 08:48:19 +02:00
"regexp"
2019-04-08 03:36:48 +02:00
"sort"
2019-08-27 06:51:09 +02:00
"strconv"
2016-10-23 03:01:05 +02:00
"strings"
2017-01-12 08:40:01 +01:00
"time"
2016-04-12 15:00:09 +02:00
2017-10-05 16:03:53 +02:00
"code.cloudfoundry.org/bytefmt"
2019-08-27 06:51:09 +02:00
"github.com/oragono/oragono/irc/caps"
2019-05-12 10:01:47 +02:00
"github.com/oragono/oragono/irc/cloaks"
2017-10-09 23:37:13 +02:00
"github.com/oragono/oragono/irc/connection_limits"
2017-06-14 20:00:53 +02:00
"github.com/oragono/oragono/irc/custime"
2019-05-10 06:27:28 +02:00
"github.com/oragono/oragono/irc/isupport"
2018-02-03 10:46:14 +01:00
"github.com/oragono/oragono/irc/languages"
2017-06-14 20:00:53 +02:00
"github.com/oragono/oragono/irc/logger"
2018-04-23 00:47:10 +02:00
"github.com/oragono/oragono/irc/modes"
2017-10-05 16:03:53 +02:00
"github.com/oragono/oragono/irc/passwd"
2017-10-05 15:47:43 +02:00
"github.com/oragono/oragono/irc/utils"
2016-04-12 15:00:09 +02:00
"gopkg.in/yaml.v2"
2014-02-09 16:53:42 +01:00
)
2018-07-16 09:46:40 +02:00
// here's how this works: exported (capitalized) members of the config structs
// are defined in the YAML file and deserialized directly from there. They may
// be postprocessed and overwritten by LoadConfig. Unexported (lowercase) members
// are derived from the exported members in LoadConfig.
2014-03-01 23:34:51 +01:00
2017-04-16 03:31:33 +02:00
// TLSListenConfig defines configuration options for listening on TLS.
2016-04-28 12:12:23 +02:00
type TLSListenConfig struct {
2019-11-20 23:14:42 +01:00
Cert string
Key string
Proxy bool
2016-04-13 12:45:09 +02:00
}
2019-06-28 16:45:34 +02:00
// This is the YAML-deserializable type of the value of the `Server.Listeners` map
type listenerConfigBlock struct {
2019-08-27 06:51:09 +02:00
TLS TLSListenConfig
Tor bool
STSOnly bool ` yaml:"sts-only" `
2019-06-28 16:45:34 +02:00
}
2019-06-18 04:21:37 +02:00
// listenerConfig is the config governing a particular listener (bound address),
// in particular whether it has TLS or Tor (or both) enabled.
type listenerConfig struct {
2019-11-20 23:43:40 +01:00
TLSConfig * tls . Config
Tor bool
STSOnly bool
ProxyBeforeTLS bool
2016-04-13 12:45:09 +02:00
}
2018-02-11 11:30:40 +01:00
type AccountConfig struct {
2018-02-18 10:46:14 +01:00
Registration AccountRegistrationConfig
2019-01-01 22:45:37 +01:00
AuthenticationEnabled bool ` yaml:"authentication-enabled" `
2019-02-05 06:19:03 +01:00
RequireSasl struct {
Enabled bool
Exempted [ ] string
exemptedNets [ ] net . IPNet
} ` yaml:"require-sasl" `
LoginThrottling struct {
2019-01-01 22:45:37 +01:00
Enabled bool
Duration time . Duration
MaxAttempts int ` yaml:"max-attempts" `
} ` yaml:"login-throttling" `
SkipServerPassword bool ` yaml:"skip-server-password" `
NickReservation NickReservationConfig ` yaml:"nick-reservation" `
2019-04-12 06:08:46 +02:00
Bouncer struct {
Enabled bool
AllowedByDefault bool ` yaml:"allowed-by-default" `
}
VHosts VHostConfig
2018-02-11 11:30:40 +01:00
}
2017-04-16 03:31:33 +02:00
// AccountRegistrationConfig controls account registration.
2016-09-04 11:25:33 +02:00
type AccountRegistrationConfig struct {
2018-02-11 11:30:40 +01:00
Enabled bool
EnabledCallbacks [ ] string ` yaml:"enabled-callbacks" `
EnabledCredentialTypes [ ] string ` yaml:"-" `
VerifyTimeout time . Duration ` yaml:"verify-timeout" `
Callbacks struct {
2016-09-04 11:25:33 +02:00
Mailto struct {
Server string
Port int
TLS struct {
Enabled bool
InsecureSkipVerify bool ` yaml:"insecure_skip_verify" `
ServerName string ` yaml:"servername" `
}
Username string
Password string
Sender string
VerifyMessageSubject string ` yaml:"verify-message-subject" `
VerifyMessage string ` yaml:"verify-message" `
}
}
2018-08-15 19:02:05 +02:00
BcryptCost uint ` yaml:"bcrypt-cost" `
2016-09-04 11:25:33 +02:00
}
2018-04-23 08:38:35 +02:00
type VHostConfig struct {
Enabled bool
MaxLength int ` yaml:"max-length" `
ValidRegexpRaw string ` yaml:"valid-regexp" `
ValidRegexp * regexp . Regexp
UserRequests struct {
Enabled bool
Channel string
Cooldown time . Duration
} ` yaml:"user-requests" `
2018-04-19 08:48:19 +02:00
}
2019-05-19 10:27:44 +02:00
type NickEnforcementMethod int
2018-02-18 10:46:14 +01:00
const (
2019-05-19 10:27:44 +02:00
// NickEnforcementOptional is the zero value; it serializes to
2019-01-02 16:08:44 +01:00
// "optional" in the yaml config, and "default" as an arg to `NS ENFORCE`.
// in both cases, it means "defer to the other source of truth", i.e.,
// in the config, defer to the user's custom setting, and as a custom setting,
2019-05-19 10:27:44 +02:00
// defer to the default in the config. if both are NickEnforcementOptional then
2019-01-02 16:08:44 +01:00
// there is no enforcement.
2019-05-19 10:27:44 +02:00
// XXX: these are serialized as numbers in the database, so beware of collisions
// when refactoring (any numbers currently in use must keep their meanings, or
// else be fixed up by a schema change)
NickEnforcementOptional NickEnforcementMethod = iota
NickEnforcementNone
NickEnforcementWithTimeout
NickEnforcementStrict
2018-02-18 10:46:14 +01:00
)
2019-05-19 10:27:44 +02:00
func nickReservationToString ( method NickEnforcementMethod ) string {
2019-01-02 16:08:44 +01:00
switch method {
2019-05-19 10:27:44 +02:00
case NickEnforcementOptional :
2019-01-02 16:08:44 +01:00
return "default"
2019-05-19 10:27:44 +02:00
case NickEnforcementNone :
2019-01-02 16:08:44 +01:00
return "none"
2019-05-19 10:27:44 +02:00
case NickEnforcementWithTimeout :
2019-01-02 16:08:44 +01:00
return "timeout"
2019-05-19 10:27:44 +02:00
case NickEnforcementStrict :
2019-01-02 16:08:44 +01:00
return "strict"
default :
return ""
}
}
2019-05-19 10:27:44 +02:00
func nickReservationFromString ( method string ) ( NickEnforcementMethod , error ) {
switch strings . ToLower ( method ) {
2019-01-02 16:08:44 +01:00
case "default" :
2019-05-19 10:27:44 +02:00
return NickEnforcementOptional , nil
2019-01-02 16:08:44 +01:00
case "optional" :
2019-05-19 10:27:44 +02:00
return NickEnforcementOptional , nil
2019-01-02 16:08:44 +01:00
case "none" :
2019-05-19 10:27:44 +02:00
return NickEnforcementNone , nil
2019-01-02 16:08:44 +01:00
case "timeout" :
2019-05-19 10:27:44 +02:00
return NickEnforcementWithTimeout , nil
2019-01-02 16:08:44 +01:00
case "strict" :
2019-05-19 10:27:44 +02:00
return NickEnforcementStrict , nil
2019-01-02 16:08:44 +01:00
default :
2019-05-19 10:27:44 +02:00
return NickEnforcementOptional , fmt . Errorf ( "invalid nick-reservation.method value: %s" , method )
2019-01-02 16:08:44 +01:00
}
}
2019-05-19 10:27:44 +02:00
func ( nr * NickEnforcementMethod ) UnmarshalYAML ( unmarshal func ( interface { } ) error ) error {
var orig string
2018-02-18 10:46:14 +01:00
var err error
if err = unmarshal ( & orig ) ; err != nil {
return err
}
2019-05-19 10:27:44 +02:00
method , err := nickReservationFromString ( orig )
2019-01-02 16:08:44 +01:00
if err == nil {
* nr = method
2018-02-18 10:46:14 +01:00
}
2019-01-02 16:08:44 +01:00
return err
2018-02-18 10:46:14 +01:00
}
type NickReservationConfig struct {
2019-01-02 16:08:44 +01:00
Enabled bool
AdditionalNickLimit int ` yaml:"additional-nick-limit" `
2019-05-19 10:27:44 +02:00
Method NickEnforcementMethod
2019-01-02 16:08:44 +01:00
AllowCustomEnforcement bool ` yaml:"allow-custom-enforcement" `
RenameTimeout time . Duration ` yaml:"rename-timeout" `
RenamePrefix string ` yaml:"rename-prefix" `
2018-02-18 10:46:14 +01:00
}
2017-04-16 03:31:33 +02:00
// ChannelRegistrationConfig controls channel registration.
2017-03-24 03:52:38 +01:00
type ChannelRegistrationConfig struct {
2019-02-06 10:32:04 +01:00
Enabled bool
MaxChannelsPerAccount int ` yaml:"max-channels-per-account" `
2017-03-24 03:52:38 +01:00
}
2017-04-16 03:31:33 +02:00
// OperClassConfig defines a specific operator class.
2016-10-23 02:47:11 +02:00
type OperClassConfig struct {
Title string
2016-10-23 03:01:05 +02:00
WhoisLine string
2016-10-23 02:47:11 +02:00
Extends string
Capabilities [ ] string
}
2017-04-16 03:31:33 +02:00
// OperConfig defines a specific operator's configuration.
2016-10-23 02:47:11 +02:00
type OperConfig struct {
2019-12-19 02:33:50 +01:00
Class string
Vhost string
WhoisLine string ` yaml:"whois-line" `
Password string
Fingerprint string
2019-12-19 12:33:43 +01:00
Auto bool
2019-12-19 02:33:50 +01:00
Modes string
2016-10-23 02:47:11 +02:00
}
2017-04-16 03:31:33 +02:00
// LineLenConfig controls line lengths.
2018-07-16 09:46:40 +02:00
type LineLenLimits struct {
2017-01-13 15:22:42 +01:00
Rest int
}
2018-07-16 09:46:40 +02:00
// Various server-enforced limits on data size.
type Limits struct {
2019-05-22 22:15:59 +02:00
AwayLen int ` yaml:"awaylen" `
ChanListModes int ` yaml:"chan-list-modes" `
ChannelLen int ` yaml:"channellen" `
IdentLen int ` yaml:"identlen" `
KickLen int ` yaml:"kicklen" `
LineLen LineLenLimits ` yaml:"linelen" `
MonitorEntries int ` yaml:"monitor-entries" `
NickLen int ` yaml:"nicklen" `
TopicLen int ` yaml:"topiclen" `
WhowasEntries int ` yaml:"whowas-entries" `
RegistrationMessages int ` yaml:"registration-messages" `
2018-07-16 09:46:40 +02:00
}
2017-04-16 03:31:33 +02:00
// STSConfig controls the STS configuration/
2017-03-09 10:07:35 +01:00
type STSConfig struct {
Enabled bool
Duration time . Duration ` yaml:"duration-real" `
DurationString string ` yaml:"duration" `
Port int
Preload bool
2019-08-27 06:51:09 +02:00
STSOnlyBanner string ` yaml:"sts-only-banner" `
bannerLines [ ] string
2017-03-09 10:07:35 +01:00
}
// Value returns the STS value to advertise in CAP
func ( sts * STSConfig ) Value ( ) string {
2018-01-28 01:52:07 +01:00
val := fmt . Sprintf ( "duration=%d" , int ( sts . Duration . Seconds ( ) ) )
2017-03-09 10:07:35 +01:00
if sts . Enabled && sts . Port > 0 {
val += fmt . Sprintf ( ",port=%d" , sts . Port )
}
if sts . Enabled && sts . Preload {
val += ",preload"
}
return val
}
2018-03-22 16:04:21 +01:00
type FakelagConfig struct {
Enabled bool
Window time . Duration
BurstLimit uint ` yaml:"burst-limit" `
MessagesPerWindow uint ` yaml:"messages-per-window" `
2018-03-28 19:18:08 +02:00
Cooldown time . Duration
2018-03-22 16:04:21 +01:00
}
2019-02-26 03:50:43 +01:00
type TorListenersConfig struct {
2019-06-28 16:45:34 +02:00
Listeners [ ] string // legacy only
RequireSasl bool ` yaml:"require-sasl" `
2019-02-26 03:50:43 +01:00
Vhost string
MaxConnections int ` yaml:"max-connections" `
ThrottleDuration time . Duration ` yaml:"throttle-duration" `
MaxConnectionsPerDuration int ` yaml:"max-connections-per-duration" `
}
2017-04-16 03:31:33 +02:00
// Config defines the overall configuration.
2014-02-09 16:53:42 +01:00
type Config struct {
2016-04-12 07:44:00 +02:00
Network struct {
Name string
}
2014-03-01 23:34:51 +01:00
Server struct {
2019-06-28 16:45:34 +02:00
Password string
passwordBytes [ ] byte
Name string
nameCasefolded string
// Listeners is the new style for configuring listeners:
Listeners map [ string ] listenerConfigBlock
UnixBindMode os . FileMode ` yaml:"unix-bind-mode" `
TorListeners TorListenersConfig ` yaml:"tor-listeners" `
// Listen and TLSListeners are the legacy style:
Listen [ ] string
TLSListeners map [ string ] TLSListenConfig ` yaml:"tls-listeners" `
// either way, the result is this:
2019-12-17 21:10:23 +01:00
trueListeners map [ string ] listenerConfig
STS STSConfig
LookupHostnames * bool ` yaml:"lookup-hostnames" `
lookupHostnames bool
ForwardConfirmHostnames bool ` yaml:"forward-confirm-hostnames" `
CheckIdent bool ` yaml:"check-ident" `
MOTD string
motdLines [ ] string
MOTDFormatting bool ` yaml:"motd-formatting" `
ProxyAllowedFrom [ ] string ` yaml:"proxy-allowed-from" `
proxyAllowedFromNets [ ] net . IPNet
WebIRC [ ] webircConfig ` yaml:"webirc" `
MaxSendQString string ` yaml:"max-sendq" `
MaxSendQBytes int
AllowPlaintextResume bool ` yaml:"allow-plaintext-resume" `
Compatibility struct {
2019-05-09 20:18:30 +02:00
ForceTrailing * bool ` yaml:"force-trailing" `
forceTrailing bool
SendUnprefixedSasl bool ` yaml:"send-unprefixed-sasl" `
}
2019-11-18 07:42:48 +01:00
isupport isupport . List
IPLimits connection_limits . LimiterConfig ` yaml:"ip-limits" `
Cloaks cloaks . CloakConfig ` yaml:"ip-cloaking" `
supportedCaps * caps . Set
capValues caps . Values
2014-03-01 23:34:51 +01:00
}
2014-02-25 20:11:34 +01:00
2018-01-21 07:11:16 +01:00
Languages struct {
Enabled bool
Path string
2018-01-22 08:30:31 +01:00
Default string
2018-01-21 07:11:16 +01:00
}
2019-02-19 08:54:57 +01:00
languageManager * languages . Manager
2016-09-04 11:25:33 +02:00
Datastore struct {
2018-04-16 22:28:31 +02:00
Path string
2018-04-20 09:57:48 +02:00
AutoUpgrade bool
2016-09-04 11:25:33 +02:00
}
2018-02-11 11:30:40 +01:00
Accounts AccountConfig
2016-09-04 11:25:33 +02:00
2017-03-24 03:52:38 +01:00
Channels struct {
2019-02-06 10:55:05 +01:00
DefaultModes * string ` yaml:"default-modes" `
defaultModes modes . Modes
2019-05-30 11:33:59 +02:00
MaxChannelsPerClient int ` yaml:"max-channels-per-client" `
OpOnlyCreation bool ` yaml:"operator-only-creation" `
2019-02-06 10:55:05 +01:00
Registration ChannelRegistrationConfig
2017-03-24 03:52:38 +01:00
}
2016-10-23 02:47:11 +02:00
OperClasses map [ string ] * OperClassConfig ` yaml:"oper-classes" `
Opers map [ string ] * OperConfig
2014-03-13 09:55:46 +01:00
2018-07-16 09:46:40 +02:00
// parsed operator definitions, unexported so they can't be defined
// directly in YAML:
operators map [ string ] * Oper
2017-10-02 05:31:40 +02:00
Logging [ ] logger . LoggingConfig
2017-03-06 04:31:10 +01:00
2017-04-30 04:35:07 +02:00
Debug struct {
2019-05-23 01:07:12 +02:00
RecoverFromErrors * bool ` yaml:"recover-from-errors" `
recoverFromErrors bool
2018-03-13 19:46:39 +01:00
PprofListener * string ` yaml:"pprof-listener" `
2017-04-30 04:35:07 +02:00
}
2018-07-16 09:46:40 +02:00
Limits Limits
2017-09-28 07:30:53 +02:00
2018-03-22 16:04:21 +01:00
Fakelag FakelagConfig
2018-11-26 11:23:27 +01:00
History struct {
2018-12-28 19:45:55 +01:00
Enabled bool
2019-05-19 22:34:52 +02:00
ChannelLength int ` yaml:"channel-length" `
ClientLength int ` yaml:"client-length" `
AutoresizeWindow time . Duration ` yaml:"autoresize-window" `
AutoreplayOnJoin int ` yaml:"autoreplay-on-join" `
ChathistoryMax int ` yaml:"chathistory-maxmessages" `
2018-11-26 11:23:27 +01:00
}
2017-09-28 07:30:53 +02:00
Filename string
2014-02-24 07:21:39 +01:00
}
2017-04-16 03:31:33 +02:00
// OperClass defines an assembled operator class.
2016-10-23 02:47:11 +02:00
type OperClass struct {
Title string
2016-10-23 03:01:05 +02:00
WhoisLine string ` yaml:"whois-line" `
2016-10-23 02:47:11 +02:00
Capabilities map [ string ] bool // map to make lookups much easier
}
2017-04-16 03:31:33 +02:00
// OperatorClasses returns a map of assembled operator classes from the given config.
2018-04-19 08:48:19 +02:00
func ( conf * Config ) OperatorClasses ( ) ( map [ string ] * OperClass , error ) {
ocs := make ( map [ string ] * OperClass )
2016-10-23 02:47:11 +02:00
// loop from no extends to most extended, breaking if we can't add any more
lenOfLastOcs := - 1
for {
if lenOfLastOcs == len ( ocs ) {
2018-02-03 13:03:36 +01:00
return nil , ErrOperClassDependencies
2016-10-23 02:47:11 +02:00
}
lenOfLastOcs = len ( ocs )
var anyMissing bool
for name , info := range conf . OperClasses {
_ , exists := ocs [ name ]
_ , extendsExists := ocs [ info . Extends ]
if exists {
// class already exists
continue
} else if len ( info . Extends ) > 0 && ! extendsExists {
// class we extend on doesn't exist
_ , exists := conf . OperClasses [ info . Extends ]
if ! exists {
return nil , fmt . Errorf ( "Operclass [%s] extends [%s], which doesn't exist" , name , info . Extends )
}
anyMissing = true
continue
}
// create new operclass
var oc OperClass
oc . Capabilities = make ( map [ string ] bool )
// get inhereted info from other operclasses
if len ( info . Extends ) > 0 {
2019-05-10 07:44:14 +02:00
einfo := ocs [ info . Extends ]
2016-10-23 02:47:11 +02:00
for capab := range einfo . Capabilities {
oc . Capabilities [ capab ] = true
}
}
// add our own info
oc . Title = info . Title
for _ , capab := range info . Capabilities {
oc . Capabilities [ capab ] = true
}
2016-10-23 03:01:05 +02:00
if len ( info . WhoisLine ) > 0 {
oc . WhoisLine = info . WhoisLine
} else {
oc . WhoisLine = "is a"
if strings . Contains ( strings . ToLower ( string ( oc . Title [ 0 ] ) ) , "aeiou" ) {
oc . WhoisLine += "n"
}
oc . WhoisLine += " "
oc . WhoisLine += oc . Title
}
2016-10-23 02:47:11 +02:00
2018-04-19 08:48:19 +02:00
ocs [ name ] = & oc
2016-10-23 02:47:11 +02:00
}
if ! anyMissing {
// we've got every operclass!
break
}
}
2018-04-19 08:48:19 +02:00
return ocs , nil
2016-10-23 02:47:11 +02:00
}
2017-04-16 03:31:33 +02:00
// Oper represents a single assembled operator's config.
2016-10-23 02:47:11 +02:00
type Oper struct {
2019-12-19 02:33:50 +01:00
Name string
Class * OperClass
WhoisLine string
Vhost string
Pass [ ] byte
Fingerprint string
2019-12-19 12:33:43 +01:00
Auto bool
2019-12-19 02:33:50 +01:00
Modes [ ] modes . ModeChange
2016-10-23 02:47:11 +02:00
}
2017-04-16 03:31:33 +02:00
// Operators returns a map of operator configs from the given OperClass and config.
2018-04-19 08:48:19 +02:00
func ( conf * Config ) Operators ( oc map [ string ] * OperClass ) ( map [ string ] * Oper , error ) {
operators := make ( map [ string ] * Oper )
2016-10-23 02:47:11 +02:00
for name , opConf := range conf . Opers {
var oper Oper
// oper name
2016-10-11 15:51:46 +02:00
name , err := CasefoldName ( name )
2016-10-23 02:47:11 +02:00
if err != nil {
return nil , fmt . Errorf ( "Could not casefold oper name: %s" , err . Error ( ) )
}
2018-04-19 08:48:19 +02:00
oper . Name = name
2016-10-23 02:47:11 +02:00
2019-12-19 12:33:43 +01:00
if opConf . Password != "" {
oper . Pass , err = decodeLegacyPasswordHash ( opConf . Password )
if err != nil {
2019-12-19 15:30:49 +01:00
return nil , fmt . Errorf ( "Oper %s has an invalid password hash: %s" , oper . Name , err . Error ( ) )
2019-12-19 12:33:43 +01:00
}
2018-08-06 04:51:39 +02:00
}
2019-12-19 02:33:50 +01:00
oper . Fingerprint = opConf . Fingerprint
2019-12-19 12:33:43 +01:00
oper . Auto = opConf . Auto
if oper . Pass == nil && oper . Fingerprint == "" {
return nil , fmt . Errorf ( "Oper %s has neither a password nor a fingerprint" , name )
}
2018-08-06 04:51:39 +02:00
2016-10-23 03:28:31 +02:00
oper . Vhost = opConf . Vhost
2018-04-19 08:48:19 +02:00
class , exists := oc [ opConf . Class ]
2016-10-23 02:47:11 +02:00
if ! exists {
return nil , fmt . Errorf ( "Could not load operator [%s] - they use operclass [%s] which does not exist" , name , opConf . Class )
2016-10-11 15:51:46 +02:00
}
2018-04-19 08:48:19 +02:00
oper . Class = class
2016-10-23 03:01:05 +02:00
if len ( opConf . WhoisLine ) > 0 {
oper . WhoisLine = opConf . WhoisLine
} else {
oper . WhoisLine = class . WhoisLine
}
2018-04-23 00:47:10 +02:00
modeStr := strings . TrimSpace ( opConf . Modes )
modeChanges , unknownChanges := modes . ParseUserModeChanges ( strings . Split ( modeStr , " " ) ... )
if len ( unknownChanges ) > 0 {
return nil , fmt . Errorf ( "Could not load operator [%s] due to unknown modes %v" , name , unknownChanges )
}
oper . Modes = modeChanges
2016-10-23 02:47:11 +02:00
// successful, attach to list of opers
2018-04-19 08:48:19 +02:00
operators [ name ] = & oper
2014-02-24 18:41:09 +01:00
}
2016-10-23 02:47:11 +02:00
return operators , nil
2014-02-24 18:41:09 +01:00
}
2019-06-28 16:45:34 +02:00
func loadTlsConfig ( config TLSListenConfig ) ( tlsConfig * tls . Config , err error ) {
cert , err := tls . LoadX509KeyPair ( config . Cert , config . Key )
if err != nil {
return nil , ErrInvalidCertKeyPair
2019-06-18 04:21:37 +02:00
}
2019-06-28 16:45:34 +02:00
result := tls . Config {
Certificates : [ ] tls . Certificate { cert } ,
ClientAuth : tls . RequestClientCert ,
}
return & result , nil
}
2019-06-18 04:21:37 +02:00
2019-06-28 16:45:34 +02:00
// prepareListeners populates Config.Server.trueListeners
func ( conf * Config ) prepareListeners ( ) ( err error ) {
listeners := make ( map [ string ] listenerConfig )
if 0 < len ( conf . Server . Listeners ) {
for addr , block := range conf . Server . Listeners {
var lconf listenerConfig
2019-11-20 23:43:40 +01:00
lconf . Tor = block . Tor
lconf . STSOnly = block . STSOnly
if lconf . STSOnly && ! conf . Server . STS . Enabled {
2019-08-27 06:51:09 +02:00
return fmt . Errorf ( "%s is configured as a STS-only listener, but STS is disabled" , addr )
}
2019-06-28 16:45:34 +02:00
if block . TLS . Cert != "" {
tlsConfig , err := loadTlsConfig ( block . TLS )
if err != nil {
return err
}
lconf . TLSConfig = tlsConfig
2019-11-20 23:43:40 +01:00
lconf . ProxyBeforeTLS = block . TLS . Proxy
2019-06-18 04:21:37 +02:00
}
2019-06-28 16:45:34 +02:00
listeners [ addr ] = lconf
}
} else if 0 < len ( conf . Server . Listen ) {
log . Printf ( "WARNING: configuring listeners via the legacy `server.listen` config option" )
log . Printf ( "This will be removed in a later release: you should update to use `server.listeners`" )
torListeners := make ( map [ string ] bool , len ( conf . Server . TorListeners . Listeners ) )
for _ , addr := range conf . Server . TorListeners . Listeners {
torListeners [ addr ] = true
}
for _ , addr := range conf . Server . Listen {
var lconf listenerConfig
2019-11-20 23:43:40 +01:00
lconf . Tor = torListeners [ addr ]
2019-06-28 16:45:34 +02:00
tlsListenConf , ok := conf . Server . TLSListeners [ addr ]
if ok {
tlsConfig , err := loadTlsConfig ( tlsListenConf )
if err != nil {
return err
}
lconf . TLSConfig = tlsConfig
2019-06-18 04:21:37 +02:00
}
2019-06-28 16:45:34 +02:00
listeners [ addr ] = lconf
2016-04-13 12:45:09 +02:00
}
2019-06-28 16:45:34 +02:00
} else {
return fmt . Errorf ( "No listeners were configured" )
2016-04-13 12:45:09 +02:00
}
2019-06-28 16:45:34 +02:00
conf . Server . trueListeners = listeners
2019-06-18 04:21:37 +02:00
return nil
2016-04-13 12:45:09 +02:00
}
2019-06-28 16:45:34 +02:00
// LoadRawConfig loads the config without doing any consistency checks or postprocessing
func LoadRawConfig ( filename string ) ( config * Config , err error ) {
2016-04-12 15:00:09 +02:00
data , err := ioutil . ReadFile ( filename )
2014-02-09 16:53:42 +01:00
if err != nil {
2016-04-12 15:00:09 +02:00
return nil , err
2014-02-09 16:53:42 +01:00
}
2016-04-12 15:00:09 +02:00
err = yaml . Unmarshal ( data , & config )
if err != nil {
return nil , err
}
2019-06-28 16:45:34 +02:00
return
}
// LoadConfig loads the given YAML configuration file.
func LoadConfig ( filename string ) ( config * Config , err error ) {
config , err = LoadRawConfig ( filename )
if err != nil {
return nil , err
}
2016-04-12 15:00:09 +02:00
2017-09-28 07:30:53 +02:00
config . Filename = filename
2016-04-12 07:44:00 +02:00
if config . Network . Name == "" {
2018-02-03 13:03:36 +01:00
return nil , ErrNetworkNameMissing
2016-04-12 07:44:00 +02:00
}
2014-03-01 23:34:51 +01:00
if config . Server . Name == "" {
2018-02-03 13:03:36 +01:00
return nil , ErrServerNameMissing
2014-03-01 23:34:51 +01:00
}
2019-12-18 15:21:45 +01:00
if ! utils . IsServerName ( config . Server . Name ) {
2018-02-03 13:03:36 +01:00
return nil , ErrServerNameNotHostname
2016-04-21 02:48:15 +02:00
}
2016-08-19 15:21:52 +02:00
if config . Datastore . Path == "" {
2018-02-03 13:03:36 +01:00
return nil , ErrDatastorePathMissing
2016-08-19 15:21:52 +02:00
}
2019-02-03 10:24:59 +01:00
//dan: automagically fix identlen until a few releases in the future (from now, 0.12.0), being a newly-introduced limit
if config . Limits . IdentLen < 1 {
2019-02-03 20:01:46 +01:00
config . Limits . IdentLen = 20
2019-02-03 10:24:59 +01:00
}
2016-10-16 12:14:56 +02:00
if config . Limits . NickLen < 1 || config . Limits . ChannelLen < 2 || config . Limits . AwayLen < 1 || config . Limits . KickLen < 1 || config . Limits . TopicLen < 1 {
2018-02-03 13:03:36 +01:00
return nil , ErrLimitsAreInsane
2016-08-12 14:20:32 +02:00
}
2019-05-22 22:15:59 +02:00
if config . Limits . RegistrationMessages == 0 {
config . Limits . RegistrationMessages = 1024
}
2019-08-27 06:51:09 +02:00
config . Server . supportedCaps = caps . NewCompleteSet ( )
config . Server . capValues = make ( caps . Values )
err = config . prepareListeners ( )
if err != nil {
return nil , fmt . Errorf ( "failed to prepare listeners: %v" , err )
}
2017-03-09 10:07:35 +01:00
if config . Server . STS . Enabled {
config . Server . STS . Duration , err = custime . ParseDuration ( config . Server . STS . DurationString )
if err != nil {
return nil , fmt . Errorf ( "Could not parse STS duration: %s" , err . Error ( ) )
}
if config . Server . STS . Port < 0 || config . Server . STS . Port > 65535 {
return nil , fmt . Errorf ( "STS port is incorrect, should be 0 if disabled: %d" , config . Server . STS . Port )
}
2019-08-27 06:51:09 +02:00
if config . Server . STS . STSOnlyBanner != "" {
config . Server . STS . bannerLines = utils . WordWrap ( config . Server . STS . STSOnlyBanner , 400 )
} else {
config . Server . STS . bannerLines = [ ] string { fmt . Sprintf ( "This server is only accessible over TLS. Please reconnect using TLS on port %d." , config . Server . STS . Port ) }
}
} else {
config . Server . supportedCaps . Disable ( caps . STS )
config . Server . STS . Duration = 0
2017-03-09 10:07:35 +01:00
}
2019-08-27 06:51:09 +02:00
// set this even if STS is disabled
config . Server . capValues [ caps . STS ] = config . Server . STS . Value ( )
2019-12-17 21:10:23 +01:00
// lookup-hostnames defaults to true if unset
if config . Server . LookupHostnames != nil {
config . Server . lookupHostnames = * config . Server . LookupHostnames
} else {
config . Server . lookupHostnames = true
}
2017-10-15 08:18:14 +02:00
// process webirc blocks
var newWebIRC [ ] webircConfig
for _ , webirc := range config . Server . WebIRC {
// skip webirc blocks with no hosts (such as the example one)
2017-10-15 10:15:18 +02:00
if len ( webirc . Hosts ) == 0 {
2017-10-15 08:18:14 +02:00
continue
}
2017-10-16 00:47:49 +02:00
err = webirc . Populate ( )
2017-10-15 08:18:14 +02:00
if err != nil {
return nil , fmt . Errorf ( "Could not parse WebIRC config: %s" , err . Error ( ) )
}
newWebIRC = append ( newWebIRC , webirc )
}
config . Server . WebIRC = newWebIRC
2019-08-27 06:51:09 +02:00
2017-10-15 08:18:14 +02:00
// process limits
2019-01-12 13:17:51 +01:00
if config . Limits . LineLen . Rest < 512 {
config . Limits . LineLen . Rest = 512
2016-11-29 09:38:04 +01:00
}
2019-08-27 06:51:09 +02:00
if config . Limits . LineLen . Rest == 512 {
config . Server . supportedCaps . Disable ( caps . MaxLine )
} else {
config . Server . capValues [ caps . MaxLine ] = strconv . Itoa ( config . Limits . LineLen . Rest )
}
2019-09-01 03:29:23 +02:00
if ! config . Accounts . Bouncer . Enabled {
config . Server . supportedCaps . Disable ( caps . Bouncer )
}
2017-10-02 05:31:40 +02:00
var newLogConfigs [ ] logger . LoggingConfig
2017-03-06 04:31:10 +01:00
for _ , logConfig := range config . Logging {
// methods
2017-03-10 13:02:08 +01:00
methods := make ( map [ string ] bool )
2017-03-06 04:31:10 +01:00
for _ , method := range strings . Split ( logConfig . Method , " " ) {
if len ( method ) > 0 {
2017-03-10 13:02:08 +01:00
methods [ strings . ToLower ( method ) ] = true
2017-03-06 04:31:10 +01:00
}
}
2017-03-10 13:02:08 +01:00
if methods [ "file" ] && logConfig . Filename == "" {
2018-02-03 13:03:36 +01:00
return nil , ErrLoggerFilenameMissing
2017-03-06 04:31:10 +01:00
}
2017-03-10 13:02:08 +01:00
logConfig . MethodFile = methods [ "file" ]
2017-05-01 10:51:37 +02:00
logConfig . MethodStdout = methods [ "stdout" ]
2017-03-10 13:02:08 +01:00
logConfig . MethodStderr = methods [ "stderr" ]
2017-03-06 04:31:10 +01:00
// levels
2017-03-10 13:02:08 +01:00
level , exists := logger . LogLevelNames [ strings . ToLower ( logConfig . LevelString ) ]
2017-03-06 04:31:10 +01:00
if ! exists {
return nil , fmt . Errorf ( "Could not translate log leve [%s]" , logConfig . LevelString )
}
logConfig . Level = level
// types
for _ , typeStr := range strings . Split ( logConfig . TypeString , " " ) {
if len ( typeStr ) == 0 {
continue
}
if typeStr == "-" {
2018-02-03 13:03:36 +01:00
return nil , ErrLoggerExcludeEmpty
2017-03-06 04:31:10 +01:00
}
if typeStr [ 0 ] == '-' {
typeStr = typeStr [ 1 : ]
2017-03-10 13:02:08 +01:00
logConfig . ExcludedTypes = append ( logConfig . ExcludedTypes , typeStr )
2017-03-06 04:31:10 +01:00
} else {
2017-03-10 13:02:08 +01:00
logConfig . Types = append ( logConfig . Types , typeStr )
2017-03-06 04:31:10 +01:00
}
}
if len ( logConfig . Types ) < 1 {
2018-02-03 13:03:36 +01:00
return nil , ErrLoggerHasNoTypes
2017-03-06 04:31:10 +01:00
}
2017-03-06 06:50:23 +01:00
newLogConfigs = append ( newLogConfigs , logConfig )
2017-03-06 04:31:10 +01:00
}
2017-03-06 06:50:23 +01:00
config . Logging = newLogConfigs
2016-10-23 02:47:11 +02:00
2018-02-11 11:30:40 +01:00
// hardcode this for now
config . Accounts . Registration . EnabledCredentialTypes = [ ] string { "passphrase" , "certfp" }
for i , name := range config . Accounts . Registration . EnabledCallbacks {
if name == "none" {
// we store "none" as "*" internally
config . Accounts . Registration . EnabledCallbacks [ i ] = "*"
}
}
2019-04-08 03:36:48 +02:00
sort . Strings ( config . Accounts . Registration . EnabledCallbacks )
2018-02-11 11:30:40 +01:00
2019-02-05 06:19:03 +01:00
config . Accounts . RequireSasl . exemptedNets , err = utils . ParseNetList ( config . Accounts . RequireSasl . Exempted )
if err != nil {
return nil , fmt . Errorf ( "Could not parse require-sasl exempted nets: %v" , err . Error ( ) )
}
config . Server . proxyAllowedFromNets , err = utils . ParseNetList ( config . Server . ProxyAllowedFrom )
if err != nil {
return nil , fmt . Errorf ( "Could not parse proxy-allowed-from nets: %v" , err . Error ( ) )
}
2018-04-23 08:38:35 +02:00
rawRegexp := config . Accounts . VHosts . ValidRegexpRaw
2018-04-19 08:48:19 +02:00
if rawRegexp != "" {
regexp , err := regexp . Compile ( rawRegexp )
if err == nil {
2018-04-23 08:38:35 +02:00
config . Accounts . VHosts . ValidRegexp = regexp
2018-04-19 08:48:19 +02:00
} else {
log . Printf ( "invalid vhost regexp: %s\n" , err . Error ( ) )
}
}
2018-04-23 08:38:35 +02:00
if config . Accounts . VHosts . ValidRegexp == nil {
config . Accounts . VHosts . ValidRegexp = defaultValidVhostRegex
2018-04-19 08:48:19 +02:00
}
2019-01-01 22:45:37 +01:00
if ! config . Accounts . LoginThrottling . Enabled {
config . Accounts . LoginThrottling . MaxAttempts = 0 // limit of 0 means disabled
}
2019-08-27 06:51:09 +02:00
config . Server . capValues [ caps . SASL ] = "PLAIN,EXTERNAL"
if ! config . Accounts . AuthenticationEnabled {
config . Server . supportedCaps . Disable ( caps . SASL )
}
2018-03-18 02:32:12 +01:00
maxSendQBytes , err := bytefmt . ToBytes ( config . Server . MaxSendQString )
2017-03-13 23:12:39 +01:00
if err != nil {
return nil , fmt . Errorf ( "Could not parse maximum SendQ size (make sure it only contains whole numbers): %s" , err . Error ( ) )
}
2018-03-18 02:32:12 +01:00
config . Server . MaxSendQBytes = int ( maxSendQBytes )
2017-03-13 23:12:39 +01:00
2019-02-19 08:54:57 +01:00
config . languageManager , err = languages . NewManager ( config . Languages . Enabled , config . Languages . Path , config . Languages . Default )
if err != nil {
return nil , fmt . Errorf ( "Could not load languages: %s" , err . Error ( ) )
2018-01-22 08:30:31 +01:00
}
2019-08-27 06:51:09 +02:00
config . Server . capValues [ caps . Languages ] = config . languageManager . CapValue ( )
2018-01-22 08:30:31 +01:00
2018-07-16 10:08:24 +02:00
// RecoverFromErrors defaults to true
2019-05-23 01:07:12 +02:00
if config . Debug . RecoverFromErrors != nil {
config . Debug . recoverFromErrors = * config . Debug . RecoverFromErrors
} else {
config . Debug . recoverFromErrors = true
2018-07-16 10:08:24 +02:00
}
2018-07-16 09:46:40 +02:00
// casefold/validate server name
config . Server . nameCasefolded , err = Casefold ( config . Server . Name )
if err != nil {
return nil , fmt . Errorf ( "Server name isn't valid [%s]: %s" , config . Server . Name , err . Error ( ) )
}
// process operator definitions, store them to config.operators
operclasses , err := config . OperatorClasses ( )
if err != nil {
return nil , err
}
opers , err := config . Operators ( operclasses )
if err != nil {
return nil , err
}
config . operators = opers
// parse default channel modes
2018-08-28 19:34:43 +02:00
config . Channels . defaultModes = ParseDefaultChannelModes ( config . Channels . DefaultModes )
2018-07-16 09:46:40 +02:00
if config . Server . Password != "" {
2018-08-06 04:51:39 +02:00
config . Server . passwordBytes , err = decodeLegacyPasswordHash ( config . Server . Password )
2018-07-16 09:46:40 +02:00
if err != nil {
return nil , err
}
2018-08-06 04:51:39 +02:00
}
if config . Accounts . Registration . BcryptCost == 0 {
config . Accounts . Registration . BcryptCost = passwd . DefaultCost
2018-07-16 09:46:40 +02:00
}
2019-02-06 10:55:05 +01:00
if config . Channels . MaxChannelsPerClient == 0 {
config . Channels . MaxChannelsPerClient = 100
}
2019-02-06 10:32:04 +01:00
if config . Channels . Registration . MaxChannelsPerAccount == 0 {
2019-02-06 21:47:20 +01:00
config . Channels . Registration . MaxChannelsPerAccount = 15
2019-02-06 10:32:04 +01:00
}
2019-05-09 20:18:30 +02:00
forceTrailingPtr := config . Server . Compatibility . ForceTrailing
if forceTrailingPtr != nil {
config . Server . Compatibility . forceTrailing = * forceTrailingPtr
} else {
config . Server . Compatibility . forceTrailing = true
}
2019-05-10 06:07:22 +02:00
config . loadMOTD ( )
2018-11-26 11:23:27 +01:00
// in the current implementation, we disable history by creating a history buffer
// with zero capacity. but the `enabled` config option MUST be respected regardless
// of this detail
if ! config . History . Enabled {
config . History . ChannelLength = 0
config . History . ClientLength = 0
}
2019-05-12 10:01:47 +02:00
config . Server . Cloaks . Initialize ( )
2019-05-12 22:26:23 +02:00
if config . Server . Cloaks . Enabled {
if config . Server . Cloaks . Secret == "" || config . Server . Cloaks . Secret == "siaELnk6Kaeo65K3RCrwJjlWaZ-Bt3WuZ2L8MXLbNb4" {
2019-05-13 01:19:36 +02:00
return nil , fmt . Errorf ( "You must generate a new value of server.ip-cloaking.secret to enable cloaking" )
2019-05-12 22:26:23 +02:00
}
2019-12-24 22:20:18 +01:00
if ! utils . IsHostname ( config . Server . Cloaks . Netname ) {
return nil , fmt . Errorf ( "Invalid netname for cloaked hostnames: %s" , config . Server . Cloaks . Netname )
}
2019-05-12 22:26:23 +02:00
}
2019-05-12 08:17:57 +02:00
2019-09-22 23:26:50 +02:00
// now that all postprocessing is complete, regenerate ISUPPORT:
err = config . generateISupport ( )
if err != nil {
return nil , err
}
2019-11-20 23:14:42 +01:00
err = config . prepareListeners ( )
if err != nil {
return nil , fmt . Errorf ( "failed to prepare listeners: %v" , err )
}
2016-04-12 15:00:09 +02:00
return config , nil
2014-02-09 16:53:42 +01:00
}