3
0
mirror of https://github.com/ergochat/ergo.git synced 2024-11-10 22:19:31 +01:00

Merge pull request #325 from slingamn/services.1

services refactor
This commit is contained in:
Shivaram Lingamneni 2019-01-05 18:30:26 -05:00 committed by GitHub
commit 3db6c9472b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 254 additions and 168 deletions

View File

@ -187,6 +187,10 @@ func (am *AccountManager) EnforcementStatus(nick string) (account string, method
defer am.RUnlock()
account = am.nickToAccount[cfnick]
if account == "" {
method = NickReservationNone
return
}
method = am.accountToMethod[account]
// if they don't have a custom setting, or customization is disabled, use the default
if method == NickReservationOptional || !config.Accounts.NickReservation.AllowCustomEnforcement {

View File

@ -15,7 +15,6 @@ import (
"github.com/goshuirc/irc-go/ircfmt"
"github.com/oragono/oragono/irc/modes"
"github.com/oragono/oragono/irc/sno"
"github.com/oragono/oragono/irc/utils"
)
const chanservHelp = `ChanServ lets you register and manage channels.
@ -26,8 +25,8 @@ To see in-depth help for a specific ChanServ command, try:
Here are the commands you can use:
%s`
func chanregEnabled(server *Server) bool {
return server.ChannelRegistrationEnabled()
func chanregEnabled(config *Config) bool {
return config.Channels.Registration.Enabled
}
var (
@ -41,6 +40,7 @@ this command if you're the founder of the channel.`,
helpShort: `$bOP$b makes the given user (or yourself) a channel admin.`,
authRequired: true,
enabled: chanregEnabled,
minParams: 1,
},
"register": {
handler: csRegisterHandler,
@ -52,6 +52,7 @@ remembered.`,
helpShort: `$bREGISTER$b lets you own a given channel.`,
authRequired: true,
enabled: chanregEnabled,
minParams: 1,
},
"unregister": {
handler: csUnregisterHandler,
@ -62,6 +63,7 @@ To prevent accidental unregistrations, a verification code is required;
invoking the command without a code will display the necessary code.`,
helpShort: `$bUNREGISTER$b deletes a channel registration.`,
enabled: chanregEnabled,
minParams: 1,
},
"drop": {
aliasOf: "unregister",
@ -77,6 +79,7 @@ accounts and modes, use $bAMODE #channel$b. Note that users are always
referenced by their registered account names, not their nicknames.`,
helpShort: `$bAMODE$b modifies persistent mode settings for channel members.`,
enabled: chanregEnabled,
minParams: 1,
},
}
)
@ -86,8 +89,8 @@ func csNotice(rb *ResponseBuffer, text string) {
rb.Add(nil, "ChanServ", "NOTICE", rb.target.Nick(), text)
}
func csAmodeHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
channelName, modeChange := utils.ExtractParam(params)
func csAmodeHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
channelName := params[0]
channel := server.channels.Get(channelName)
if channel == nil {
@ -98,7 +101,7 @@ func csAmodeHandler(server *Server, client *Client, command, params string, rb *
return
}
modeChanges, unknown := modes.ParseChannelModeChanges(strings.Fields(modeChange)...)
modeChanges, unknown := modes.ParseChannelModeChanges(params[1:]...)
var change modes.ModeChange
if len(modeChanges) > 1 || len(unknown) > 0 {
csNotice(rb, client.t("Invalid mode change"))
@ -159,27 +162,13 @@ func csAmodeHandler(server *Server, client *Client, command, params string, rb *
}
}
func csOpHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
channelName, clientToOp := utils.ExtractParam(params)
if channelName == "" {
csNotice(rb, ircfmt.Unescape(client.t("Syntax: $bOP #channel [nickname]$b")))
return
}
clientToOp = strings.TrimSpace(clientToOp)
channelKey, err := CasefoldChannel(channelName)
if err != nil {
csNotice(rb, client.t("Channel name is not valid"))
return
}
channelInfo := server.channels.Get(channelKey)
func csOpHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
channelInfo := server.channels.Get(params[0])
if channelInfo == nil {
csNotice(rb, client.t("Channel does not exist"))
return
}
channelName := channelInfo.Name()
clientAccount := client.Account()
if clientAccount == "" || clientAccount != channelInfo.Founder() {
@ -188,10 +177,9 @@ func csOpHandler(server *Server, client *Client, command, params string, rb *Res
}
var target *Client
if clientToOp != "" {
casefoldedNickname, err := CasefoldName(clientToOp)
target = server.clients.Get(casefoldedNickname)
if err != nil || target == nil {
if len(params) > 1 {
target = server.clients.Get(params[1])
if target == nil {
csNotice(rb, client.t("Could not find given client"))
return
}
@ -216,16 +204,13 @@ func csOpHandler(server *Server, client *Client, command, params string, rb *Res
csNotice(rb, fmt.Sprintf(client.t("Successfully op'd in channel %s"), channelName))
server.logger.Info("chanserv", fmt.Sprintf("Client %s op'd [%s] in channel %s", client.nick, clientToOp, channelName))
server.snomasks.Send(sno.LocalChannels, fmt.Sprintf(ircfmt.Unescape("Client $c[grey][$r%s$c[grey]] CS OP'd $c[grey][$r%s$c[grey]] in channel $c[grey][$r%s$c[grey]]"), client.nickMaskString, clientToOp, channelName))
tnick := target.Nick()
server.logger.Info("services", fmt.Sprintf("Client %s op'd [%s] in channel %s", client.Nick(), tnick, channelName))
server.snomasks.Send(sno.LocalChannels, fmt.Sprintf(ircfmt.Unescape("Client $c[grey][$r%s$c[grey]] CS OP'd $c[grey][$r%s$c[grey]] in channel $c[grey][$r%s$c[grey]]"), client.NickMaskString(), tnick, channelName))
}
func csRegisterHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
channelName := strings.TrimSpace(params)
if channelName == "" {
csNotice(rb, ircfmt.Unescape(client.t("Syntax: $bREGISTER #channel$b")))
return
}
func csRegisterHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
channelName := params[0]
channelKey, err := CasefoldChannel(channelName)
if err != nil {
@ -251,7 +236,7 @@ func csRegisterHandler(server *Server, client *Client, command, params string, r
csNotice(rb, fmt.Sprintf(client.t("Channel %s successfully registered"), channelName))
server.logger.Info("chanserv", fmt.Sprintf("Client %s registered channel %s", client.nick, channelName))
server.logger.Info("services", fmt.Sprintf("Client %s registered channel %s", client.nick, channelName))
server.snomasks.Send(sno.LocalChannels, fmt.Sprintf(ircfmt.Unescape("Channel registered $c[grey][$r%s$c[grey]] by $c[grey][$r%s$c[grey]]"), channelName, client.nickMaskString))
// give them founder privs
@ -266,8 +251,13 @@ func csRegisterHandler(server *Server, client *Client, command, params string, r
}
}
func csUnregisterHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
channelName, verificationCode := utils.ExtractParam(params)
func csUnregisterHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
channelName := params[0]
var verificationCode string
if len(params) > 1 {
verificationCode = params[1]
}
channelKey, err := CasefoldChannel(channelName)
if channelKey == "" || err != nil {
csNotice(rb, client.t("Channel name is not valid"))

View File

@ -129,7 +129,7 @@ func (clients *ClientManager) SetNick(client *Client, newNick string) error {
if currentNewEntry != nil && currentNewEntry != client {
return errNicknameInUse
}
if method == NickReservationStrict && reservedAccount != client.Account() {
if method == NickReservationStrict && reservedAccount != "" && reservedAccount != client.Account() {
return errNicknameReserved
}
clients.removeInternal(client)

View File

@ -7,10 +7,7 @@ import (
"errors"
"fmt"
"regexp"
"strings"
"time"
"github.com/oragono/oragono/irc/utils"
)
const hostservHelp = `HostServ lets you manage your vhost (i.e., the string displayed
@ -29,13 +26,12 @@ var (
defaultValidVhostRegex = regexp.MustCompile(`^[0-9A-Za-z.\-_/]+$`)
)
func hostservEnabled(server *Server) bool {
return server.AccountConfig().VHosts.Enabled
func hostservEnabled(config *Config) bool {
return config.Accounts.VHosts.Enabled
}
func hostservRequestsEnabled(server *Server) bool {
ac := server.AccountConfig()
return ac.VHosts.Enabled && ac.VHosts.UserRequests.Enabled
func hostservRequestsEnabled(config *Config) bool {
return config.Accounts.VHosts.Enabled && config.Accounts.VHosts.UserRequests.Enabled
}
var (
@ -67,16 +63,16 @@ then be approved by a server operator.`,
helpShort: `$bREQUEST$b requests a new vhost, pending operator approval.`,
authRequired: true,
enabled: hostservRequestsEnabled,
minParams: 1,
},
"status": {
handler: hsStatusHandler,
help: `Syntax: $bSTATUS$b
help: `Syntax: $bSTATUS [user]$b
STATUS displays your current vhost, if any, and the status of your most recent
request for a new one.`,
helpShort: `$bSTATUS$b shows your vhost and request status.`,
authRequired: true,
enabled: hostservEnabled,
request for a new one. A server operator can view someone else's status.`,
helpShort: `$bSTATUS$b shows your vhost and request status.`,
enabled: hostservEnabled,
},
"set": {
handler: hsSetHandler,
@ -86,6 +82,7 @@ SET sets a user's vhost, bypassing the request system.`,
helpShort: `$bSET$b sets a user's vhost.`,
capabs: []string{"vhosts"},
enabled: hostservEnabled,
minParams: 2,
},
"del": {
handler: hsSetHandler,
@ -95,6 +92,7 @@ DEL deletes a user's vhost.`,
helpShort: `$bDEL$b deletes a user's vhost.`,
capabs: []string{"vhosts"},
enabled: hostservEnabled,
minParams: 1,
},
"waiting": {
handler: hsWaitingHandler,
@ -114,6 +112,7 @@ APPROVE approves a user's vhost request.`,
helpShort: `$bAPPROVE$b approves a user's vhost request.`,
capabs: []string{"vhosts"},
enabled: hostservEnabled,
minParams: 1,
},
"reject": {
handler: hsRejectHandler,
@ -124,6 +123,8 @@ for the rejection.`,
helpShort: `$bREJECT$b rejects a user's vhost request.`,
capabs: []string{"vhosts"},
enabled: hostservEnabled,
minParams: 1,
maxParams: 2,
},
}
)
@ -146,7 +147,7 @@ func hsNotifyChannel(server *Server, message string) {
}
}
func hsOnOffHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
func hsOnOffHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
enable := false
if command == "on" {
enable = true
@ -162,8 +163,8 @@ func hsOnOffHandler(server *Server, client *Client, command, params string, rb *
}
}
func hsRequestHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
vhost, _ := utils.ExtractParam(params)
func hsRequestHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
vhost := params[0]
if validateVhost(server, vhost, false) != nil {
hsNotice(rb, client.t("Invalid vhost"))
return
@ -195,12 +196,24 @@ func hsRequestHandler(server *Server, client *Client, command, params string, rb
}
}
func hsStatusHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
accountName := client.Account()
func hsStatusHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
var accountName string
if len(params) > 0 {
if !client.HasRoleCapabs("vhosts") {
hsNotice(rb, client.t("Command restricted"))
return
}
accountName = params[0]
} else {
accountName = client.Account()
}
account, err := server.accounts.LoadAccount(accountName)
if err != nil {
server.logger.Warning("internal", "error loading account info", accountName, err.Error())
hsNotice(rb, client.t("An error occurred"))
if err != errAccountDoesNotExist {
server.logger.Warning("internal", "error loading account info", accountName, err.Error())
}
hsNotice(rb, client.t("No such account"))
return
}
@ -232,23 +245,18 @@ func validateVhost(server *Server, vhost string, oper bool) error {
return nil
}
func hsSetHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
var user, vhost string
user, params = utils.ExtractParam(params)
if user == "" {
hsNotice(rb, client.t("A user is required"))
return
}
func hsSetHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
user := params[0]
var vhost string
if command == "set" {
vhost, _ = utils.ExtractParam(params)
vhost = params[1]
if validateVhost(server, vhost, true) != nil {
hsNotice(rb, client.t("Invalid vhost"))
return
}
} else if command != "del" {
server.logger.Warning("internal", "invalid hostserv set command", command)
return
}
// else: command == "del", vhost == ""
_, err := server.accounts.VHostSet(user, vhost)
if err != nil {
@ -260,7 +268,7 @@ func hsSetHandler(server *Server, client *Client, command, params string, rb *Re
}
}
func hsWaitingHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
func hsWaitingHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
requests, total := server.accounts.VHostListRequests(10)
hsNotice(rb, fmt.Sprintf(client.t("There are %d pending requests for vhosts (%d displayed)"), total, len(requests)))
for i, request := range requests {
@ -268,12 +276,8 @@ func hsWaitingHandler(server *Server, client *Client, command, params string, rb
}
}
func hsApproveHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
user, _ := utils.ExtractParam(params)
if user == "" {
hsNotice(rb, client.t("A user is required"))
return
}
func hsApproveHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
user := params[0]
vhostInfo, err := server.accounts.VHostApprove(user)
if err != nil {
@ -288,13 +292,12 @@ func hsApproveHandler(server *Server, client *Client, command, params string, rb
}
}
func hsRejectHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
user, params := utils.ExtractParam(params)
if user == "" {
hsNotice(rb, client.t("A user is required"))
return
func hsRejectHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
var reason string
user := params[0]
if len(params) > 1 {
reason = params[1]
}
reason := strings.TrimSpace(params)
vhostInfo, err := server.accounts.VHostReject(user, reason)
if err != nil {

View File

@ -15,9 +15,10 @@ import (
)
var (
// anything added here MUST be casefolded:
restrictedNicknames = map[string]bool{
"=scene=": true, // used for rp commands
"HistServ": true, // TODO(slingamn) this should become a real service
"histserv": true, // TODO(slingamn) this should become a real service
}
)

View File

@ -8,25 +8,22 @@ import (
"strings"
"github.com/goshuirc/irc-go/ircfmt"
"github.com/oragono/oragono/irc/utils"
)
// "enabled" callbacks for specific nickserv commands
func servCmdRequiresAccreg(server *Server) bool {
return server.AccountConfig().Registration.Enabled
func servCmdRequiresAccreg(config *Config) bool {
return config.Accounts.Registration.Enabled
}
func servCmdRequiresAuthEnabled(server *Server) bool {
return server.AccountConfig().AuthenticationEnabled
func servCmdRequiresAuthEnabled(config *Config) bool {
return config.Accounts.AuthenticationEnabled
}
func nsGroupEnabled(server *Server) bool {
conf := server.Config()
return conf.Accounts.AuthenticationEnabled && conf.Accounts.NickReservation.Enabled
func nsGroupEnabled(config *Config) bool {
return config.Accounts.AuthenticationEnabled && config.Accounts.NickReservation.Enabled
}
func nsEnforceEnabled(server *Server) bool {
config := server.Config()
func nsEnforceEnabled(config *Config) bool {
return config.Accounts.NickReservation.Enabled && config.Accounts.NickReservation.AllowCustomEnforcement
}
@ -73,6 +70,7 @@ GHOST disconnects the given user from the network if they're logged in with the
same user account, letting you reclaim your nickname.`,
helpShort: `$bGHOST$b reclaims your nickname.`,
authRequired: true,
minParams: 1,
},
"group": {
handler: nsGroupHandler,
@ -84,7 +82,6 @@ users from changing to it (or forcing them to rename).`,
enabled: nsGroupEnabled,
authRequired: true,
},
"identify": {
handler: nsIdentifyHandler,
help: `Syntax: $bIDENTIFY <username> [password]$b
@ -92,6 +89,7 @@ users from changing to it (or forcing them to rename).`,
IDENTIFY lets you login to the given username using either password auth, or
certfp (your client certificate) if a password is not given.`,
helpShort: `$bIDENTIFY$b lets you login to your account.`,
minParams: 1,
},
"info": {
handler: nsInfoHandler,
@ -113,6 +111,7 @@ If the password is left out, your account will be registered to your TLS client
certificate (and you will need to use that certificate to login in future).`,
helpShort: `$bREGISTER$b lets you register a user account.`,
enabled: servCmdRequiresAccreg,
minParams: 2,
},
"sadrop": {
handler: nsDropHandler,
@ -122,6 +121,7 @@ SADROP forcibly de-links the given nickname from the attached user account.`,
helpShort: `$bSADROP$b forcibly de-links the given nickname from its user account.`,
capabs: []string{"accreg"},
enabled: servCmdRequiresAccreg,
minParams: 1,
},
"unregister": {
handler: nsUnregisterHandler,
@ -132,6 +132,8 @@ IRC operator with the correct permissions). To prevent accidental
unregistrations, a verification code is required; invoking the command without
a code will display the necessary code.`,
helpShort: `$bUNREGISTER$b lets you delete your user account.`,
enabled: servCmdRequiresAccreg,
minParams: 1,
},
"verify": {
handler: nsVerifyHandler,
@ -141,6 +143,7 @@ VERIFY lets you complete an account registration, if the server requires email
or other verification.`,
helpShort: `$bVERIFY$b lets you complete account registration.`,
enabled: servCmdRequiresAccreg,
minParams: 2,
},
"passwd": {
handler: nsPasswdHandler,
@ -153,6 +156,7 @@ with the correct permissions, you can use PASSWD to reset someone else's
password by supplying their username and then the desired password.`,
helpShort: `$bPASSWD$b lets you change your password.`,
enabled: servCmdRequiresAuthEnabled,
minParams: 2,
},
}
)
@ -162,9 +166,14 @@ func nsNotice(rb *ResponseBuffer, text string) {
rb.Add(nil, "NickServ", "NOTICE", rb.target.Nick(), text)
}
func nsDropHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
func nsDropHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
sadrop := command == "sadrop"
nick, _ := utils.ExtractParam(params)
var nick string
if len(params) > 0 {
nick = params[0]
} else {
nick = client.NickCasefolded()
}
err := server.accounts.SetNickReserved(client, nick, sadrop, false)
if err == nil {
@ -173,15 +182,13 @@ func nsDropHandler(server *Server, client *Client, command, params string, rb *R
nsNotice(rb, client.t("You're not logged into an account"))
} else if err == errAccountCantDropPrimaryNick {
nsNotice(rb, client.t("You can't ungroup your primary nickname (try unregistering your account instead)"))
} else if err == errNicknameReserved {
nsNotice(rb, client.t("That nickname is already reserved by someone else"))
} else {
nsNotice(rb, client.t("Could not ungroup nick"))
}
}
func nsGhostHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
nick, _ := utils.ExtractParam(params)
func nsGhostHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
nick := params[0]
ghost := server.clients.Get(nick)
if ghost == nil {
@ -207,7 +214,7 @@ func nsGhostHandler(server *Server, client *Client, command, params string, rb *
ghost.destroy(false)
}
func nsGroupHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
func nsGroupHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
nick := client.NickCasefolded()
err := server.accounts.SetNickReserved(client, nick, false, true)
if err == nil {
@ -230,13 +237,17 @@ func nsLoginThrottleCheck(client *Client, rb *ResponseBuffer) (success bool) {
return true
}
func nsIdentifyHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
func nsIdentifyHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
loginSuccessful := false
username, passphrase := utils.ExtractParam(params)
username := params[0]
var passphrase string
if len(params) > 1 {
passphrase = params[1]
}
// try passphrase
if username != "" && passphrase != "" {
if passphrase != "" {
if !nsLoginThrottleCheck(client, rb) {
return
}
@ -257,18 +268,23 @@ func nsIdentifyHandler(server *Server, client *Client, command, params string, r
}
}
func nsInfoHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
nick, _ := utils.ExtractParam(params)
if nick == "" {
nick = client.Nick()
}
accountName := nick
if server.AccountConfig().NickReservation.Enabled {
accountName = server.accounts.NickToAccount(nick)
func nsInfoHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
var accountName string
if len(params) > 0 {
nick := params[0]
if server.AccountConfig().NickReservation.Enabled {
accountName = server.accounts.NickToAccount(nick)
if accountName == "" {
nsNotice(rb, client.t("That nickname is not registered"))
return
}
} else {
accountName = nick
}
} else {
accountName = client.Account()
if accountName == "" {
nsNotice(rb, client.t("That nickname is not registered"))
nsNotice(rb, client.t("You're not logged into an account"))
return
}
}
@ -276,6 +292,7 @@ func nsInfoHandler(server *Server, client *Client, command, params string, rb *R
account, err := server.accounts.LoadAccount(accountName)
if err != nil || !account.Verified {
nsNotice(rb, client.t("Account does not exist"))
return
}
nsNotice(rb, fmt.Sprintf(client.t("Account: %s"), account.Name))
@ -287,10 +304,13 @@ func nsInfoHandler(server *Server, client *Client, command, params string, rb *R
}
}
func nsRegisterHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
func nsRegisterHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
// get params
username, afterUsername := utils.ExtractParam(params)
email, passphrase := utils.ExtractParam(afterUsername)
username, email := params[0], params[1]
var passphrase string
if len(params) > 0 {
passphrase = params[2]
}
if !server.AccountConfig().Registration.Enabled {
nsNotice(rb, client.t("Account registration has been disabled"))
@ -366,12 +386,11 @@ func nsRegisterHandler(server *Server, client *Client, command, params string, r
}
}
func nsUnregisterHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
username, verificationCode := utils.ExtractParam(params)
if !server.AccountConfig().Registration.Enabled {
nsNotice(rb, client.t("Account registration has been disabled"))
return
func nsUnregisterHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
username := params[0]
var verificationCode string
if len(params) > 1 {
verificationCode = params[1]
}
if username == "" {
@ -415,9 +434,8 @@ func nsUnregisterHandler(server *Server, client *Client, command, params string,
}
}
func nsVerifyHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
username, code := utils.ExtractParam(params)
func nsVerifyHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
username, code := params[0], params[1]
err := server.accounts.Verify(client, username, code)
var errorMessage string
@ -435,7 +453,7 @@ func nsVerifyHandler(server *Server, client *Client, command, params string, rb
sendSuccessfulRegResponse(client, rb, true)
}
func nsPasswdHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
func nsPasswdHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
var target string
var newPassword string
var errorMessage string
@ -445,27 +463,26 @@ func nsPasswdHandler(server *Server, client *Client, command, params string, rb
return
}
fields := strings.Fields(params)
switch len(fields) {
switch len(params) {
case 2:
if !hasPrivs {
errorMessage = "Insufficient privileges"
} else {
target, newPassword = fields[0], fields[1]
target, newPassword = params[0], params[1]
}
case 3:
target = client.Account()
if target == "" {
errorMessage = "You're not logged into an account"
} else if fields[1] != fields[2] {
} else if params[1] != params[2] {
errorMessage = "Passwords do not match"
} else {
// check that they correctly supplied the preexisting password
_, err := server.accounts.checkPassphrase(target, fields[0])
_, err := server.accounts.checkPassphrase(target, params[0])
if err != nil {
errorMessage = "Password incorrect"
} else {
newPassword = fields[1]
newPassword = params[1]
}
}
default:
@ -486,14 +503,12 @@ func nsPasswdHandler(server *Server, client *Client, command, params string, rb
}
}
func nsEnforceHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
arg := strings.TrimSpace(params)
if arg == "" {
func nsEnforceHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
if len(params) == 0 {
status := server.accounts.getStoredEnforcementStatus(client.Account())
nsNotice(rb, fmt.Sprintf(client.t("Your current nickname enforcement is: %s"), status))
} else {
method, err := nickReservationFromString(arg)
method, err := nickReservationFromString(params[0])
if err != nil {
nsNotice(rb, client.t("Invalid parameters"))
return

View File

@ -11,7 +11,6 @@ import (
"github.com/goshuirc/irc-go/ircfmt"
"github.com/goshuirc/irc-go/ircmsg"
"github.com/oragono/oragono/irc/utils"
)
@ -28,11 +27,13 @@ type ircService struct {
type serviceCommand struct {
aliasOf string // marks this command as an alias of another
capabs []string // oper capabs the given user has to have to access this command
handler func(server *Server, client *Client, command, params string, rb *ResponseBuffer)
handler func(server *Server, client *Client, command string, params []string, rb *ResponseBuffer)
help string
helpShort string
authRequired bool
enabled func(*Server) bool // is this command enabled in the server config?
enabled func(*Config) bool // is this command enabled in the server config?
minParams int
maxParams int // split into at most n params, with last param containing remaining unsplit text
}
// looks up a command in the table of command definitions for a service, resolving aliases
@ -90,7 +91,7 @@ HELP returns information on the given command.`,
helpShort: `$bHELP$b shows in-depth information about commands.`,
}
// this handles IRC commands like `/NICKSERV INFO`, translating into `/MSG NICKSERV INFO`
// generic handler for IRC commands like `/NICKSERV INFO`
func serviceCmdHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
service, ok := oragonoServicesByCommandAlias[msg.Command]
if !ok {
@ -98,28 +99,60 @@ func serviceCmdHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb
return false
}
fakePrivmsg := strings.Join(msg.Params, " ")
servicePrivmsgHandler(service, server, client, fakePrivmsg, rb)
if len(msg.Params) == 0 {
return false
}
commandName := strings.ToLower(msg.Params[0])
params := msg.Params[1:]
cmd := lookupServiceCommand(service.Commands, commandName)
// for a maxParams command, join all final parameters together if necessary
if cmd != nil && cmd.maxParams != 0 && cmd.maxParams < len(params) {
newParams := make([]string, cmd.maxParams)
copy(newParams, params[:cmd.maxParams-1])
newParams[cmd.maxParams-1] = strings.Join(params[cmd.maxParams-1:], " ")
params = newParams
}
serviceRunCommand(service, server, client, cmd, commandName, params, rb)
return false
}
// generic handler for service PRIVMSG
// generic handler for service PRIVMSG, like `/msg NickServ INFO`
func servicePrivmsgHandler(service *ircService, server *Server, client *Client, message string, rb *ResponseBuffer) {
commandName, params := utils.ExtractParam(message)
commandName = strings.ToLower(commandName)
params := strings.Fields(message)
if len(params) == 0 {
return
}
// look up the service command to see how to parse it
commandName := strings.ToLower(params[0])
cmd := lookupServiceCommand(service.Commands, commandName)
// reparse if needed
if cmd != nil && cmd.maxParams != 0 {
params = utils.FieldsN(message, cmd.maxParams+1)[1:]
} else {
params = params[1:]
}
serviceRunCommand(service, server, client, cmd, commandName, params, rb)
}
// actually execute a service command
func serviceRunCommand(service *ircService, server *Server, client *Client, cmd *serviceCommand, commandName string, params []string, rb *ResponseBuffer) {
nick := rb.target.Nick()
sendNotice := func(notice string) {
rb.Add(nil, service.Name, "NOTICE", nick, notice)
}
cmd := lookupServiceCommand(service.Commands, commandName)
if cmd == nil {
sendNotice(fmt.Sprintf("%s /%s HELP", client.t("Unknown command. To see available commands, run"), service.ShortName))
return
}
if cmd.enabled != nil && !cmd.enabled(server) {
if len(params) < cmd.minParams {
sendNotice(fmt.Sprintf(client.t("Invalid parameters. For usage, do /msg %s HELP %s"), service.Name, strings.ToUpper(commandName)))
return
}
if cmd.enabled != nil && !cmd.enabled(server.Config()) {
sendNotice(client.t("This command has been disabled by the server administrators"))
return
}
@ -143,15 +176,16 @@ func servicePrivmsgHandler(service *ircService, server *Server, client *Client,
}
// generic handler that displays help for service commands
func serviceHelpHandler(service *ircService, server *Server, client *Client, params string, rb *ResponseBuffer) {
func serviceHelpHandler(service *ircService, server *Server, client *Client, params []string, rb *ResponseBuffer) {
nick := rb.target.Nick()
config := server.Config()
sendNotice := func(notice string) {
rb.Add(nil, service.Name, "NOTICE", nick, notice)
}
sendNotice(ircfmt.Unescape(fmt.Sprintf("*** $b%s HELP$b ***", service.Name)))
if params == "" {
if len(params) == 0 {
// show general help
var shownHelpLines sort.StringSlice
var disabledCommands bool
@ -163,7 +197,7 @@ func serviceHelpHandler(service *ircService, server *Server, client *Client, par
if commandInfo.aliasOf != "" {
continue // don't show help lines for aliases
}
if commandInfo.enabled != nil && !commandInfo.enabled(server) {
if commandInfo.enabled != nil && !commandInfo.enabled(config) {
disabledCommands = true
continue
}
@ -187,7 +221,7 @@ func serviceHelpHandler(service *ircService, server *Server, client *Client, par
sendNotice(line)
}
} else {
commandName := strings.ToLower(strings.TrimSpace(params))
commandName := strings.ToLower(params[0])
commandInfo := lookupServiceCommand(service.Commands, commandName)
if commandInfo == nil {
sendNotice(client.t(fmt.Sprintf("Unknown command. To see available commands, run /%s HELP", service.ShortName)))

View File

@ -3,19 +3,6 @@
package utils
import "strings"
// ExtractParam extracts a parameter from the given string, returning the param and the rest of the string.
func ExtractParam(line string) (string, string) {
rawParams := strings.SplitN(strings.TrimSpace(line), " ", 2)
param0 := rawParams[0]
var param1 string
if 1 < len(rawParams) {
param1 = strings.TrimSpace(rawParams[1])
}
return param0, param1
}
// ArgsToStrings takes the arguments and splits them into a series of strings,
// each argument separated by delim and each string bounded by maxLength.
func ArgsToStrings(maxLength int, arguments []string, delim string) []string {

52
irc/utils/fieldsn.go Normal file
View File

@ -0,0 +1,52 @@
package utils
// Copyright (c) 2014 Kevin Wallace <kevin@pentabarf.net>
// Found here: https://github.com/kevinwallace/fieldsn
// Released under the MIT license
// XXX this implementation treats negative n as "return nil",
// unlike stdlib SplitN and friends, which treat it as "no limit"
// Original source code below:
// Package fieldsn implements FieldsN and FieldsFuncN,
// which are conspicuously missing from the strings package.
import (
"unicode"
)
// FieldsN is like strings.Fields, but returns at most n fields,
// and the nth field includes any whitespace at the end of the string.
func FieldsN(s string, n int) []string {
return FieldsFuncN(s, unicode.IsSpace, n)
}
// FieldsFuncN is like strings.FieldsFunc, but returns at most n fields,
// and the nth field includes any runes at the end of the string normally excluded by f.
func FieldsFuncN(s string, f func(rune) bool, n int) []string {
if n <= 0 {
return nil
}
a := make([]string, 0, n)
na := 0
fieldStart := -1
for i, rune := range s {
if f(rune) {
if fieldStart >= 0 {
a = append(a, s[fieldStart:i])
na++
fieldStart = -1
}
} else if fieldStart == -1 {
fieldStart = i
if na+1 == n {
break
}
}
}
if fieldStart >= 0 {
a = append(a, s[fieldStart:])
}
return a
}