fix: address 3 blocking review findings for IRC protocol listener
All checks were successful
check / check (push) Successful in 59s
All checks were successful
check / check (push) Successful in 59s
1. ISUPPORT/applyChannelModes: extend IRC MODE handler to support +i/-i, +s/-s, +n/-n (routed through svc.SetChannelFlag), and +H/-H (hashcash bits with parameter parsing). Add 'n' (no external messages) as a proper DB-backed channel flag with is_no_external column (default: on). Update IRC ISUPPORT to CHANMODES=,,H,imnst to match actual support. 2. QueryChannelMode: rewrite to return complete mode string including all boolean flags (n, i, m, s, t) and parameterized modes (k, l, H), matching the HTTP handler's buildChannelModeString logic. Simplify buildChannelModeString to delegate to QueryChannelMode for consistency. 3. Service struct encapsulation: change exported fields (DB, Broker, Config, Log) to unexported (db, broker, config, log). Add NewTestService constructor for use by external test packages. Update ircserver export_test.go to use the new constructor. Closes #89
This commit is contained in:
@@ -490,6 +490,124 @@ func (c *Conn) handleChannelMode(
|
||||
)
|
||||
}
|
||||
|
||||
// modeResult holds the delta strings produced by a
|
||||
// single mode-char application.
|
||||
type modeResult struct {
|
||||
applied string
|
||||
appliedArgs string
|
||||
consumed int
|
||||
skip bool
|
||||
}
|
||||
|
||||
// applyHashcashMode handles +H/-H (hashcash difficulty).
|
||||
func (c *Conn) applyHashcashMode(
|
||||
ctx context.Context,
|
||||
chID int64,
|
||||
adding bool,
|
||||
args []string,
|
||||
argIdx int,
|
||||
) modeResult {
|
||||
if !adding {
|
||||
_ = c.database.SetChannelHashcashBits(
|
||||
ctx, chID, 0,
|
||||
)
|
||||
|
||||
return modeResult{
|
||||
applied: "-H",
|
||||
appliedArgs: "",
|
||||
consumed: 0,
|
||||
skip: false,
|
||||
}
|
||||
}
|
||||
|
||||
if argIdx >= len(args) {
|
||||
return modeResult{
|
||||
applied: "",
|
||||
appliedArgs: "",
|
||||
consumed: 0,
|
||||
skip: true,
|
||||
}
|
||||
}
|
||||
|
||||
bitsStr := args[argIdx]
|
||||
|
||||
bits, parseErr := strconv.Atoi(bitsStr)
|
||||
if parseErr != nil ||
|
||||
bits < 1 || bits > maxHashcashBits {
|
||||
c.sendNumeric(
|
||||
irc.ErrUnknownMode, "H",
|
||||
"is unknown mode char to me",
|
||||
)
|
||||
|
||||
return modeResult{
|
||||
applied: "",
|
||||
appliedArgs: "",
|
||||
consumed: 1,
|
||||
skip: true,
|
||||
}
|
||||
}
|
||||
|
||||
_ = c.database.SetChannelHashcashBits(
|
||||
ctx, chID, bits,
|
||||
)
|
||||
|
||||
return modeResult{
|
||||
applied: "+H",
|
||||
appliedArgs: " " + bitsStr,
|
||||
consumed: 1,
|
||||
skip: false,
|
||||
}
|
||||
}
|
||||
|
||||
// applyMemberMode handles +o/-o and +v/-v.
|
||||
func (c *Conn) applyMemberMode(
|
||||
ctx context.Context,
|
||||
chID int64,
|
||||
channel string,
|
||||
modeChar rune,
|
||||
adding bool,
|
||||
args []string,
|
||||
argIdx int,
|
||||
) modeResult {
|
||||
if argIdx >= len(args) {
|
||||
return modeResult{
|
||||
applied: "",
|
||||
appliedArgs: "",
|
||||
consumed: 0,
|
||||
skip: true,
|
||||
}
|
||||
}
|
||||
|
||||
targetNick := args[argIdx]
|
||||
|
||||
err := c.svc.ApplyMemberMode(
|
||||
ctx, chID, channel,
|
||||
targetNick, modeChar, adding,
|
||||
)
|
||||
if err != nil {
|
||||
c.sendIRCError(err)
|
||||
|
||||
return modeResult{
|
||||
applied: "",
|
||||
appliedArgs: "",
|
||||
consumed: 1,
|
||||
skip: true,
|
||||
}
|
||||
}
|
||||
|
||||
prefix := "+"
|
||||
if !adding {
|
||||
prefix = "-"
|
||||
}
|
||||
|
||||
return modeResult{
|
||||
applied: prefix + string(modeChar),
|
||||
appliedArgs: " " + targetNick,
|
||||
consumed: 1,
|
||||
skip: false,
|
||||
}
|
||||
}
|
||||
|
||||
// applyChannelModes applies mode changes using the
|
||||
// service for individual mode operations.
|
||||
func (c *Conn) applyChannelModes(
|
||||
@@ -505,52 +623,57 @@ func (c *Conn) applyChannelModes(
|
||||
appliedArgs := ""
|
||||
|
||||
for _, modeChar := range modeStr {
|
||||
var res modeResult
|
||||
|
||||
switch modeChar {
|
||||
case '+':
|
||||
adding = true
|
||||
|
||||
continue
|
||||
case '-':
|
||||
adding = false
|
||||
case 'm', 't':
|
||||
|
||||
continue
|
||||
case 'i', 'm', 'n', 's', 't':
|
||||
_ = c.svc.SetChannelFlag(
|
||||
ctx, chID, modeChar, adding,
|
||||
)
|
||||
|
||||
if adding {
|
||||
applied += "+" + string(modeChar)
|
||||
} else {
|
||||
applied += "-" + string(modeChar)
|
||||
}
|
||||
case 'o', 'v':
|
||||
if argIdx >= len(args) {
|
||||
break
|
||||
prefix := "+"
|
||||
if !adding {
|
||||
prefix = "-"
|
||||
}
|
||||
|
||||
targetNick := args[argIdx]
|
||||
argIdx++
|
||||
|
||||
err := c.svc.ApplyMemberMode(
|
||||
ctx, chID, channel,
|
||||
targetNick, modeChar, adding,
|
||||
res = modeResult{
|
||||
applied: prefix + string(modeChar),
|
||||
appliedArgs: "",
|
||||
consumed: 0,
|
||||
skip: false,
|
||||
}
|
||||
case 'H':
|
||||
res = c.applyHashcashMode(
|
||||
ctx, chID, adding, args, argIdx,
|
||||
)
|
||||
case 'o', 'v':
|
||||
res = c.applyMemberMode(
|
||||
ctx, chID, channel,
|
||||
modeChar, adding, args, argIdx,
|
||||
)
|
||||
if err != nil {
|
||||
c.sendIRCError(err)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if adding {
|
||||
applied += "+" + string(modeChar)
|
||||
} else {
|
||||
applied += "-" + string(modeChar)
|
||||
}
|
||||
|
||||
appliedArgs += " " + targetNick
|
||||
default:
|
||||
c.sendNumeric(
|
||||
irc.ErrUnknownMode,
|
||||
string(modeChar),
|
||||
"is unknown mode char to me",
|
||||
)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
argIdx += res.consumed
|
||||
|
||||
if !res.skip {
|
||||
applied += res.applied
|
||||
appliedArgs += res.appliedArgs
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,15 +19,16 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
maxLineLen = 512
|
||||
readTimeout = 5 * time.Minute
|
||||
writeTimeout = 30 * time.Second
|
||||
dnsTimeout = 3 * time.Second
|
||||
pollInterval = 100 * time.Millisecond
|
||||
pingInterval = 90 * time.Second
|
||||
pongDeadline = 30 * time.Second
|
||||
maxNickLen = 32
|
||||
minPasswordLen = 8
|
||||
maxLineLen = 512
|
||||
readTimeout = 5 * time.Minute
|
||||
writeTimeout = 30 * time.Second
|
||||
dnsTimeout = 3 * time.Second
|
||||
pollInterval = 100 * time.Millisecond
|
||||
pingInterval = 90 * time.Second
|
||||
pongDeadline = 30 * time.Second
|
||||
maxNickLen = 32
|
||||
minPasswordLen = 8
|
||||
maxHashcashBits = 40
|
||||
)
|
||||
|
||||
// cmdHandler is the signature for registered IRC command
|
||||
@@ -434,7 +435,7 @@ func (c *Conn) deliverWelcome() {
|
||||
"CHANTYPES=#",
|
||||
"NICKLEN=32",
|
||||
"PREFIX=(ov)@+",
|
||||
"CHANMODES=,,H,mnst",
|
||||
"CHANMODES=,,H,imnst",
|
||||
"NETWORK="+c.serverSfx,
|
||||
"are supported by this server",
|
||||
)
|
||||
|
||||
@@ -19,12 +19,9 @@ func NewTestServer(
|
||||
database *db.Database,
|
||||
brk *broker.Broker,
|
||||
) *Server {
|
||||
svc := &service.Service{
|
||||
DB: database,
|
||||
Broker: brk,
|
||||
Config: cfg,
|
||||
Log: log,
|
||||
}
|
||||
svc := service.NewTestService(
|
||||
database, brk, cfg, log,
|
||||
)
|
||||
|
||||
return &Server{ //nolint:exhaustruct
|
||||
log: log,
|
||||
|
||||
Reference in New Issue
Block a user