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:
parent
6e24efe84b
commit
6cd71e1b9e
@ -10,6 +10,7 @@ New release of Oragono!
|
||||
### Security
|
||||
|
||||
### Added
|
||||
* Added `REHASH` command.
|
||||
|
||||
### Changed
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
Loading…
Reference in New Issue
Block a user