mirror of
https://github.com/ergochat/ergo.git
synced 2024-11-15 00:19:29 +01:00
199 lines
6.1 KiB
Go
199 lines
6.1 KiB
Go
// Copyright (c) 2018 Shivaram Lingamneni <slingamn@cs.stanford.edu>
|
|
// released under the MIT license
|
|
|
|
package irc
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"regexp"
|
|
|
|
"github.com/ergochat/irc-go/ircfmt"
|
|
|
|
"github.com/ergochat/ergo/irc/sno"
|
|
"github.com/ergochat/ergo/irc/utils"
|
|
)
|
|
|
|
const (
|
|
hostservHelp = `HostServ lets you manage your vhost (i.e., the string displayed
|
|
in place of your client's hostname/IP).`
|
|
)
|
|
|
|
var (
|
|
errVHostBadCharacters = errors.New("Vhost contains prohibited characters")
|
|
errVHostTooLong = errors.New("Vhost is too long")
|
|
// ascii only for now
|
|
defaultValidVhostRegex = regexp.MustCompile(`^[0-9A-Za-z.\-_/]+$`)
|
|
)
|
|
|
|
func hostservEnabled(config *Config) bool {
|
|
return config.Accounts.VHosts.Enabled
|
|
}
|
|
|
|
var (
|
|
hostservCommands = map[string]*serviceCommand{
|
|
"on": {
|
|
handler: hsOnOffHandler,
|
|
help: `Syntax: $bON$b
|
|
|
|
ON enables your vhost, if you have one approved.`,
|
|
helpShort: `$bON$b enables your vhost, if you have one approved.`,
|
|
authRequired: true,
|
|
enabled: hostservEnabled,
|
|
},
|
|
"off": {
|
|
handler: hsOnOffHandler,
|
|
help: `Syntax: $bOFF$b
|
|
|
|
OFF disables your vhost, if you have one approved.`,
|
|
helpShort: `$bOFF$b disables your vhost, if you have one approved.`,
|
|
authRequired: true,
|
|
enabled: hostservEnabled,
|
|
},
|
|
"status": {
|
|
handler: hsStatusHandler,
|
|
help: `Syntax: $bSTATUS [user]$b
|
|
|
|
STATUS displays your current vhost, if any, and whether it is enabled or
|
|
disabled. A server operator can view someone else's status.`,
|
|
helpShort: `$bSTATUS$b shows your vhost status.`,
|
|
enabled: hostservEnabled,
|
|
},
|
|
"set": {
|
|
handler: hsSetHandler,
|
|
help: `Syntax: $bSET <user> <vhost>$b
|
|
|
|
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,
|
|
help: `Syntax: $bDEL <user>$b
|
|
|
|
DEL deletes a user's vhost.`,
|
|
helpShort: `$bDEL$b deletes a user's vhost.`,
|
|
capabs: []string{"vhosts"},
|
|
enabled: hostservEnabled,
|
|
minParams: 1,
|
|
},
|
|
"setcloaksecret": {
|
|
handler: hsSetCloakSecretHandler,
|
|
help: `Syntax: $bSETCLOAKSECRET$b <secret> [code]
|
|
|
|
SETCLOAKSECRET can be used to set or rotate the cloak secret. You should use
|
|
a cryptographically strong secret. To prevent accidental modification, a
|
|
verification code is required; invoking the command without a code will
|
|
display the necessary code.`,
|
|
helpShort: `$bSETCLOAKSECRET$b modifies the IP cloaking secret.`,
|
|
capabs: []string{"vhosts", "rehash"},
|
|
minParams: 1,
|
|
maxParams: 2,
|
|
},
|
|
}
|
|
)
|
|
|
|
func hsOnOffHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
|
|
enable := false
|
|
if command == "on" {
|
|
enable = true
|
|
}
|
|
|
|
_, err := server.accounts.VHostSetEnabled(client, enable)
|
|
if err == errNoVhost {
|
|
service.Notice(rb, client.t(err.Error()))
|
|
} else if err != nil {
|
|
service.Notice(rb, client.t("An error occurred"))
|
|
} else if enable {
|
|
service.Notice(rb, client.t("Successfully enabled your vhost"))
|
|
} else {
|
|
service.Notice(rb, client.t("Successfully disabled your vhost"))
|
|
}
|
|
}
|
|
|
|
func hsStatusHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
|
|
var accountName string
|
|
if len(params) > 0 {
|
|
if !client.HasRoleCapabs("vhosts") {
|
|
service.Notice(rb, client.t("Command restricted"))
|
|
return
|
|
}
|
|
accountName = params[0]
|
|
} else {
|
|
accountName = client.Account()
|
|
if accountName == "" {
|
|
service.Notice(rb, client.t("You're not logged into an account"))
|
|
return
|
|
}
|
|
}
|
|
|
|
account, err := server.accounts.LoadAccount(accountName)
|
|
if err != nil {
|
|
if err != errAccountDoesNotExist {
|
|
server.logger.Warning("internal", "error loading account info", accountName, err.Error())
|
|
}
|
|
service.Notice(rb, client.t("No such account"))
|
|
return
|
|
}
|
|
|
|
if account.VHost.ApprovedVHost != "" {
|
|
service.Notice(rb, fmt.Sprintf(client.t("Account %[1]s has vhost: %[2]s"), accountName, account.VHost.ApprovedVHost))
|
|
if !account.VHost.Enabled {
|
|
service.Notice(rb, client.t("This vhost is currently disabled, but can be enabled with /HS ON"))
|
|
}
|
|
} else {
|
|
service.Notice(rb, fmt.Sprintf(client.t("Account %s has no vhost"), accountName))
|
|
}
|
|
}
|
|
|
|
func validateVhost(server *Server, vhost string, oper bool) error {
|
|
config := server.Config()
|
|
if len(vhost) > config.Accounts.VHosts.MaxLength {
|
|
return errVHostTooLong
|
|
}
|
|
if !config.Accounts.VHosts.validRegexp.MatchString(vhost) {
|
|
return errVHostBadCharacters
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func hsSetHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
|
|
oper := client.Oper()
|
|
user := params[0]
|
|
var vhost string
|
|
|
|
if command == "set" {
|
|
vhost = params[1]
|
|
if validateVhost(server, vhost, true) != nil {
|
|
service.Notice(rb, client.t("Invalid vhost"))
|
|
return
|
|
}
|
|
}
|
|
// else: command == "del", vhost == ""
|
|
|
|
_, err := server.accounts.VHostSet(user, vhost)
|
|
if err != nil {
|
|
service.Notice(rb, client.t("An error occurred"))
|
|
} else if vhost != "" {
|
|
service.Notice(rb, client.t("Successfully set vhost"))
|
|
server.snomasks.Send(sno.LocalVhosts, fmt.Sprintf("Operator %[1]s set vhost %[2]s on account %[3]s", oper.Name, vhost, user))
|
|
} else {
|
|
service.Notice(rb, client.t("Successfully cleared vhost"))
|
|
server.snomasks.Send(sno.LocalVhosts, fmt.Sprintf("Operator %[1]s cleared vhost on account %[2]s", oper.Name, user))
|
|
}
|
|
}
|
|
|
|
func hsSetCloakSecretHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
|
|
secret := params[0]
|
|
expectedCode := utils.ConfirmationCode(secret, server.ctime)
|
|
if len(params) == 1 || params[1] != expectedCode {
|
|
service.Notice(rb, ircfmt.Unescape(client.t("$bWarning: changing the cloak secret will invalidate stored ban/invite/exception lists.$b")))
|
|
service.Notice(rb, fmt.Sprintf(client.t("To confirm, run this command: %s"), fmt.Sprintf("/HS SETCLOAKSECRET %s %s", secret, expectedCode)))
|
|
return
|
|
}
|
|
StoreCloakSecret(server.store, secret)
|
|
service.Notice(rb, client.t("Rotated the cloak secret; you must rehash or restart the server for it to take effect"))
|
|
}
|