mirror of
				https://github.com/ergochat/ergo.git
				synced 2025-11-03 23:37:22 +01:00 
			
		
		
		
	
						commit
						d9f663c400
					
				@ -223,6 +223,13 @@ func (session *Session) SetAway(awayMessage string) (wasAway, nowAway string) {
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (session *Session) ConnID() string {
 | 
			
		||||
	if session == nil {
 | 
			
		||||
		return "*"
 | 
			
		||||
	}
 | 
			
		||||
	return session.connID
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (client *Client) autoAwayEnabledNoMutex(config *Config) bool {
 | 
			
		||||
	return client.registered && client.alwaysOn &&
 | 
			
		||||
		persistenceEnabled(config.Accounts.Multiclient.AutoAway, client.accountSettings.AutoAway)
 | 
			
		||||
 | 
			
		||||
@ -137,7 +137,7 @@ func sendSuccessfulAccountAuth(service *ircService, client *Client, rb *Response
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	client.server.logger.Info("accounts", "client", details.nick, "logged into account", details.accountName)
 | 
			
		||||
	client.server.logger.Info("accounts", rb.session.ConnID(), details.nick, "logged into account", details.accountName)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (server *Server) sendLoginSnomask(nickMask, accountName string) {
 | 
			
		||||
@ -1852,14 +1852,14 @@ func cmodeHandler(server *Server, client *Client, msg ircmsg.Message, rb *Respon
 | 
			
		||||
	if 1 < len(msg.Params) {
 | 
			
		||||
		// parse out real mode changes
 | 
			
		||||
		params := msg.Params[1:]
 | 
			
		||||
		var unknown map[rune]bool
 | 
			
		||||
		var unknown []rune
 | 
			
		||||
		changes, unknown = modes.ParseChannelModeChanges(params...)
 | 
			
		||||
 | 
			
		||||
		// alert for unknown mode changes
 | 
			
		||||
		for char := range unknown {
 | 
			
		||||
		for _, char := range unknown {
 | 
			
		||||
			rb.Add(nil, server.name, ERR_UNKNOWNMODE, client.nick, string(char), client.t("is an unknown mode character to me"))
 | 
			
		||||
		}
 | 
			
		||||
		if len(unknown) == 1 && len(changes) == 0 {
 | 
			
		||||
		if len(unknown) != 0 && len(changes) == 0 {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@ -1943,10 +1943,10 @@ func umodeHandler(server *Server, client *Client, msg ircmsg.Message, rb *Respon
 | 
			
		||||
		changes, unknown := modes.ParseUserModeChanges(params...)
 | 
			
		||||
 | 
			
		||||
		// alert for unknown mode changes
 | 
			
		||||
		for char := range unknown {
 | 
			
		||||
		for _, char := range unknown {
 | 
			
		||||
			rb.Add(nil, server.name, ERR_UNKNOWNMODE, cDetails.nick, string(char), client.t("is an unknown mode character to me"))
 | 
			
		||||
		}
 | 
			
		||||
		if len(unknown) == 1 && len(changes) == 0 {
 | 
			
		||||
		if len(unknown) != 0 && len(changes) == 0 {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,7 @@ package isupport
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"slices"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@ -58,7 +58,7 @@ func getTokenString(name string, value string) string {
 | 
			
		||||
 | 
			
		||||
// GetDifference returns the difference between two token lists.
 | 
			
		||||
func (il *List) GetDifference(newil *List) [][]string {
 | 
			
		||||
	var outTokens sort.StringSlice
 | 
			
		||||
	var outTokens []string
 | 
			
		||||
 | 
			
		||||
	// append removed tokens
 | 
			
		||||
	for name := range il.Tokens {
 | 
			
		||||
@ -84,7 +84,7 @@ func (il *List) GetDifference(newil *List) [][]string {
 | 
			
		||||
		outTokens = append(outTokens, token)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sort.Sort(outTokens)
 | 
			
		||||
	slices.Sort(outTokens)
 | 
			
		||||
 | 
			
		||||
	// create output list
 | 
			
		||||
	replies := make([][]string, 0)
 | 
			
		||||
@ -117,38 +117,34 @@ func (il *List) GetDifference(newil *List) [][]string {
 | 
			
		||||
 | 
			
		||||
// RegenerateCachedReply regenerates the cached RPL_ISUPPORT reply
 | 
			
		||||
func (il *List) RegenerateCachedReply() (err error) {
 | 
			
		||||
	il.CachedReply = make([][]string, 0)
 | 
			
		||||
	var length int     // Length of the current cache
 | 
			
		||||
	var cache []string // Token list cache
 | 
			
		||||
 | 
			
		||||
	// make sure we get a sorted list of tokens, needed for tests and looks nice
 | 
			
		||||
	var tokens sort.StringSlice
 | 
			
		||||
	for name := range il.Tokens {
 | 
			
		||||
		tokens = append(tokens, name)
 | 
			
		||||
	}
 | 
			
		||||
	sort.Sort(tokens)
 | 
			
		||||
 | 
			
		||||
	for _, name := range tokens {
 | 
			
		||||
		token := getTokenString(name, il.Tokens[name])
 | 
			
		||||
	var tokens []string
 | 
			
		||||
	for name, value := range il.Tokens {
 | 
			
		||||
		token := getTokenString(name, value)
 | 
			
		||||
		if token[0] == ':' || strings.Contains(token, " ") {
 | 
			
		||||
			err = fmt.Errorf("bad isupport token (cannot contain spaces or start with :): %s", token)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		tokens = append(tokens, token)
 | 
			
		||||
	}
 | 
			
		||||
	// make sure we get a sorted list of tokens, needed for tests and looks nice
 | 
			
		||||
	slices.Sort(tokens)
 | 
			
		||||
 | 
			
		||||
		if len(token)+length <= maxLastArgLength {
 | 
			
		||||
			// account for the space separating tokens
 | 
			
		||||
			if len(cache) > 0 {
 | 
			
		||||
				length++
 | 
			
		||||
			}
 | 
			
		||||
			cache = append(cache, token)
 | 
			
		||||
			length += len(token)
 | 
			
		||||
		}
 | 
			
		||||
	var cache []string // Tokens in current line
 | 
			
		||||
	var length int     // Length of the current line
 | 
			
		||||
 | 
			
		||||
		if len(cache) == maxParameters || len(token)+length >= maxLastArgLength {
 | 
			
		||||
	for _, token := range tokens {
 | 
			
		||||
		// account for the space separating tokens
 | 
			
		||||
		if len(cache) == maxParameters || (len(token)+1)+length > maxLastArgLength {
 | 
			
		||||
			il.CachedReply = append(il.CachedReply, cache)
 | 
			
		||||
			cache = make([]string, 0)
 | 
			
		||||
			cache = nil
 | 
			
		||||
			length = 0
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(cache) > 0 {
 | 
			
		||||
			length++
 | 
			
		||||
		}
 | 
			
		||||
		length += len(token)
 | 
			
		||||
		cache = append(cache, token)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(cache) > 0 {
 | 
			
		||||
 | 
			
		||||
@ -37,7 +37,7 @@ func TestISUPPORT(t *testing.T) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !reflect.DeepEqual(tListLong.CachedReply, longReplies) {
 | 
			
		||||
		t.Errorf("Multiple output replies did not match, got [%v]", longReplies)
 | 
			
		||||
		t.Errorf("Multiple output replies did not match, got [%v]", tListLong.CachedReply)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// create first list
 | 
			
		||||
 | 
			
		||||
@ -116,7 +116,7 @@ func ApplyUserModeChanges(client *Client, changes modes.ModeChanges, force bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// parseDefaultModes uses the provided mode change parser to parse the rawModes.
 | 
			
		||||
func parseDefaultModes(rawModes string, parser func(params ...string) (modes.ModeChanges, map[rune]bool)) modes.Modes {
 | 
			
		||||
func parseDefaultModes(rawModes string, parser func(params ...string) (modes.ModeChanges, []rune)) modes.Modes {
 | 
			
		||||
	modeChangeStrings := strings.Fields(rawModes)
 | 
			
		||||
	modeChanges, _ := parser(modeChangeStrings...)
 | 
			
		||||
	defaultModes := make(modes.Modes, 0)
 | 
			
		||||
 | 
			
		||||
@ -7,7 +7,7 @@ package modes
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"slices"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/ergochat/ergo/irc/utils"
 | 
			
		||||
@ -189,10 +189,7 @@ func GetLowestChannelModePrefix(prefixes string) (lowest Mode) {
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
// ParseUserModeChanges returns the valid changes, and the list of unknown chars.
 | 
			
		||||
func ParseUserModeChanges(params ...string) (ModeChanges, map[rune]bool) {
 | 
			
		||||
	changes := make(ModeChanges, 0)
 | 
			
		||||
	unknown := make(map[rune]bool)
 | 
			
		||||
 | 
			
		||||
func ParseUserModeChanges(params ...string) (changes ModeChanges, unknown []rune) {
 | 
			
		||||
	op := List
 | 
			
		||||
 | 
			
		||||
	if 0 < len(params) {
 | 
			
		||||
@ -219,19 +216,11 @@ func ParseUserModeChanges(params ...string) (ModeChanges, map[rune]bool) {
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			var isKnown bool
 | 
			
		||||
			for _, supportedMode := range SupportedUserModes {
 | 
			
		||||
				if rune(supportedMode) == mode {
 | 
			
		||||
					isKnown = true
 | 
			
		||||
					break
 | 
			
		||||
				}
 | 
			
		||||
			if slices.Contains(SupportedUserModes, Mode(mode)) {
 | 
			
		||||
				changes = append(changes, change)
 | 
			
		||||
			} else {
 | 
			
		||||
				unknown = append(unknown, mode)
 | 
			
		||||
			}
 | 
			
		||||
			if !isKnown {
 | 
			
		||||
				unknown[mode] = true
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			changes = append(changes, change)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -239,10 +228,7 @@ func ParseUserModeChanges(params ...string) (ModeChanges, map[rune]bool) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ParseChannelModeChanges returns the valid changes, and the list of unknown chars.
 | 
			
		||||
func ParseChannelModeChanges(params ...string) (ModeChanges, map[rune]bool) {
 | 
			
		||||
	changes := make(ModeChanges, 0)
 | 
			
		||||
	unknown := make(map[rune]bool)
 | 
			
		||||
 | 
			
		||||
func ParseChannelModeChanges(params ...string) (changes ModeChanges, unknown []rune) {
 | 
			
		||||
	op := List
 | 
			
		||||
 | 
			
		||||
	if 0 < len(params) {
 | 
			
		||||
@ -304,25 +290,11 @@ func ParseChannelModeChanges(params ...string) (ModeChanges, map[rune]bool) {
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			var isKnown bool
 | 
			
		||||
			for _, supportedMode := range SupportedChannelModes {
 | 
			
		||||
				if rune(supportedMode) == mode {
 | 
			
		||||
					isKnown = true
 | 
			
		||||
					break
 | 
			
		||||
				}
 | 
			
		||||
			if slices.Contains(SupportedChannelModes, Mode(mode)) || slices.Contains(ChannelUserModes, Mode(mode)) {
 | 
			
		||||
				changes = append(changes, change)
 | 
			
		||||
			} else {
 | 
			
		||||
				unknown = append(unknown, mode)
 | 
			
		||||
			}
 | 
			
		||||
			for _, supportedMode := range ChannelUserModes {
 | 
			
		||||
				if rune(supportedMode) == mode {
 | 
			
		||||
					isKnown = true
 | 
			
		||||
					break
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if !isKnown {
 | 
			
		||||
				unknown[mode] = true
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			changes = append(changes, change)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -428,33 +400,37 @@ func (set *ModeSet) HighestChannelUserMode() (result Mode) {
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ByCodepoint Modes
 | 
			
		||||
var (
 | 
			
		||||
	rplMyInfo1, rplMyInfo2, rplMyInfo3, chanmodesToken string
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (a ByCodepoint) Len() int           { return len(a) }
 | 
			
		||||
func (a ByCodepoint) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
 | 
			
		||||
func (a ByCodepoint) Less(i, j int) bool { return a[i] < a[j] }
 | 
			
		||||
func init() {
 | 
			
		||||
	initRplMyInfo()
 | 
			
		||||
	initChanmodesToken()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func RplMyInfo() (param1, param2, param3 string) {
 | 
			
		||||
func initRplMyInfo() {
 | 
			
		||||
	// initialize constant strings published in initial numerics
 | 
			
		||||
	userModes := make(Modes, len(SupportedUserModes), len(SupportedUserModes)+1)
 | 
			
		||||
	copy(userModes, SupportedUserModes)
 | 
			
		||||
	// TLS is not in SupportedUserModes because it can't be modified
 | 
			
		||||
	userModes = append(userModes, TLS)
 | 
			
		||||
	sort.Sort(ByCodepoint(userModes))
 | 
			
		||||
	slices.Sort(userModes)
 | 
			
		||||
 | 
			
		||||
	channelModes := make(Modes, len(SupportedChannelModes)+len(ChannelUserModes))
 | 
			
		||||
	copy(channelModes, SupportedChannelModes)
 | 
			
		||||
	copy(channelModes[len(SupportedChannelModes):], ChannelUserModes)
 | 
			
		||||
	sort.Sort(ByCodepoint(channelModes))
 | 
			
		||||
	slices.Sort(channelModes)
 | 
			
		||||
 | 
			
		||||
	// XXX enumerate these by hand, i can't see any way to DRY this
 | 
			
		||||
	channelParametrizedModes := Modes{BanMask, ExceptMask, InviteMask, Key, UserLimit, Forward}
 | 
			
		||||
	channelParametrizedModes = append(channelParametrizedModes, ChannelUserModes...)
 | 
			
		||||
	sort.Sort(ByCodepoint(channelParametrizedModes))
 | 
			
		||||
	slices.Sort(channelParametrizedModes)
 | 
			
		||||
 | 
			
		||||
	return userModes.String(), channelModes.String(), channelParametrizedModes.String()
 | 
			
		||||
	rplMyInfo1, rplMyInfo2, rplMyInfo3 = userModes.String(), channelModes.String(), channelParametrizedModes.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ChanmodesToken() (result string) {
 | 
			
		||||
func initChanmodesToken() {
 | 
			
		||||
	// https://modern.ircdocs.horse#chanmodes-parameter
 | 
			
		||||
	// type A: listable modes with parameters
 | 
			
		||||
	A := Modes{BanMask, ExceptMask, InviteMask}
 | 
			
		||||
@ -465,10 +441,18 @@ func ChanmodesToken() (result string) {
 | 
			
		||||
	// type D: modes without parameters
 | 
			
		||||
	D := Modes{InviteOnly, Moderated, NoOutside, OpOnlyTopic, ChanRoleplaying, Secret, NoCTCP, RegisteredOnly, RegisteredOnlySpeak, Auditorium, OpModerated}
 | 
			
		||||
 | 
			
		||||
	sort.Sort(ByCodepoint(A))
 | 
			
		||||
	sort.Sort(ByCodepoint(B))
 | 
			
		||||
	sort.Sort(ByCodepoint(C))
 | 
			
		||||
	sort.Sort(ByCodepoint(D))
 | 
			
		||||
	slices.Sort(A)
 | 
			
		||||
	slices.Sort(B)
 | 
			
		||||
	slices.Sort(C)
 | 
			
		||||
	slices.Sort(D)
 | 
			
		||||
 | 
			
		||||
	return fmt.Sprintf("%s,%s,%s,%s", A.String(), B.String(), C.String(), D.String())
 | 
			
		||||
	chanmodesToken = fmt.Sprintf("%s,%s,%s,%s", A.String(), B.String(), C.String(), D.String())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func RplMyInfo() (param1, param2, param3 string) {
 | 
			
		||||
	return rplMyInfo1, rplMyInfo2, rplMyInfo3
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ChanmodesToken() (result string) {
 | 
			
		||||
	return chanmodesToken
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -5,6 +5,7 @@ package modes
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"slices"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"testing"
 | 
			
		||||
)
 | 
			
		||||
@ -16,7 +17,7 @@ func assertEqual(supplied, expected interface{}, t *testing.T) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestParseUserModeChanges(t *testing.T) {
 | 
			
		||||
	emptyUnknown := make(map[rune]bool)
 | 
			
		||||
	var emptyUnknown []rune
 | 
			
		||||
	changes, unknown := ParseUserModeChanges("+i")
 | 
			
		||||
	assertEqual(unknown, emptyUnknown, t)
 | 
			
		||||
	assertEqual(changes, ModeChanges{ModeChange{Op: Add, Mode: Invisible}}, t)
 | 
			
		||||
@ -48,10 +49,11 @@ func TestParseUserModeChanges(t *testing.T) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestIssue874(t *testing.T) {
 | 
			
		||||
	emptyUnknown := make(map[rune]bool)
 | 
			
		||||
	var emptyModeChanges ModeChanges
 | 
			
		||||
	var emptyUnknown []rune
 | 
			
		||||
	modes, unknown := ParseChannelModeChanges("+k")
 | 
			
		||||
	assertEqual(unknown, emptyUnknown, t)
 | 
			
		||||
	assertEqual(modes, ModeChanges{}, t)
 | 
			
		||||
	assertEqual(modes, emptyModeChanges, t)
 | 
			
		||||
 | 
			
		||||
	modes, unknown = ParseChannelModeChanges("+k", "beer")
 | 
			
		||||
	assertEqual(unknown, emptyUnknown, t)
 | 
			
		||||
@ -151,7 +153,7 @@ func TestParseChannelModeChanges(t *testing.T) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	modes, unknown = ParseChannelModeChanges("+tx")
 | 
			
		||||
	if len(unknown) != 1 || !unknown['x'] {
 | 
			
		||||
	if len(unknown) != 1 || !slices.Contains(unknown, 'x') {
 | 
			
		||||
		t.Errorf("expected that x is an unknown mode, instead: %v", unknown)
 | 
			
		||||
	}
 | 
			
		||||
	expected = ModeChange{
 | 
			
		||||
 | 
			
		||||
@ -7,7 +7,7 @@ import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"slices"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
@ -250,7 +250,7 @@ func serviceHelpHandler(service *ircService, server *Server, client *Client, par
 | 
			
		||||
			client.t("Here are the commands you can use:"),
 | 
			
		||||
		}...)
 | 
			
		||||
		// show general help
 | 
			
		||||
		var shownHelpLines sort.StringSlice
 | 
			
		||||
		var shownHelpLines []string
 | 
			
		||||
		var disabledCommands bool
 | 
			
		||||
		for _, commandInfo := range service.Commands {
 | 
			
		||||
			// skip commands user can't access
 | 
			
		||||
@ -268,13 +268,13 @@ func serviceHelpHandler(service *ircService, server *Server, client *Client, par
 | 
			
		||||
			shownHelpLines = append(shownHelpLines, "    "+ircfmt.Unescape(client.t(commandInfo.helpShort)))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// sort help lines
 | 
			
		||||
		slices.Sort(shownHelpLines)
 | 
			
		||||
 | 
			
		||||
		if disabledCommands {
 | 
			
		||||
			shownHelpLines = append(shownHelpLines, "    "+client.t("... and other commands which have been disabled"))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// sort help lines
 | 
			
		||||
		sort.Sort(shownHelpLines)
 | 
			
		||||
 | 
			
		||||
		// push out help text
 | 
			
		||||
		for _, line := range helpBannerLines {
 | 
			
		||||
			sendNotice(line)
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user