Migrate case-mapping to xirc

This commit is contained in:
Simon Ser 2023-03-01 13:30:47 +01:00
parent 07cd1f2f5d
commit 3eb2679612
5 changed files with 102 additions and 93 deletions

View file

@ -365,7 +365,7 @@ func newDownstreamConn(srv *Server, ic ircConn, id uint64) *downstreamConn {
monitored: newCasemapMap[struct{}](), monitored: newCasemapMap[struct{}](),
registration: new(downstreamRegistration), registration: new(downstreamRegistration),
} }
dc.monitored.SetCasemapping(casemapASCII) dc.monitored.SetCasemapping(xirc.CaseMappingASCII)
if host, _, err := net.SplitHostPort(remoteAddr); err == nil { if host, _, err := net.SplitHostPort(remoteAddr); err == nil {
dc.hostname = host dc.hostname = host
} else { } else {
@ -1110,7 +1110,7 @@ func (dc *downstreamConn) updateNick() {
Params: []string{nick}, Params: []string{nick},
}) })
dc.nick = nick dc.nick = nick
dc.nickCM = casemapASCII(dc.nick) dc.nickCM = xirc.CaseMappingASCII(dc.nick)
} }
func (dc *downstreamConn) updateHost() { func (dc *downstreamConn) updateHost() {
@ -1197,7 +1197,7 @@ func (dc *downstreamConn) updateAccount() {
} }
func (dc *downstreamConn) updateCasemapping() { func (dc *downstreamConn) updateCasemapping() {
cm := casemapASCII cm := xirc.CaseMappingASCII
if dc.network != nil { if dc.network != nil {
cm = dc.network.casemap cm = dc.network.casemap
} }
@ -1397,7 +1397,7 @@ func (dc *downstreamConn) loadNetwork(ctx context.Context) error {
Params: []string{dc.nick, dc.registration.nick, "Nickname contains illegal characters"}, Params: []string{dc.nick, dc.registration.nick, "Nickname contains illegal characters"},
}} }}
} }
if casemapASCII(nick) == serviceNickCM { if xirc.CaseMappingASCII(nick) == serviceNickCM {
return ircError{&irc.Message{ return ircError{&irc.Message{
Command: irc.ERR_NICKNAMEINUSE, Command: irc.ERR_NICKNAMEINUSE,
Params: []string{dc.nick, dc.registration.nick, "Nickname reserved for bouncer service"}, Params: []string{dc.nick, dc.registration.nick, "Nickname reserved for bouncer service"},
@ -1446,7 +1446,7 @@ func (dc *downstreamConn) welcome(ctx context.Context) error {
} else { } else {
dc.nick = dc.user.Username dc.nick = dc.user.Username
} }
dc.nickCM = casemapASCII(dc.nick) dc.nickCM = xirc.CaseMappingASCII(dc.nick)
var isupport []string var isupport []string
if dc.network != nil { if dc.network != nil {
@ -1761,7 +1761,7 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
Params: []string{dc.nick, nick, "Nickname contains illegal characters"}, Params: []string{dc.nick, nick, "Nickname contains illegal characters"},
}} }}
} }
if casemapASCII(nick) == serviceNickCM { if xirc.CaseMappingASCII(nick) == serviceNickCM {
return ircError{&irc.Message{ return ircError{&irc.Message{
Command: irc.ERR_NICKNAMEINUSE, Command: irc.ERR_NICKNAMEINUSE,
Params: []string{dc.nick, nick, "Nickname reserved for bouncer service"}, Params: []string{dc.nick, nick, "Nickname reserved for bouncer service"},
@ -2000,7 +2000,7 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
modeStr = msg.Params[1] modeStr = msg.Params[1]
} }
if casemapASCII(name) == dc.nickCM { if xirc.CaseMappingASCII(name) == dc.nickCM {
if modeStr != "" { if modeStr != "" {
if uc := dc.upstream(); uc != nil { if uc := dc.upstream(); uc != nil {
uc.SendMessageLabeled(ctx, dc.id, &irc.Message{ uc.SendMessageLabeled(ctx, dc.id, &irc.Message{
@ -2165,7 +2165,7 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
fields, whoxToken := xirc.ParseWHOXOptions(options) fields, whoxToken := xirc.ParseWHOXOptions(options)
// TODO: support mixed bouncer/upstream WHO queries // TODO: support mixed bouncer/upstream WHO queries
maskCM := casemapASCII(mask) maskCM := xirc.CaseMappingASCII(mask)
if dc.network == nil && maskCM == dc.nickCM { if dc.network == nil && maskCM == dc.nickCM {
// TODO: support AWAY (H/G) in self WHO reply // TODO: support AWAY (H/G) in self WHO reply
flags := "H" flags := "H"
@ -2274,7 +2274,7 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
mask = mask[:i] mask = mask[:i]
} }
if dc.network == nil && casemapASCII(mask) == dc.nickCM { if dc.network == nil && xirc.CaseMappingASCII(mask) == dc.nickCM {
dc.SendMessage(&irc.Message{ dc.SendMessage(&irc.Message{
Prefix: dc.srv.prefix(), Prefix: dc.srv.prefix(),
Command: irc.RPL_WHOISUSER, Command: irc.RPL_WHOISUSER,
@ -2304,7 +2304,7 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
}) })
return nil return nil
} }
if casemapASCII(mask) == serviceNickCM { if xirc.CaseMappingASCII(mask) == serviceNickCM {
dc.SendMessage(&irc.Message{ dc.SendMessage(&irc.Message{
Prefix: dc.srv.prefix(), Prefix: dc.srv.prefix(),
Command: irc.RPL_WHOISUSER, Command: irc.RPL_WHOISUSER,
@ -2391,7 +2391,7 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
continue continue
} }
if dc.network == nil && casemapASCII(name) == dc.nickCM { if dc.network == nil && xirc.CaseMappingASCII(name) == dc.nickCM {
dc.SendMessage(&irc.Message{ dc.SendMessage(&irc.Message{
Tags: msg.Tags.Copy(), Tags: msg.Tags.Copy(),
Prefix: dc.prefix(), Prefix: dc.prefix(),
@ -2401,7 +2401,7 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
continue continue
} }
if casemapASCII(name) == serviceNickCM { if xirc.CaseMappingASCII(name) == serviceNickCM {
if dc.caps.IsEnabled("echo-message") { if dc.caps.IsEnabled("echo-message") {
echoTags := tags.Copy() echoTags := tags.Copy()
echoTags["time"] = dc.user.FormatServerTime(time.Now()) echoTags["time"] = dc.user.FormatServerTime(time.Now())
@ -2724,7 +2724,7 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
} }
// We don't save history for our service // We don't save history for our service
if casemapASCII(target) == serviceNickCM { if xirc.CaseMappingASCII(target) == serviceNickCM {
dc.SendBatch("chathistory", []string{target}, nil, func(batchRef string) {}) dc.SendBatch("chathistory", []string{target}, nil, func(batchRef string) {})
return nil return nil
} }
@ -2849,7 +2849,7 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
} }
// We don't save read receipts for our service // We don't save read receipts for our service
if casemapASCII(target) == serviceNickCM { if xirc.CaseMappingASCII(target) == serviceNickCM {
dc.SendMessage(&irc.Message{ dc.SendMessage(&irc.Message{
Prefix: dc.prefix(), Prefix: dc.prefix(),
Command: msg.Command, Command: msg.Command,

76
irc.go
View file

@ -216,79 +216,11 @@ func copyClientTags(tags irc.Tags) irc.Tags {
return t return t
} }
type casemapping func(string) string var stdCaseMapping = xirc.CaseMappingRFC1459
func casemapNone(name string) string {
return name
}
// CasemapASCII of name is the canonical representation of name according to the
// ascii casemapping.
func casemapASCII(name string) string {
nameBytes := []byte(name)
for i, r := range nameBytes {
if 'A' <= r && r <= 'Z' {
nameBytes[i] = r + 'a' - 'A'
}
}
return string(nameBytes)
}
// casemapRFC1459 of name is the canonical representation of name according to the
// rfc1459 casemapping.
func casemapRFC1459(name string) string {
nameBytes := []byte(name)
for i, r := range nameBytes {
if 'A' <= r && r <= 'Z' {
nameBytes[i] = r + 'a' - 'A'
} else if r == '{' {
nameBytes[i] = '['
} else if r == '}' {
nameBytes[i] = ']'
} else if r == '\\' {
nameBytes[i] = '|'
} else if r == '~' {
nameBytes[i] = '^'
}
}
return string(nameBytes)
}
// casemapRFC1459Strict of name is the canonical representation of name
// according to the rfc1459-strict casemapping.
func casemapRFC1459Strict(name string) string {
nameBytes := []byte(name)
for i, r := range nameBytes {
if 'A' <= r && r <= 'Z' {
nameBytes[i] = r + 'a' - 'A'
} else if r == '{' {
nameBytes[i] = '['
} else if r == '}' {
nameBytes[i] = ']'
} else if r == '\\' {
nameBytes[i] = '|'
}
}
return string(nameBytes)
}
func parseCasemappingToken(tokenValue string) (casemap casemapping, ok bool) {
switch tokenValue {
case "ascii":
casemap = casemapASCII
case "rfc1459":
casemap = casemapRFC1459
case "rfc1459-strict":
casemap = casemapRFC1459Strict
default:
return nil, false
}
return casemap, true
}
type casemapMap[V interface{}] struct { type casemapMap[V interface{}] struct {
m map[string]casemapEntry[V] m map[string]casemapEntry[V]
casemap casemapping casemap xirc.CaseMapping
} }
type casemapEntry[V interface{}] struct { type casemapEntry[V interface{}] struct {
@ -299,7 +231,7 @@ type casemapEntry[V interface{}] struct {
func newCasemapMap[V interface{}]() casemapMap[V] { func newCasemapMap[V interface{}]() casemapMap[V] {
return casemapMap[V]{ return casemapMap[V]{
m: make(map[string]casemapEntry[V]), m: make(map[string]casemapEntry[V]),
casemap: casemapNone, casemap: xirc.CaseMappingNone,
} }
} }
@ -345,7 +277,7 @@ func (cm *casemapMap[V]) ForEach(f func(string, V)) {
} }
} }
func (cm *casemapMap[V]) SetCasemapping(newCasemap casemapping) { func (cm *casemapMap[V]) SetCasemapping(newCasemap xirc.CaseMapping) {
cm.casemap = newCasemap cm.casemap = newCasemap
m := make(map[string]casemapEntry[V], len(cm.m)) m := make(map[string]casemapEntry[V], len(cm.m))
for _, entry := range cm.m { for _, entry := range cm.m {

View file

@ -942,9 +942,9 @@ func (uc *upstreamConn) handleMessage(ctx context.Context, msg *irc.Message) err
var err error var err error
switch parameter { switch parameter {
case "CASEMAPPING": case "CASEMAPPING":
casemap, ok := parseCasemappingToken(value) casemap := xirc.ParseCaseMapping(value)
if !ok { if casemap == nil {
casemap = casemapRFC1459 casemap = xirc.CaseMappingRFC1459
} }
uc.network.updateCasemapping(casemap) uc.network.updateCasemapping(casemap)
case "CHANMODES": case "CHANMODES":
@ -992,7 +992,7 @@ func (uc *upstreamConn) handleMessage(ctx context.Context, msg *irc.Message) err
// If upstream did not send any CASEMAPPING token, assume it // If upstream did not send any CASEMAPPING token, assume it
// implements the old RFCs with rfc1459. // implements the old RFCs with rfc1459.
if uc.isupport["CASEMAPPING"] == nil { if uc.isupport["CASEMAPPING"] == nil {
uc.network.updateCasemapping(casemapRFC1459) uc.network.updateCasemapping(stdCaseMapping)
} }
// If the server doesn't support MONITOR, periodically try to // If the server doesn't support MONITOR, periodically try to

View file

@ -151,7 +151,7 @@ type network struct {
delivered deliveredStore delivered deliveredStore
pushTargets casemapMap[time.Time] pushTargets casemapMap[time.Time]
lastError error lastError error
casemap casemapping casemap xirc.CaseMapping
} }
func newNetwork(user *user, record *database.Network, channels []database.Channel) *network { func newNetwork(user *user, record *database.Network, channels []database.Channel) *network {
@ -171,7 +171,7 @@ func newNetwork(user *user, record *database.Network, channels []database.Channe
channels: m, channels: m,
delivered: newDeliveredStore(), delivered: newDeliveredStore(),
pushTargets: newCasemapMap[time.Time](), pushTargets: newCasemapMap[time.Time](),
casemap: casemapRFC1459, casemap: stdCaseMapping,
} }
} }
@ -387,7 +387,7 @@ func (net *network) deleteChannel(ctx context.Context, name string) error {
return nil return nil
} }
func (net *network) updateCasemapping(newCasemap casemapping) { func (net *network) updateCasemapping(newCasemap xirc.CaseMapping) {
net.casemap = newCasemap net.casemap = newCasemap
net.channels.SetCasemapping(newCasemap) net.channels.SetCasemapping(newCasemap)
net.delivered.m.SetCasemapping(newCasemap) net.delivered.m.SetCasemapping(newCasemap)

77
xirc/casemapping.go Normal file
View file

@ -0,0 +1,77 @@
package xirc
func casemapNone(name string) string {
return name
}
// CasemapASCII of name is the canonical representation of name according to the
// ascii casemapping.
func casemapASCII(name string) string {
nameBytes := []byte(name)
for i, r := range nameBytes {
if 'A' <= r && r <= 'Z' {
nameBytes[i] = r + 'a' - 'A'
}
}
return string(nameBytes)
}
// casemapRFC1459 of name is the canonical representation of name according to the
// rfc1459 casemapping.
func casemapRFC1459(name string) string {
nameBytes := []byte(name)
for i, r := range nameBytes {
if 'A' <= r && r <= 'Z' {
nameBytes[i] = r + 'a' - 'A'
} else if r == '{' {
nameBytes[i] = '['
} else if r == '}' {
nameBytes[i] = ']'
} else if r == '\\' {
nameBytes[i] = '|'
} else if r == '~' {
nameBytes[i] = '^'
}
}
return string(nameBytes)
}
// casemapRFC1459Strict of name is the canonical representation of name
// according to the rfc1459-strict casemapping.
func casemapRFC1459Strict(name string) string {
nameBytes := []byte(name)
for i, r := range nameBytes {
if 'A' <= r && r <= 'Z' {
nameBytes[i] = r + 'a' - 'A'
} else if r == '{' {
nameBytes[i] = '['
} else if r == '}' {
nameBytes[i] = ']'
} else if r == '\\' {
nameBytes[i] = '|'
}
}
return string(nameBytes)
}
type CaseMapping func(string) string
var (
CaseMappingNone CaseMapping = casemapNone
CaseMappingASCII CaseMapping = casemapASCII
CaseMappingRFC1459 CaseMapping = casemapRFC1459
CaseMappingRFC1459Strict CaseMapping = casemapRFC1459Strict
)
func ParseCaseMapping(s string) CaseMapping {
var cm CaseMapping
switch s {
case "ascii":
cm = CaseMappingASCII
case "rfc1459":
cm = CaseMappingRFC1459
case "rfc1459-strict":
cm = CaseMappingRFC1459Strict
}
return cm
}