diff --git a/irc/accounts.go b/irc/accounts.go index 68a8b7a6..99e1239a 100644 --- a/irc/accounts.go +++ b/irc/accounts.go @@ -7,6 +7,7 @@ import ( "bytes" "encoding/json" "fmt" + "sort" "strconv" "strings" "sync" @@ -1047,6 +1048,41 @@ func (am *AccountManager) AuthenticateByPassphrase(client *Client, accountName s return err } +// AllNicks returns the uncasefolded nicknames for all accounts, including additional (grouped) nicks. +func (am *AccountManager) AllNicks() (result []string) { + accountNamePrefix := fmt.Sprintf(keyAccountName, "") + accountAdditionalNicksPrefix := fmt.Sprintf(keyAccountAdditionalNicks, "") + + am.server.store.View(func(tx *buntdb.Tx) error { + // Account names + err := tx.AscendGreaterOrEqual("", accountNamePrefix, func(key, value string) bool { + if !strings.HasPrefix(key, accountNamePrefix) { + return false + } + result = append(result, value) + return true + }) + if err != nil { + return err + } + + // Additional nicks + return tx.AscendGreaterOrEqual("", accountAdditionalNicksPrefix, func(key, value string) bool { + if !strings.HasPrefix(key, accountAdditionalNicksPrefix) { + return false + } + additionalNicks := unmarshalReservedNicks(value) + for _, additionalNick := range additionalNicks { + result = append(result, additionalNick) + } + return true + }) + }) + + sort.Strings(result) + return +} + func (am *AccountManager) LoadAccount(accountName string) (result ClientAccount, err error) { casefoldedAccount, err := CasefoldName(accountName) if err != nil { diff --git a/irc/chanserv.go b/irc/chanserv.go index 2a08d313..e34ab4c1 100644 --- a/irc/chanserv.go +++ b/irc/chanserv.go @@ -5,6 +5,7 @@ package irc import ( "fmt" + "regexp" "sort" "strings" "time" @@ -126,6 +127,16 @@ set using PURGE.`, capabs: []string{"chanreg"}, minParams: 1, }, + "list": { + handler: csListHandler, + help: `Syntax: $bLIST [regex]$b + +LIST returns the list of registered channels, which match the given regex. +If no regex is provided, all registered channels are returned.`, + helpShort: `$bLIST$b searches the list of registered channels.`, + capabs: []string{"chanreg"}, + minParams: 0, + }, "info": { handler: csInfoHandler, help: `Syntax: $INFO #channel$b @@ -559,6 +570,34 @@ func csUnpurgeHandler(server *Server, client *Client, command string, params []s } } +func csListHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { + if !client.HasRoleCapabs("chanreg") { + csNotice(rb, client.t("Insufficient privileges")) + return + } + + var searchRegex *regexp.Regexp + if len(params) > 0 { + var err error + searchRegex, err = regexp.Compile(params[0]) + if err != nil { + csNotice(rb, client.t("Invalid regex")) + return + } + } + + csNotice(rb, ircfmt.Unescape(client.t("*** $bChanServ LIST$b ***"))) + + channels := server.channelRegistry.AllChannels() + for _, channel := range channels { + if searchRegex == nil || searchRegex.MatchString(channel) { + csNotice(rb, fmt.Sprintf(" %s", channel)) + } + } + + csNotice(rb, ircfmt.Unescape(client.t("*** $bEnd of ChanServ LIST$b ***"))) +} + func csInfoHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { chname, err := CasefoldChannel(params[0]) if err != nil { diff --git a/irc/nickserv.go b/irc/nickserv.go index 1f52a5df..6b73642e 100644 --- a/irc/nickserv.go +++ b/irc/nickserv.go @@ -5,6 +5,7 @@ package irc import ( "fmt" + "regexp" "strconv" "strings" "time" @@ -97,6 +98,17 @@ certfp (your client certificate) if a password is not given.`, enabled: servCmdRequiresAuthEnabled, minParams: 1, }, + "list": { + handler: nsListHandler, + help: `Syntax: $bLIST [regex]$b + +LIST returns the list of registered nicknames, which match the given regex. +If no regex is provided, all registered nicknames are returned.`, + helpShort: `$bLIST$b searches the list of registered nicknames.`, + enabled: servCmdRequiresAuthEnabled, + capabs: []string{"accreg"}, + minParams: 0, + }, "info": { handler: nsInfoHandler, help: `Syntax: $bINFO [username]$b @@ -681,6 +693,34 @@ func nsIdentifyHandler(server *Server, client *Client, command string, params [] } } +func nsListHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { + if !client.HasRoleCapabs("accreg") { + nsNotice(rb, client.t("Insufficient privileges")) + return + } + + var searchRegex *regexp.Regexp + if len(params) > 0 { + var err error + searchRegex, err = regexp.Compile(params[0]) + if err != nil { + nsNotice(rb, client.t("Invalid regex")) + return + } + } + + nsNotice(rb, ircfmt.Unescape(client.t("*** $bNickServ LIST$b ***"))) + + nicks := server.accounts.AllNicks() + for _, nick := range nicks { + if searchRegex == nil || searchRegex.MatchString(nick) { + nsNotice(rb, fmt.Sprintf(" %s", nick)) + } + } + + nsNotice(rb, ircfmt.Unescape(client.t("*** $bEnd of NickServ LIST$b ***"))) +} + func nsInfoHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) { if !server.Config().Accounts.AuthenticationEnabled && !client.HasRoleCapabs("accreg") { nsNotice(rb, client.t("This command has been disabled by the server administrators"))