123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194 |
- // 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/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) {
- 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"))
- } else {
- service.Notice(rb, client.t("Successfully cleared vhost"))
- }
- }
-
- 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"))
- }
|