fix: include hostmask in NAMES replies (RPL_NAMREPLY)
All checks were successful
check / check (push) Successful in 1m4s
All checks were successful
check / check (push) Successful in 1m4s
This commit is contained in:
@@ -1014,7 +1014,7 @@ the server to the client (never C2S) and use 3-digit string codes in the
|
||||
| `331` | RPL_NOTOPIC | Channel has no topic (on JOIN) | `{"command":"331","to":"alice","params":["#general"],"body":["No topic is set"]}` |
|
||||
| `332` | RPL_TOPIC | On JOIN or TOPIC query | `{"command":"332","to":"alice","params":["#general"],"body":["Welcome!"]}` |
|
||||
| `352` | RPL_WHOREPLY | In response to WHO | `{"command":"352","to":"alice","params":["#general","bobident","host.example.com","neoirc","bob","H"],"body":["0 bob"]}` |
|
||||
| `353` | RPL_NAMREPLY | On JOIN or NAMES query | `{"command":"353","to":"alice","params":["=","#general"],"body":["@op1 alice bob +voiced1"]}` |
|
||||
| `353` | RPL_NAMREPLY | On JOIN or NAMES query | `{"command":"353","to":"alice","params":["=","#general"],"body":["op1!op1@host1 alice!alice@host2 bob!bob@host3"]}` |
|
||||
| `366` | RPL_ENDOFNAMES | End of NAMES response | `{"command":"366","to":"alice","params":["#general"],"body":["End of /NAMES list"]}` |
|
||||
| `372` | RPL_MOTD | MOTD line | `{"command":"372","to":"alice","body":["Welcome to the server"]}` |
|
||||
| `375` | RPL_MOTDSTART | Start of MOTD | `{"command":"375","to":"alice","body":["- neoirc-server Message of the Day -"]}` |
|
||||
|
||||
@@ -1474,16 +1474,16 @@ func (hdlr *Handlers) deliverNamesNumerics(
|
||||
)
|
||||
|
||||
if memErr == nil && len(members) > 0 {
|
||||
nicks := make([]string, 0, len(members))
|
||||
entries := make([]string, 0, len(members))
|
||||
|
||||
for _, mem := range members {
|
||||
nicks = append(nicks, mem.Nick)
|
||||
entries = append(entries, mem.Hostmask())
|
||||
}
|
||||
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, irc.RplNamReply, nick,
|
||||
[]string{"=", channel},
|
||||
strings.Join(nicks, " "),
|
||||
strings.Join(entries, " "),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2082,16 +2082,16 @@ func (hdlr *Handlers) handleNames(
|
||||
ctx, chID,
|
||||
)
|
||||
if memErr == nil && len(members) > 0 {
|
||||
nicks := make([]string, 0, len(members))
|
||||
entries := make([]string, 0, len(members))
|
||||
|
||||
for _, mem := range members {
|
||||
nicks = append(nicks, mem.Nick)
|
||||
entries = append(entries, mem.Hostmask())
|
||||
}
|
||||
|
||||
hdlr.enqueueNumeric(
|
||||
ctx, clientID, irc.RplNamReply, nick,
|
||||
[]string{"=", channel},
|
||||
strings.Join(nicks, " "),
|
||||
strings.Join(entries, " "),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -2400,3 +2400,135 @@ func TestNickBroadcastToChannels(t *testing.T) {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNamesShowsHostmask(t *testing.T) {
|
||||
tserver := newTestServer(t)
|
||||
|
||||
queryToken, lastID := setupChannelWithIdentMember(
|
||||
tserver, "namesmember", "nmident",
|
||||
"namesquery", "#namestest",
|
||||
)
|
||||
|
||||
// Issue an explicit NAMES command.
|
||||
tserver.sendCommand(queryToken, map[string]any{
|
||||
commandKey: "NAMES",
|
||||
toKey: "#namestest",
|
||||
})
|
||||
|
||||
msgs, _ := tserver.pollMessages(queryToken, lastID)
|
||||
|
||||
assertNamesHostmask(
|
||||
t, msgs, "namesmember", "nmident",
|
||||
)
|
||||
}
|
||||
|
||||
func TestNamesOnJoinShowsHostmask(t *testing.T) {
|
||||
tserver := newTestServer(t)
|
||||
|
||||
// First user joins to populate the channel.
|
||||
firstToken := tserver.createSessionWithUsername(
|
||||
"joinmem", "jmident",
|
||||
)
|
||||
|
||||
tserver.sendCommand(firstToken, map[string]any{
|
||||
commandKey: joinCmd, toKey: "#joinnamestest",
|
||||
})
|
||||
|
||||
// Second user joins; the JOIN triggers
|
||||
// deliverNamesNumerics which should include
|
||||
// hostmask data.
|
||||
joinerToken := tserver.createSession("joiner")
|
||||
|
||||
tserver.sendCommand(joinerToken, map[string]any{
|
||||
commandKey: joinCmd, toKey: "#joinnamestest",
|
||||
})
|
||||
|
||||
msgs, _ := tserver.pollMessages(joinerToken, 0)
|
||||
|
||||
assertNamesHostmask(
|
||||
t, msgs, "joinmem", "jmident",
|
||||
)
|
||||
}
|
||||
|
||||
// setupChannelWithIdentMember creates a member session
|
||||
// with username, joins a channel, then creates a querier
|
||||
// and joins the same channel. Returns the querier token
|
||||
// and last message ID.
|
||||
func setupChannelWithIdentMember(
|
||||
tserver *testServer,
|
||||
memberNick, memberUsername,
|
||||
querierNick, channel string,
|
||||
) (string, int64) {
|
||||
tserver.t.Helper()
|
||||
|
||||
memberToken := tserver.createSessionWithUsername(
|
||||
memberNick, memberUsername,
|
||||
)
|
||||
|
||||
tserver.sendCommand(memberToken, map[string]any{
|
||||
commandKey: joinCmd, toKey: channel,
|
||||
})
|
||||
|
||||
queryToken := tserver.createSession(querierNick)
|
||||
|
||||
tserver.sendCommand(queryToken, map[string]any{
|
||||
commandKey: joinCmd, toKey: channel,
|
||||
})
|
||||
|
||||
_, lastID := tserver.pollMessages(queryToken, 0)
|
||||
|
||||
return queryToken, lastID
|
||||
}
|
||||
|
||||
// assertNamesHostmask verifies that a RPL_NAMREPLY (353)
|
||||
// message contains the expected nick with hostmask format
|
||||
// (nick!user@host).
|
||||
func assertNamesHostmask(
|
||||
t *testing.T,
|
||||
msgs []map[string]any,
|
||||
targetNick, expectedUsername string,
|
||||
) {
|
||||
t.Helper()
|
||||
|
||||
for _, msg := range msgs {
|
||||
code, ok := msg["code"].(float64)
|
||||
if !ok || int(code) != 353 {
|
||||
continue
|
||||
}
|
||||
|
||||
raw, exists := msg["body"]
|
||||
if !exists || raw == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
arr, isArr := raw.([]any)
|
||||
if !isArr || len(arr) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
bodyStr, isStr := arr[0].(string)
|
||||
if !isStr {
|
||||
continue
|
||||
}
|
||||
|
||||
// Look for the target nick's hostmask entry.
|
||||
expected := targetNick + "!" +
|
||||
expectedUsername + "@"
|
||||
|
||||
if !strings.Contains(bodyStr, expected) {
|
||||
t.Fatalf(
|
||||
"expected NAMES body to contain %q, "+
|
||||
"got %q",
|
||||
expected, bodyStr,
|
||||
)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
t.Fatalf(
|
||||
"expected RPL_NAMREPLY (353) with hostmask "+
|
||||
"for %s, msgs: %v",
|
||||
targetNick, msgs,
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user