3
0
mirror of https://github.com/ergochat/ergo.git synced 2024-11-13 07:29:30 +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 ### Security
### Added ### Added
* Added `REHASH` command.
### Changed ### Changed

View File

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

View File

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

View File

@ -39,6 +39,7 @@ type Server struct {
channels ChannelNameMap channels ChannelNameMap
clients *ClientLookupSet clients *ClientLookupSet
commands chan Command commands chan Command
configFilename string
ctime time.Time ctime time.Time
store buntdb.DB store buntdb.DB
idle chan *Client idle chan *Client
@ -47,6 +48,7 @@ type Server struct {
motdLines []string motdLines []string
name string name string
nameCasefolded string nameCasefolded string
networkName string
newConns chan clientConn newConns chan clientConn
operators map[string][]byte operators map[string][]byte
password []byte password []byte
@ -72,7 +74,8 @@ type clientConn struct {
IsTLS bool 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) casefoldedName, err := Casefold(config.Server.Name)
if err != nil { if err != nil {
log.Println(fmt.Sprintf("Server name isn't valid: [%s]", config.Server.Name), err.Error()) 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{ server := &Server{
accounts: make(map[string]*ClientAccount), accounts: make(map[string]*ClientAccount),
channels: make(ChannelNameMap), channels: make(ChannelNameMap),
clients: NewClientLookupSet(), clients: NewClientLookupSet(),
commands: make(chan Command), commands: make(chan Command),
ctime: time.Now(), configFilename: configFilename,
idle: make(chan *Client), ctime: time.Now(),
idle: make(chan *Client),
limits: Limits{ limits: Limits{
AwayLen: int(config.Limits.AwayLen), AwayLen: int(config.Limits.AwayLen),
ChannelLen: int(config.Limits.ChannelLen), ChannelLen: int(config.Limits.ChannelLen),
@ -97,6 +101,7 @@ func NewServer(config *Config) *Server {
monitoring: make(map[string][]Client), monitoring: make(map[string][]Client),
name: config.Server.Name, name: config.Server.Name,
nameCasefolded: casefoldedName, nameCasefolded: casefoldedName,
networkName: config.Network.Name,
newConns: make(chan clientConn), newConns: make(chan clientConn),
operators: config.Operators(), operators: config.Operators(),
signals: make(chan os.Signal, len(SERVER_SIGNALS)), 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. // Attempt to clean up when receiving these signals.
signal.Notify(server.signals, SERVER_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 // add RPL_ISUPPORT tokens
server.isupport = NewISupportList() server.isupport = NewISupportList()
server.isupport.Add("AWAYLEN", strconv.Itoa(server.limits.AwayLen)) 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("MAXLIST", "") //TODO(dan): Support max list length?
// server.isupport.Add("MODES", "") //TODO(dan): Support max modes? // server.isupport.Add("MODES", "") //TODO(dan): Support max modes?
server.isupport.Add("MONITOR", strconv.Itoa(server.limits.MonitorEntries)) 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("NICKLEN", strconv.Itoa(server.limits.NickLen))
server.isupport.Add("PREFIX", "(qaohv)~&@%+") server.isupport.Add("PREFIX", "(qaohv)~&@%+")
// server.isupport.Add("STATUSMSG", "@+") //TODO(dan): Support STATUSMSG // server.isupport.Add("STATUSMSG", "@+") //TODO(dan): Support STATUSMSG
@ -207,8 +219,6 @@ func NewServer(config *Config) *Server {
} }
server.isupport.RegenerateCachedReply() server.isupport.RegenerateCachedReply()
return server
} }
func loadChannelList(channel *Channel, list string, maskMode ChannelMode) { func loadChannelList(channel *Channel, list string, maskMode ChannelMode) {
@ -220,7 +230,7 @@ func loadChannelList(channel *Channel, list string, maskMode ChannelMode) {
func (server *Server) Shutdown() { func (server *Server) Shutdown() {
//TODO(dan): Make sure we disallow new nicks //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") 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 { for i, name := range channels {
casefoldedName, err := CasefoldChannel(name) casefoldedName, err := CasefoldChannel(name)
if err != nil { if err != nil {
log.Println("ISN'T CHANNEL NAME:", name)
client.Send(nil, server.name, ERR_NOSUCHCHANNEL, client.nick, name, "No such channel") client.Send(nil, server.name, ERR_NOSUCHCHANNEL, client.nick, name, "No such channel")
continue continue
} }
channel := server.channels.Get(casefoldedName) channel := server.channels.Get(casefoldedName)
if channel == nil { 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) channel = NewChannel(server, name, true)
} }
@ -780,6 +793,50 @@ func operHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
return false 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>] // AWAY [<message>]
func awayHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool { func awayHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
var isAway bool var isAway bool

View File

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