mirror of
				https://github.com/ergochat/ergo.git
				synced 2025-11-04 07:47:25 +01:00 
			
		
		
		
	fix #749
This commit is contained in:
		
							parent
							
								
									ee63cd135b
								
							
						
					
					
						commit
						26fd3e69a8
					
				@ -337,24 +337,28 @@ func (am *AccountManager) Register(client *Client, account string, callbackNames
 | 
			
		||||
		return errAccountAlreadyRegistered
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	config := am.server.AccountConfig()
 | 
			
		||||
	config := am.server.Config()
 | 
			
		||||
 | 
			
		||||
	// final "is registration allowed" check, probably redundant:
 | 
			
		||||
	if !(config.Registration.Enabled || callbackNamespace == "admin") {
 | 
			
		||||
	if !(config.Accounts.Registration.Enabled || callbackNamespace == "admin") {
 | 
			
		||||
		return errFeatureDisabled
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// if nick reservation is enabled, you can only register your current nickname
 | 
			
		||||
	// as an account; this prevents "land-grab" situations where someone else
 | 
			
		||||
	// registers your nick out from under you and then NS GHOSTs you
 | 
			
		||||
	// n.b. client is nil during a SAREGISTER:
 | 
			
		||||
	if config.NickReservation.Enabled && client != nil && client.NickCasefolded() != casefoldedAccount {
 | 
			
		||||
	// n.b. client is nil during a SAREGISTER
 | 
			
		||||
	// n.b. if EnforceGuestFormat, then there's no concern, because you can't
 | 
			
		||||
	// register a guest nickname anyway, and the actual registration system
 | 
			
		||||
	// will prevent any double-register
 | 
			
		||||
	if client != nil && config.Accounts.NickReservation.Enabled &&
 | 
			
		||||
		!config.Accounts.NickReservation.EnforceGuestFormat &&
 | 
			
		||||
		client.NickCasefolded() != casefoldedAccount {
 | 
			
		||||
		return errAccountMustHoldNick
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// can't register a guest nickname
 | 
			
		||||
	renamePrefix := strings.ToLower(config.NickReservation.RenamePrefix)
 | 
			
		||||
	if renamePrefix != "" && strings.HasPrefix(casefoldedAccount, renamePrefix) {
 | 
			
		||||
	if config.Accounts.NickReservation.guestRegexpFolded.MatchString(casefoldedAccount) {
 | 
			
		||||
		return errAccountAlreadyRegistered
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -382,7 +386,7 @@ func (am *AccountManager) Register(client *Client, account string, callbackNames
 | 
			
		||||
	callbackSpec := fmt.Sprintf("%s:%s", callbackNamespace, callbackValue)
 | 
			
		||||
 | 
			
		||||
	var setOptions *buntdb.SetOptions
 | 
			
		||||
	ttl := time.Duration(config.Registration.VerifyTimeout)
 | 
			
		||||
	ttl := time.Duration(config.Accounts.Registration.VerifyTimeout)
 | 
			
		||||
	if ttl != 0 {
 | 
			
		||||
		setOptions = &buntdb.SetOptions{Expires: true, TTL: ttl}
 | 
			
		||||
	}
 | 
			
		||||
@ -652,7 +656,7 @@ func (am *AccountManager) dispatchCallback(client *Client, casefoldedAccount str
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (am *AccountManager) dispatchMailtoCallback(client *Client, casefoldedAccount string, callbackValue string) (code string, err error) {
 | 
			
		||||
	config := am.server.AccountConfig().Registration.Callbacks.Mailto
 | 
			
		||||
	config := am.server.Config().Accounts.Registration.Callbacks.Mailto
 | 
			
		||||
	code = utils.GenerateSecretToken()
 | 
			
		||||
 | 
			
		||||
	subject := config.VerifyMessageSubject
 | 
			
		||||
@ -811,7 +815,7 @@ func (am *AccountManager) SetNickReserved(client *Client, nick string, saUnreser
 | 
			
		||||
	cfnick, err := CasefoldName(nick)
 | 
			
		||||
	skeleton, skerr := Skeleton(nick)
 | 
			
		||||
	// garbage nick, or garbage options, or disabled
 | 
			
		||||
	nrconfig := am.server.AccountConfig().NickReservation
 | 
			
		||||
	nrconfig := am.server.Config().Accounts.NickReservation
 | 
			
		||||
	if err != nil || skerr != nil || cfnick == "" || (reserve && saUnreserve) || !nrconfig.Enabled {
 | 
			
		||||
		return errAccountNickReservationFailed
 | 
			
		||||
	}
 | 
			
		||||
@ -1536,7 +1540,7 @@ func (am *AccountManager) VHostListRequests(limit int) (requests []PendingVHostR
 | 
			
		||||
func (am *AccountManager) applyVHostInfo(client *Client, info VHostInfo) {
 | 
			
		||||
	// if hostserv is disabled in config, then don't grant vhosts
 | 
			
		||||
	// that were previously approved while it was enabled
 | 
			
		||||
	if !am.server.AccountConfig().VHosts.Enabled {
 | 
			
		||||
	if !am.server.Config().Accounts.VHosts.Enabled {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -116,23 +116,9 @@ func (clients *ClientManager) Resume(oldClient *Client, session *Session) (err e
 | 
			
		||||
// SetNick sets a client's nickname, validating it against nicknames in use
 | 
			
		||||
func (clients *ClientManager) SetNick(client *Client, session *Session, newNick string) (setNick string, err error) {
 | 
			
		||||
	config := client.server.Config()
 | 
			
		||||
	newcfnick, err := CasefoldName(newNick)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", errNicknameInvalid
 | 
			
		||||
	}
 | 
			
		||||
	if len(newNick) > config.Limits.NickLen || len(newcfnick) > config.Limits.NickLen {
 | 
			
		||||
		return "", errNicknameInvalid
 | 
			
		||||
	}
 | 
			
		||||
	newSkeleton, err := Skeleton(newNick)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", errNicknameInvalid
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if restrictedCasefoldedNicks[newcfnick] || restrictedSkeletons[newSkeleton] {
 | 
			
		||||
		return "", errNicknameInvalid
 | 
			
		||||
	}
 | 
			
		||||
	var newcfnick, newSkeleton string
 | 
			
		||||
 | 
			
		||||
	reservedAccount, method := client.server.accounts.EnforcementStatus(newcfnick, newSkeleton)
 | 
			
		||||
	client.stateMutex.RLock()
 | 
			
		||||
	account := client.account
 | 
			
		||||
	accountName := client.accountName
 | 
			
		||||
@ -141,19 +127,58 @@ func (clients *ClientManager) SetNick(client *Client, session *Session, newNick
 | 
			
		||||
	realname := client.realname
 | 
			
		||||
	client.stateMutex.RUnlock()
 | 
			
		||||
 | 
			
		||||
	// recompute this (client.alwaysOn is not set for unregistered clients):
 | 
			
		||||
	alwaysOn := account != "" && persistenceEnabled(config.Accounts.Multiclient.AlwaysOn, settings.AlwaysOn)
 | 
			
		||||
	// recompute always-on status, because client.alwaysOn is not set for unregistered clients
 | 
			
		||||
	var alwaysOn, useAccountName bool
 | 
			
		||||
	if account != "" {
 | 
			
		||||
		alwaysOn = persistenceEnabled(config.Accounts.Multiclient.AlwaysOn, settings.AlwaysOn)
 | 
			
		||||
		useAccountName = alwaysOn || config.Accounts.NickReservation.EnforceAccountName
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if alwaysOn && registered {
 | 
			
		||||
		return "", errCantChangeNick
 | 
			
		||||
	if useAccountName {
 | 
			
		||||
		if registered && newNick != accountName && newNick != "" {
 | 
			
		||||
			return "", errNickAccountMismatch
 | 
			
		||||
		}
 | 
			
		||||
		newNick = accountName
 | 
			
		||||
		newcfnick = account
 | 
			
		||||
		newSkeleton, err = Skeleton(newNick)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", errNicknameInvalid
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		newNick = strings.TrimSpace(newNick)
 | 
			
		||||
		if len(newNick) == 0 {
 | 
			
		||||
			return "", errNickMissing
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if account == "" && config.Accounts.NickReservation.EnforceGuestFormat {
 | 
			
		||||
			newNick = strings.Replace(config.Accounts.NickReservation.GuestFormat, "*", newNick, 1)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		newcfnick, err = CasefoldName(newNick)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", errNicknameInvalid
 | 
			
		||||
		}
 | 
			
		||||
		if len(newNick) > config.Limits.NickLen || len(newcfnick) > config.Limits.NickLen {
 | 
			
		||||
			return "", errNicknameInvalid
 | 
			
		||||
		}
 | 
			
		||||
		newSkeleton, err = Skeleton(newNick)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", errNicknameInvalid
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if restrictedCasefoldedNicks[newcfnick] || restrictedSkeletons[newSkeleton] {
 | 
			
		||||
			return "", errNicknameInvalid
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		reservedAccount, method := client.server.accounts.EnforcementStatus(newcfnick, newSkeleton)
 | 
			
		||||
		if method == NickEnforcementStrict && reservedAccount != "" && reservedAccount != account {
 | 
			
		||||
			return "", errNicknameReserved
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var bouncerAllowed bool
 | 
			
		||||
	if config.Accounts.Multiclient.Enabled {
 | 
			
		||||
		if alwaysOn {
 | 
			
		||||
			// ignore the pre-reg nick, force a reattach
 | 
			
		||||
			newNick = accountName
 | 
			
		||||
			newcfnick = account
 | 
			
		||||
		if useAccountName {
 | 
			
		||||
			bouncerAllowed = true
 | 
			
		||||
		} else {
 | 
			
		||||
			if config.Accounts.Multiclient.AllowedByDefault && settings.AllowBouncer != MulticlientDisallowedByUser {
 | 
			
		||||
@ -198,9 +223,7 @@ func (clients *ClientManager) SetNick(client *Client, session *Session, newNick
 | 
			
		||||
	if skeletonHolder != nil && skeletonHolder != client {
 | 
			
		||||
		return "", errNicknameInUse
 | 
			
		||||
	}
 | 
			
		||||
	if method == NickEnforcementStrict && reservedAccount != "" && reservedAccount != account {
 | 
			
		||||
		return "", errNicknameReserved
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	clients.removeInternal(client)
 | 
			
		||||
	clients.byNick[newcfnick] = client
 | 
			
		||||
	clients.bySkeleton[newSkeleton] = client
 | 
			
		||||
 | 
			
		||||
@ -7,6 +7,7 @@ package irc
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/tls"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"log"
 | 
			
		||||
@ -242,11 +243,24 @@ type AccountConfig struct {
 | 
			
		||||
		Duration    time.Duration
 | 
			
		||||
		MaxAttempts int `yaml:"max-attempts"`
 | 
			
		||||
	} `yaml:"login-throttling"`
 | 
			
		||||
	SkipServerPassword bool                  `yaml:"skip-server-password"`
 | 
			
		||||
	NickReservation    NickReservationConfig `yaml:"nick-reservation"`
 | 
			
		||||
	Multiclient        MulticlientConfig
 | 
			
		||||
	Bouncer            *MulticlientConfig // # handle old name for 'multiclient'
 | 
			
		||||
	VHosts             VHostConfig
 | 
			
		||||
	SkipServerPassword bool `yaml:"skip-server-password"`
 | 
			
		||||
	NickReservation    struct {
 | 
			
		||||
		Enabled                bool
 | 
			
		||||
		AdditionalNickLimit    int `yaml:"additional-nick-limit"`
 | 
			
		||||
		Method                 NickEnforcementMethod
 | 
			
		||||
		AllowCustomEnforcement bool          `yaml:"allow-custom-enforcement"`
 | 
			
		||||
		RenameTimeout          time.Duration `yaml:"rename-timeout"`
 | 
			
		||||
		// RenamePrefix is the legacy field, GuestFormat is the new version
 | 
			
		||||
		RenamePrefix       string `yaml:"rename-prefix"`
 | 
			
		||||
		GuestFormat        string `yaml:"guest-nickname-format"`
 | 
			
		||||
		guestRegexp        *regexp.Regexp
 | 
			
		||||
		guestRegexpFolded  *regexp.Regexp
 | 
			
		||||
		EnforceGuestFormat bool `yaml:"enforce-guest-format"`
 | 
			
		||||
		EnforceAccountName bool `yaml:"enforce-account-name"`
 | 
			
		||||
	} `yaml:"nick-reservation"`
 | 
			
		||||
	Multiclient MulticlientConfig
 | 
			
		||||
	Bouncer     *MulticlientConfig // # handle old name for 'multiclient'
 | 
			
		||||
	VHosts      VHostConfig
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AccountRegistrationConfig controls account registration.
 | 
			
		||||
@ -371,15 +385,6 @@ func (cm *Casemapping) UnmarshalYAML(unmarshal func(interface{}) error) (err err
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type NickReservationConfig struct {
 | 
			
		||||
	Enabled                bool
 | 
			
		||||
	AdditionalNickLimit    int `yaml:"additional-nick-limit"`
 | 
			
		||||
	Method                 NickEnforcementMethod
 | 
			
		||||
	AllowCustomEnforcement bool          `yaml:"allow-custom-enforcement"`
 | 
			
		||||
	RenameTimeout          time.Duration `yaml:"rename-timeout"`
 | 
			
		||||
	RenamePrefix           string        `yaml:"rename-prefix"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ChannelRegistrationConfig controls channel registration.
 | 
			
		||||
type ChannelRegistrationConfig struct {
 | 
			
		||||
	Enabled               bool
 | 
			
		||||
@ -883,6 +888,19 @@ func LoadConfig(filename string) (config *Config, err error) {
 | 
			
		||||
		config.Accounts.Multiclient.AllowedByDefault = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// handle guest format, including the legacy key rename-prefix
 | 
			
		||||
	if config.Accounts.NickReservation.GuestFormat == "" {
 | 
			
		||||
		renamePrefix := config.Accounts.NickReservation.RenamePrefix
 | 
			
		||||
		if renamePrefix == "" {
 | 
			
		||||
			renamePrefix = "Guest-"
 | 
			
		||||
		}
 | 
			
		||||
		config.Accounts.NickReservation.GuestFormat = renamePrefix + "*"
 | 
			
		||||
	}
 | 
			
		||||
	config.Accounts.NickReservation.guestRegexp, config.Accounts.NickReservation.guestRegexpFolded, err = compileGuestRegexp(config.Accounts.NickReservation.GuestFormat, config.Server.Casemapping)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var newLogConfigs []logger.LoggingConfig
 | 
			
		||||
	for _, logConfig := range config.Logging {
 | 
			
		||||
		// methods
 | 
			
		||||
@ -1163,3 +1181,29 @@ func (config *Config) Diff(oldConfig *Config) (addedCaps, removedCaps *caps.Set)
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func compileGuestRegexp(guestFormat string, casemapping Casemapping) (standard, folded *regexp.Regexp, err error) {
 | 
			
		||||
	starIndex := strings.IndexByte(guestFormat, '*')
 | 
			
		||||
	if starIndex == -1 {
 | 
			
		||||
		return nil, nil, errors.New("guest format must contain exactly one *")
 | 
			
		||||
	}
 | 
			
		||||
	initial := guestFormat[:starIndex]
 | 
			
		||||
	final := guestFormat[starIndex+1:]
 | 
			
		||||
	if strings.IndexByte(final, '*') != -1 {
 | 
			
		||||
		return nil, nil, errors.New("guest format must contain exactly one *")
 | 
			
		||||
	}
 | 
			
		||||
	standard, err = regexp.Compile(fmt.Sprintf("^%s(.*)%s$", initial, final))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	initialFolded, err := casefoldWithSetting(initial, casemapping)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	finalFolded, err := casefoldWithSetting(final, casemapping)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	folded, err = regexp.Compile(fmt.Sprintf("^%s(.*)%s$", initialFolded, finalFolded))
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -41,8 +41,8 @@ var (
 | 
			
		||||
	errNicknameInvalid                = errors.New("invalid nickname")
 | 
			
		||||
	errNicknameInUse                  = errors.New("nickname in use")
 | 
			
		||||
	errNicknameReserved               = errors.New("nickname is reserved")
 | 
			
		||||
	errCantChangeNick                 = errors.New(`Always-on clients can't change nicknames`)
 | 
			
		||||
	errNickAccountMismatch            = errors.New(`Your nickname doesn't match your account name`)
 | 
			
		||||
	errCantChangeNick                 = errors.New(`You must use your account name as your nickname`)
 | 
			
		||||
	errNickAccountMismatch            = errors.New(`Your nickname must match your account name`)
 | 
			
		||||
	errNoExistingBan                  = errors.New("Ban does not exist")
 | 
			
		||||
	errNoSuchChannel                  = errors.New(`No such channel`)
 | 
			
		||||
	errChannelPurged                  = errors.New(`This channel was purged by the server operators and cannot be used`)
 | 
			
		||||
 | 
			
		||||
@ -25,10 +25,6 @@ func (server *Server) ChannelRegistrationEnabled() bool {
 | 
			
		||||
	return server.Config().Channels.Registration.Enabled
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (server *Server) AccountConfig() *AccountConfig {
 | 
			
		||||
	return &server.Config().Accounts
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (server *Server) GetOperator(name string) (oper *Oper) {
 | 
			
		||||
	name, err := CasefoldName(name)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
 | 
			
		||||
@ -32,7 +32,7 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// helper function to parse ACC callbacks, e.g., mailto:person@example.com, tel:16505551234
 | 
			
		||||
func parseCallback(spec string, config *AccountConfig) (callbackNamespace string, callbackValue string) {
 | 
			
		||||
func parseCallback(spec string, config AccountConfig) (callbackNamespace string, callbackValue string) {
 | 
			
		||||
	callback := strings.ToLower(spec)
 | 
			
		||||
	if callback == "*" {
 | 
			
		||||
		callbackNamespace = "*"
 | 
			
		||||
@ -127,7 +127,7 @@ func authenticateHandler(server *Server, client *Client, msg ircmsg.IrcMessage,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// sasl abort
 | 
			
		||||
	if !server.AccountConfig().AuthenticationEnabled || len(msg.Params) == 1 && msg.Params[0] == "*" {
 | 
			
		||||
	if !config.Accounts.AuthenticationEnabled || len(msg.Params) == 1 && msg.Params[0] == "*" {
 | 
			
		||||
		rb.Add(nil, server.name, ERR_SASLABORTED, details.nick, client.t("SASL authentication aborted"))
 | 
			
		||||
		session.sasl.Clear()
 | 
			
		||||
		return false
 | 
			
		||||
 | 
			
		||||
@ -178,7 +178,7 @@ func hsNotice(rb *ResponseBuffer, text string) {
 | 
			
		||||
 | 
			
		||||
// hsNotifyChannel notifies the designated channel of new vhost activity
 | 
			
		||||
func hsNotifyChannel(server *Server, message string) {
 | 
			
		||||
	chname := server.AccountConfig().VHosts.UserRequests.Channel
 | 
			
		||||
	chname := server.Config().Accounts.VHosts.UserRequests.Channel
 | 
			
		||||
	channel := server.channels.Get(chname)
 | 
			
		||||
	if channel == nil {
 | 
			
		||||
		return
 | 
			
		||||
@ -280,11 +280,11 @@ func hsStatusHandler(server *Server, client *Client, command string, params []st
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func validateVhost(server *Server, vhost string, oper bool) error {
 | 
			
		||||
	ac := server.AccountConfig()
 | 
			
		||||
	if len(vhost) > ac.VHosts.MaxLength {
 | 
			
		||||
	config := server.Config()
 | 
			
		||||
	if len(vhost) > config.Accounts.VHosts.MaxLength {
 | 
			
		||||
		return errVHostTooLong
 | 
			
		||||
	}
 | 
			
		||||
	if !ac.VHosts.ValidRegexp.MatchString(vhost) {
 | 
			
		||||
	if !config.Accounts.VHosts.ValidRegexp.MatchString(vhost) {
 | 
			
		||||
		return errVHostBadCharacters
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,6 @@ package irc
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/rand"
 | 
			
		||||
	"encoding/hex"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
@ -27,22 +26,15 @@ var (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// returns whether the change succeeded or failed
 | 
			
		||||
func performNickChange(server *Server, client *Client, target *Client, session *Session, newnick string, rb *ResponseBuffer) bool {
 | 
			
		||||
	nickname := strings.TrimSpace(newnick)
 | 
			
		||||
func performNickChange(server *Server, client *Client, target *Client, session *Session, nickname string, rb *ResponseBuffer) bool {
 | 
			
		||||
	currentNick := client.Nick()
 | 
			
		||||
 | 
			
		||||
	if len(nickname) < 1 {
 | 
			
		||||
		rb.Add(nil, server.name, ERR_NONICKNAMEGIVEN, currentNick, client.t("No nickname given"))
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if target.Nick() == nickname {
 | 
			
		||||
	details := target.Details()
 | 
			
		||||
	if details.nick == nickname {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	hadNick := details.nick != "*"
 | 
			
		||||
	origNickMask := details.nickMask
 | 
			
		||||
 | 
			
		||||
	hadNick := target.HasNick()
 | 
			
		||||
	origNickMask := target.NickMaskString()
 | 
			
		||||
	details := target.Details()
 | 
			
		||||
	assignedNickname, err := client.server.clients.SetNick(target, session, nickname)
 | 
			
		||||
	if err == errNicknameInUse {
 | 
			
		||||
		rb.Add(nil, server.name, ERR_NICKNAMEINUSE, currentNick, nickname, client.t("Nickname is already in use"))
 | 
			
		||||
@ -50,8 +42,12 @@ func performNickChange(server *Server, client *Client, target *Client, session *
 | 
			
		||||
		rb.Add(nil, server.name, ERR_NICKNAMEINUSE, currentNick, nickname, client.t("Nickname is reserved by a different account"))
 | 
			
		||||
	} else if err == errNicknameInvalid {
 | 
			
		||||
		rb.Add(nil, server.name, ERR_ERRONEUSNICKNAME, currentNick, utils.SafeErrorParam(nickname), client.t("Erroneous nickname"))
 | 
			
		||||
	} else if err == errCantChangeNick {
 | 
			
		||||
		rb.Add(nil, server.name, ERR_NICKNAMEINUSE, currentNick, utils.SafeErrorParam(nickname), client.t(err.Error()))
 | 
			
		||||
	} else if err == errNickAccountMismatch {
 | 
			
		||||
		// this used to use ERR_NICKNAMEINUSE, but it displayed poorly in some clients;
 | 
			
		||||
		// ERR_UNKNOWNERROR at least has a better chance of displaying our error text
 | 
			
		||||
		rb.Add(nil, server.name, ERR_UNKNOWNERROR, currentNick, "NICK", client.t(err.Error()))
 | 
			
		||||
	} else if err == errNickMissing {
 | 
			
		||||
		rb.Add(nil, server.name, ERR_NONICKNAMEGIVEN, currentNick, client.t("No nickname given"))
 | 
			
		||||
	} else if err != nil {
 | 
			
		||||
		rb.Add(nil, server.name, ERR_UNKNOWNERROR, currentNick, "NICK", fmt.Sprintf(client.t("Could not set or change nickname: %s"), err.Error()))
 | 
			
		||||
	}
 | 
			
		||||
@ -96,13 +92,10 @@ func performNickChange(server *Server, client *Client, target *Client, session *
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (server *Server) RandomlyRename(client *Client) {
 | 
			
		||||
	prefix := server.AccountConfig().NickReservation.RenamePrefix
 | 
			
		||||
	if prefix == "" {
 | 
			
		||||
		prefix = "Guest-"
 | 
			
		||||
	}
 | 
			
		||||
	format := server.Config().Accounts.NickReservation.GuestFormat
 | 
			
		||||
	buf := make([]byte, 8)
 | 
			
		||||
	rand.Read(buf)
 | 
			
		||||
	nick := fmt.Sprintf("%s%s", prefix, hex.EncodeToString(buf))
 | 
			
		||||
	nick := strings.Replace(format, "*", utils.B32Encoder.EncodeToString(buf), -1)
 | 
			
		||||
	sessions := client.Sessions()
 | 
			
		||||
	if len(sessions) == 0 {
 | 
			
		||||
		return
 | 
			
		||||
 | 
			
		||||
@ -610,6 +610,23 @@ func nsLoginThrottleCheck(client *Client, rb *ResponseBuffer) (success bool) {
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// if enforce-account-name is set, account name and nickname must be equal,
 | 
			
		||||
// so we need to re-NICK automatically on every login event (IDENTIFY,
 | 
			
		||||
// VERIFY, and a REGISTER that auto-verifies). if we can't get the nick
 | 
			
		||||
// then we log them out (they will be able to reattach with SASL)
 | 
			
		||||
func nsFixNickname(client *Client, rb *ResponseBuffer, config *Config) (success bool) {
 | 
			
		||||
	if !config.Accounts.NickReservation.EnforceAccountName {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	// don't need to supply a nickname, SetNick will use the account name
 | 
			
		||||
	if !performNickChange(client.server, client, client, rb.session, "", rb) {
 | 
			
		||||
		client.server.accounts.Logout(client)
 | 
			
		||||
		nsNotice(rb, client.t("A client is already using that account; try logging out and logging back in with SASL"))
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func nsIdentifyHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
 | 
			
		||||
	if client.LoggedIntoAccount() {
 | 
			
		||||
		nsNotice(rb, client.t("You're already logged into an account"))
 | 
			
		||||
@ -649,11 +666,16 @@ func nsIdentifyHandler(server *Server, client *Client, command string, params []
 | 
			
		||||
		loginSuccessful = (err == nil)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if loginSuccessful {
 | 
			
		||||
		if !nsFixNickname(client, rb, server.Config()) {
 | 
			
		||||
			loginSuccessful = false
 | 
			
		||||
			err = errNickAccountMismatch
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if loginSuccessful {
 | 
			
		||||
		sendSuccessfulAccountAuth(client, rb, true, true)
 | 
			
		||||
	} else if err == errNickAccountMismatch {
 | 
			
		||||
		nsNotice(rb, client.t("That account is set to always-on; try logging out and logging back in with SASL"))
 | 
			
		||||
	} else {
 | 
			
		||||
	} else if err != errNickAccountMismatch {
 | 
			
		||||
		nsNotice(rb, client.t("Could not login with your TLS certificate or supplied username/password"))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -667,7 +689,7 @@ func nsInfoHandler(server *Server, client *Client, command string, params []stri
 | 
			
		||||
	var accountName string
 | 
			
		||||
	if len(params) > 0 {
 | 
			
		||||
		nick := params[0]
 | 
			
		||||
		if server.AccountConfig().NickReservation.Enabled {
 | 
			
		||||
		if server.Config().Accounts.NickReservation.Enabled {
 | 
			
		||||
			accountName = server.accounts.NickToAccount(nick)
 | 
			
		||||
			if accountName == "" {
 | 
			
		||||
				nsNotice(rb, client.t("That nickname is not registered"))
 | 
			
		||||
@ -704,7 +726,6 @@ func nsInfoHandler(server *Server, client *Client, command string, params []stri
 | 
			
		||||
 | 
			
		||||
func nsRegisterHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
 | 
			
		||||
	details := client.Details()
 | 
			
		||||
	account := details.nick
 | 
			
		||||
	passphrase := params[0]
 | 
			
		||||
	var email string
 | 
			
		||||
	if 1 < len(params) {
 | 
			
		||||
@ -730,10 +751,20 @@ func nsRegisterHandler(server *Server, client *Client, command string, params []
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	config := server.AccountConfig()
 | 
			
		||||
	config := server.Config()
 | 
			
		||||
	account := details.nick
 | 
			
		||||
	if config.Accounts.NickReservation.EnforceGuestFormat {
 | 
			
		||||
		matches := config.Accounts.NickReservation.guestRegexp.FindStringSubmatch(account)
 | 
			
		||||
		if matches == nil || len(matches) < 2 {
 | 
			
		||||
			nsNotice(rb, client.t("Erroneous nickname"))
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		account = matches[1]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var callbackNamespace, callbackValue string
 | 
			
		||||
	noneCallbackAllowed := false
 | 
			
		||||
	for _, callback := range config.Registration.EnabledCallbacks {
 | 
			
		||||
	for _, callback := range config.Accounts.Registration.EnabledCallbacks {
 | 
			
		||||
		if callback == "*" {
 | 
			
		||||
			noneCallbackAllowed = true
 | 
			
		||||
		}
 | 
			
		||||
@ -744,7 +775,7 @@ func nsRegisterHandler(server *Server, client *Client, command string, params []
 | 
			
		||||
	if noneCallbackAllowed {
 | 
			
		||||
		callbackNamespace = "*"
 | 
			
		||||
	} else {
 | 
			
		||||
		callbackNamespace, callbackValue = parseCallback(email, config)
 | 
			
		||||
		callbackNamespace, callbackValue = parseCallback(email, config.Accounts)
 | 
			
		||||
		if callbackNamespace == "" || callbackValue == "" {
 | 
			
		||||
			nsNotice(rb, client.t("Registration requires a valid e-mail address"))
 | 
			
		||||
			return
 | 
			
		||||
@ -755,7 +786,7 @@ func nsRegisterHandler(server *Server, client *Client, command string, params []
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		if callbackNamespace == "*" {
 | 
			
		||||
			err = server.accounts.Verify(client, account, "")
 | 
			
		||||
			if err == nil {
 | 
			
		||||
			if err == nil && nsFixNickname(client, rb, config) {
 | 
			
		||||
				sendSuccessfulRegResponse(client, rb, true)
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
@ -861,7 +892,9 @@ func nsVerifyHandler(server *Server, client *Client, command string, params []st
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sendSuccessfulRegResponse(client, rb, true)
 | 
			
		||||
	if nsFixNickname(client, rb, server.Config()) {
 | 
			
		||||
		sendSuccessfulRegResponse(client, rb, true)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func nsPasswdHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
 | 
			
		||||
 | 
			
		||||
@ -76,7 +76,11 @@ func iterateFolding(profile *precis.Profile, oldStr string) (str string, err err
 | 
			
		||||
 | 
			
		||||
// Casefold returns a casefolded string, without doing any name or channel character checks.
 | 
			
		||||
func Casefold(str string) (string, error) {
 | 
			
		||||
	switch globalCasemappingSetting {
 | 
			
		||||
	return casefoldWithSetting(str, globalCasemappingSetting)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func casefoldWithSetting(str string, setting Casemapping) (string, error) {
 | 
			
		||||
	switch setting {
 | 
			
		||||
	default:
 | 
			
		||||
		return iterateFolding(precis.UsernameCaseMapped, str)
 | 
			
		||||
	case CasemappingASCII:
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										20
									
								
								oragono.yaml
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								oragono.yaml
									
									
									
									
									
								
							@ -343,8 +343,24 @@ accounts:
 | 
			
		||||
        # rename-timeout - this is how long users have 'til they're renamed
 | 
			
		||||
        rename-timeout: 30s
 | 
			
		||||
 | 
			
		||||
        # rename-prefix - this is the prefix to use when renaming clients (e.g. Guest-AB54U31)
 | 
			
		||||
        rename-prefix: Guest-
 | 
			
		||||
        # format for guest nicknames:
 | 
			
		||||
        # 1. these nicknames cannot be registered or reserved
 | 
			
		||||
        # 2. if a client is automatically renamed by the server,
 | 
			
		||||
        #    this is the template that will be used (e.g., Guest-nccj6rgmt97cg)
 | 
			
		||||
        # 3. if enforce-guest-format (see below) is enabled, clients without
 | 
			
		||||
        #    a registered account will have this template applied to their
 | 
			
		||||
        #    nicknames (e.g., 'katie' will become 'Guest-katie')
 | 
			
		||||
        guest-nickname-format: "Guest-*"
 | 
			
		||||
 | 
			
		||||
        # when enabled, forces users not logged into an account to use
 | 
			
		||||
        # a nickname matching the guest template:
 | 
			
		||||
        enforce-guest-format: false
 | 
			
		||||
 | 
			
		||||
        # when enabled, forces users logged into an account to use the
 | 
			
		||||
        # account name as their nickname. when combined with strict nickname
 | 
			
		||||
        # enforcement, this lets users treat nicknames and account names
 | 
			
		||||
        # as equivalent for the purpose of ban/invite/exception lists.
 | 
			
		||||
        enforce-account-name: false
 | 
			
		||||
 | 
			
		||||
    # multiclient controls whether oragono allows multiple connections to
 | 
			
		||||
    # attach to the same client/nickname identity; this is part of the
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user