diff --git a/irc/chanserv.go b/irc/chanserv.go index bdf779c0..9cd5ad42 100644 --- a/irc/chanserv.go +++ b/irc/chanserv.go @@ -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,9 @@ 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] + modeChange := strings.Join(params[1:], " ") channel := server.channels.Get(channelName) if channel == nil { @@ -159,27 +163,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 +178,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 +205,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 +237,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 +252,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")) diff --git a/irc/hostserv.go b/irc/hostserv.go index 73faa55f..3706669a 100644 --- a/irc/hostserv.go +++ b/irc/hostserv.go @@ -9,8 +9,6 @@ import ( "regexp" "strings" "time" - - "github.com/oragono/oragono/irc/utils" ) const hostservHelp = `HostServ lets you manage your vhost (i.e., the string displayed @@ -29,13 +27,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 +64,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 +83,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 +93,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 +113,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 +124,7 @@ for the rejection.`, helpShort: `$bREJECT$b rejects a user's vhost request.`, capabs: []string{"vhosts"}, enabled: hostservEnabled, + minParams: 1, }, } ) @@ -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 = strings.Join(params[1:], " ") } - reason := strings.TrimSpace(params) vhostInfo, err := server.accounts.VHostReject(user, reason) if err != nil { diff --git a/irc/nickserv.go b/irc/nickserv.go index 936f0819..8acb1083 100644 --- a/irc/nickserv.go +++ b/irc/nickserv.go @@ -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 [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 diff --git a/irc/services.go b/irc/services.go index 674b84b1..a195e832 100644 --- a/irc/services.go +++ b/irc/services.go @@ -11,8 +11,6 @@ import ( "github.com/goshuirc/irc-go/ircfmt" "github.com/goshuirc/irc-go/ircmsg" - - "github.com/oragono/oragono/irc/utils" ) // defines an IRC service, e.g., NICKSERV @@ -28,11 +26,12 @@ 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 } // looks up a command in the table of command definitions for a service, resolving aliases @@ -90,7 +89,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,15 +97,22 @@ func serviceCmdHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb return false } - fakePrivmsg := strings.Join(msg.Params, " ") - servicePrivmsgHandler(service, server, client, fakePrivmsg, rb) + serviceRunCommand(service, server, client, msg.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) + serviceRunCommand(service, server, client, strings.Fields(message), rb) +} + +// actually execute a service command +func serviceRunCommand(service *ircService, server *Server, client *Client, params []string, rb *ResponseBuffer) { + if len(params) == 0 { + return + } + commandName := strings.ToLower(params[0]) + params = params[1:] nick := rb.target.Nick() sendNotice := func(notice string) { @@ -119,7 +125,12 @@ func servicePrivmsgHandler(service *ircService, server *Server, client *Client, 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 +154,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 +175,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 +199,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))) diff --git a/irc/utils/args.go b/irc/utils/args.go index 966d7026..8f087423 100644 --- a/irc/utils/args.go +++ b/irc/utils/args.go @@ -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 {