3
0
mirror of https://github.com/ergochat/ergo.git synced 2024-12-22 10:42:52 +01:00

Add REHASH command

This commit is contained in:
Daniel Oaks 2016-10-19 21:38:31 +10:00
parent 6e24efe84b
commit 6cd71e1b9e
5 changed files with 161 additions and 27 deletions

View File

@ -10,6 +10,7 @@ New release of Oragono!
### Security
### Added
* Added `REHASH` command.
### Changed

View File

@ -164,6 +164,11 @@ var Commands = map[string]Command{
handler: regHandler,
minParams: 3,
},
"REHASH": {
handler: rehashHandler,
minParams: 0,
oper: true,
},
"TIME": {
handler: timeHandler,
minParams: 0,

View File

@ -5,6 +5,9 @@ package irc
import "fmt"
const isupportSupportedString = "are supported by this server"
const maxISupportLength = 400 // Max length of a single ISUPPORT token line
// ISupportList holds a list of ISUPPORT tokens
type ISupportList struct {
Tokens map[string]*string
@ -29,22 +32,30 @@ func (il *ISupportList) AddNoValue(name string) {
il.Tokens[name] = nil
}
// RegenerateCachedReply regenerates the cached RPL_ISUPPORT reply
func (il *ISupportList) RegenerateCachedReply() {
il.CachedReply = make([][]string, 0)
maxlen := 400 // Max length of a single ISUPPORT token line
// getTokenString gets the appropriate string for a token+value.
func getTokenString(name string, value *string) string {
if value == nil {
return name
}
return fmt.Sprintf("%s=%s", name, *value)
}
// GetDifference returns the difference between two token lists.
func (il *ISupportList) GetDifference(newil *ISupportList) [][]string {
replies := make([][]string, 0)
var length int // Length of the current cache
var cache []string // Token list cache
for name, value := range il.Tokens {
var token string
if value == nil {
token = name
} else {
token = fmt.Sprintf("%s=%s", name, *value)
// append removed tokens
for name := range il.Tokens {
_, exists := newil.Tokens[name]
if exists {
continue
}
if len(token)+length <= maxlen {
token := fmt.Sprintf("-%s", name)
if len(token)+length <= maxISupportLength {
// account for the space separating tokens
if len(cache) > 0 {
length++
@ -53,8 +64,68 @@ func (il *ISupportList) RegenerateCachedReply() {
length += len(token)
}
if len(cache) == 13 || len(token)+length >= maxlen {
cache = append(cache, "are supported by this server")
if len(cache) == 13 || len(token)+length >= maxISupportLength {
cache = append(cache, isupportSupportedString)
replies = append(replies, cache)
cache = make([]string, 0)
length = 0
}
}
// append added tokens
for name, value := range newil.Tokens {
newval, exists := il.Tokens[name]
if exists && *value == *newval {
continue
}
token := getTokenString(name, value)
if len(token)+length <= maxISupportLength {
// account for the space separating tokens
if len(cache) > 0 {
length++
}
cache = append(cache, token)
length += len(token)
}
if len(cache) == 13 || len(token)+length >= maxISupportLength {
cache = append(cache, isupportSupportedString)
replies = append(replies, cache)
cache = make([]string, 0)
length = 0
}
}
if len(cache) > 0 {
cache = append(cache, isupportSupportedString)
replies = append(replies, cache)
}
return replies
}
// RegenerateCachedReply regenerates the cached RPL_ISUPPORT reply
func (il *ISupportList) RegenerateCachedReply() {
il.CachedReply = make([][]string, 0)
var length int // Length of the current cache
var cache []string // Token list cache
for name, value := range il.Tokens {
token := getTokenString(name, value)
if len(token)+length <= maxISupportLength {
// account for the space separating tokens
if len(cache) > 0 {
length++
}
cache = append(cache, token)
length += len(token)
}
if len(cache) == 13 || len(token)+length >= maxISupportLength {
cache = append(cache, isupportSupportedString)
il.CachedReply = append(il.CachedReply, cache)
cache = make([]string, 0)
length = 0
@ -62,7 +133,7 @@ func (il *ISupportList) RegenerateCachedReply() {
}
if len(cache) > 0 {
cache = append(cache, "are supported by this server")
cache = append(cache, isupportSupportedString)
il.CachedReply = append(il.CachedReply, cache)
}
}

View File

@ -39,6 +39,7 @@ type Server struct {
channels ChannelNameMap
clients *ClientLookupSet
commands chan Command
configFilename string
ctime time.Time
store buntdb.DB
idle chan *Client
@ -47,6 +48,7 @@ type Server struct {
motdLines []string
name string
nameCasefolded string
networkName string
newConns chan clientConn
operators map[string][]byte
password []byte
@ -72,7 +74,8 @@ type clientConn struct {
IsTLS bool
}
func NewServer(config *Config) *Server {
// NewServer returns a new Oragono server.
func NewServer(configFilename string, config *Config) *Server {
casefoldedName, err := Casefold(config.Server.Name)
if err != nil {
log.Println(fmt.Sprintf("Server name isn't valid: [%s]", config.Server.Name), err.Error())
@ -80,12 +83,13 @@ func NewServer(config *Config) *Server {
}
server := &Server{
accounts: make(map[string]*ClientAccount),
channels: make(ChannelNameMap),
clients: NewClientLookupSet(),
commands: make(chan Command),
ctime: time.Now(),
idle: make(chan *Client),
accounts: make(map[string]*ClientAccount),
channels: make(ChannelNameMap),
clients: NewClientLookupSet(),
commands: make(chan Command),
configFilename: configFilename,
ctime: time.Now(),
idle: make(chan *Client),
limits: Limits{
AwayLen: int(config.Limits.AwayLen),
ChannelLen: int(config.Limits.ChannelLen),
@ -97,6 +101,7 @@ func NewServer(config *Config) *Server {
monitoring: make(map[string][]Client),
name: config.Server.Name,
nameCasefolded: casefoldedName,
networkName: config.Network.Name,
newConns: make(chan clientConn),
operators: config.Operators(),
signals: make(chan os.Signal, len(SERVER_SIGNALS)),
@ -171,6 +176,13 @@ func NewServer(config *Config) *Server {
// Attempt to clean up when receiving these signals.
signal.Notify(server.signals, SERVER_SIGNALS...)
server.setISupport()
return server
}
// setISupport sets up our RPL_ISUPPORT reply.
func (server *Server) setISupport() {
// add RPL_ISUPPORT tokens
server.isupport = NewISupportList()
server.isupport.Add("AWAYLEN", strconv.Itoa(server.limits.AwayLen))
@ -184,7 +196,7 @@ func NewServer(config *Config) *Server {
// server.isupport.Add("MAXLIST", "") //TODO(dan): Support max list length?
// server.isupport.Add("MODES", "") //TODO(dan): Support max modes?
server.isupport.Add("MONITOR", strconv.Itoa(server.limits.MonitorEntries))
server.isupport.Add("NETWORK", config.Network.Name)
server.isupport.Add("NETWORK", server.networkName)
server.isupport.Add("NICKLEN", strconv.Itoa(server.limits.NickLen))
server.isupport.Add("PREFIX", "(qaohv)~&@%+")
// server.isupport.Add("STATUSMSG", "@+") //TODO(dan): Support STATUSMSG
@ -207,8 +219,6 @@ func NewServer(config *Config) *Server {
}
server.isupport.RegenerateCachedReply()
return server
}
func loadChannelList(channel *Channel, list string, maskMode ChannelMode) {
@ -220,7 +230,7 @@ func loadChannelList(channel *Channel, list string, maskMode ChannelMode) {
func (server *Server) Shutdown() {
//TODO(dan): Make sure we disallow new nicks
for _, client := range server.clients.byNick {
for _, client := range server.clients.ByNick {
client.Notice("Server is shutting down")
}
@ -511,13 +521,16 @@ func joinHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
for i, name := range channels {
casefoldedName, err := CasefoldChannel(name)
if err != nil {
log.Println("ISN'T CHANNEL NAME:", name)
client.Send(nil, server.name, ERR_NOSUCHCHANNEL, client.nick, name, "No such channel")
continue
}
channel := server.channels.Get(casefoldedName)
if channel == nil {
if len(casefoldedName) > server.limits.ChannelLen {
client.Send(nil, server.name, ERR_NOSUCHCHANNEL, client.nick, name, "No such channel")
continue
}
channel = NewChannel(server, name, true)
}
@ -780,6 +793,50 @@ func operHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
return false
}
// REHASH
func rehashHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
config, err := LoadConfig(server.configFilename)
if err != nil {
client.Send(nil, server.name, ERR_UNKNOWNERROR, client.nick, "REHASH", fmt.Sprintf("Error rehashing config file: %s", err.Error()))
return false
}
//TODO(dan): burst CAP DEL for sasl
// set server options
server.limits = Limits{
AwayLen: int(config.Limits.AwayLen),
ChannelLen: int(config.Limits.ChannelLen),
KickLen: int(config.Limits.KickLen),
MonitorEntries: int(config.Limits.MonitorEntries),
NickLen: int(config.Limits.NickLen),
TopicLen: int(config.Limits.TopicLen),
}
server.operators = config.Operators()
server.checkIdent = config.Server.CheckIdent
// registration
accountReg := NewAccountRegistration(config.Registration.Accounts)
server.accountRegistration = &accountReg
// set RPL_ISUPPORT
oldISupportList := server.isupport
server.setISupport()
newISupportReplies := oldISupportList.GetDifference(server.isupport)
// push new info to all of our clients
for _, sClient := range server.clients.ByNick {
for _, tokenline := range newISupportReplies {
// ugly trickery ahead
sClient.Send(nil, client.server.name, RPL_ISUPPORT, append([]string{sClient.nick}, tokenline...)...)
}
}
client.Send(nil, server.name, RPL_REHASHING, client.nick, "ircd.yaml", "Rehashing")
return false
}
// AWAY [<message>]
func awayHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
var isAway bool

View File

@ -83,7 +83,7 @@ Options:
}
} else if arguments["run"].(bool) {
irc.Log.SetLevel(config.Server.Log)
server := irc.NewServer(config)
server := irc.NewServer(configfile, config)
if !arguments["--quiet"].(bool) {
log.Println(irc.SemVer, "running")
defer log.Println(irc.SemVer, "exiting")