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