Both transports sent an empty string as the user modes in RPL_MYINFO. On the IRC listener that left an empty parameter in the middle of the line, so a client that splits on spaces read the channel modes as the user modes. The reply now lists o and w, the user modes the server supports. Both transports read them from one constant in internal/service, next to the code that decides which user modes MODE accepts. The README and the schema notes now show what the HTTP API sends. Model: opus-5-5
This commit was merged in pull request #237.
This commit is contained in:
@@ -1236,7 +1236,7 @@ IRC listener sends the usual 3-digit code. Over the HTTP API, 005 arrives named
|
|||||||
| `001` | RPL_WELCOME | After session creation or login, on MOTD | `{"command":"RPL_WELCOME","code":1,"to":"alice","body":["Welcome to the network, alice"]}` |
|
| `001` | RPL_WELCOME | After session creation or login, on MOTD | `{"command":"RPL_WELCOME","code":1,"to":"alice","body":["Welcome to the network, alice"]}` |
|
||||||
| `002` | RPL_YOURHOST | After session creation or login, on MOTD | `{"command":"RPL_YOURHOST","code":2,"to":"alice","body":["Your host is neoirc, running version 0.1"]}` |
|
| `002` | RPL_YOURHOST | After session creation or login, on MOTD | `{"command":"RPL_YOURHOST","code":2,"to":"alice","body":["Your host is neoirc, running version 0.1"]}` |
|
||||||
| `003` | RPL_CREATED | After session creation or login, on MOTD | `{"command":"RPL_CREATED","code":3,"to":"alice","body":["This server was created 2026-02-10"]}` |
|
| `003` | RPL_CREATED | After session creation or login, on MOTD | `{"command":"RPL_CREATED","code":3,"to":"alice","body":["This server was created 2026-02-10"]}` |
|
||||||
| `004` | RPL_MYINFO | After session creation or login, on MOTD | `{"command":"RPL_MYINFO","code":4,"to":"alice","params":["neoirc","0.1","","ikmnostl"]}` |
|
| `004` | RPL_MYINFO | After session creation or login, on MOTD | `{"command":"RPL_MYINFO","code":4,"to":"alice","params":["neoirc","0.1","ow","ikmnostl"]}` |
|
||||||
| `005` | RPL_ISUPPORT | After session creation or login, on MOTD | `{"command":"RPL_BOUNCE","code":5,"to":"alice","params":["CHANTYPES=#","NICKLEN=32","PREFIX=(ov)@+","CHANMODES=b,k,Hl,imnst","NETWORK=neoirc","CASEMAPPING=ascii"],"body":["are supported by this server"]}` |
|
| `005` | RPL_ISUPPORT | After session creation or login, on MOTD | `{"command":"RPL_BOUNCE","code":5,"to":"alice","params":["CHANTYPES=#","NICKLEN=32","PREFIX=(ov)@+","CHANMODES=b,k,Hl,imnst","NETWORK=neoirc","CASEMAPPING=ascii"],"body":["are supported by this server"]}` |
|
||||||
| `221` | RPL_UMODEIS | In response to user MODE | `{"command":"RPL_UMODEIS","code":221,"to":"alice","body":["+"]}` |
|
| `221` | RPL_UMODEIS | In response to user MODE | `{"command":"RPL_UMODEIS","code":221,"to":"alice","body":["+"]}` |
|
||||||
| `251` | RPL_LUSERCLIENT | On connect, MOTD or LUSERS | `{"command":"RPL_LUSERCLIENT","code":251,"to":"alice","body":["There are 5 users and 0 invisible on 1 servers"]}` |
|
| `251` | RPL_LUSERCLIENT | On connect, MOTD or LUSERS | `{"command":"RPL_LUSERCLIENT","code":251,"to":"alice","body":["There are 5 users and 0 invisible on 1 servers"]}` |
|
||||||
|
|||||||
@@ -412,7 +412,7 @@ func (hdlr *Handlers) deliverWelcome(
|
|||||||
// 004 RPL_MYINFO
|
// 004 RPL_MYINFO
|
||||||
hdlr.enqueueNumeric(
|
hdlr.enqueueNumeric(
|
||||||
ctx, clientID, irc.RplMyInfo, nick,
|
ctx, clientID, irc.RplMyInfo, nick,
|
||||||
[]string{srvName, version, "", "ikmnostl"},
|
[]string{srvName, version, service.UserModes, "ikmnostl"},
|
||||||
"",
|
"",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -6797,6 +6797,30 @@ func TestISUPPORT(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestMyInfoListsUserModes verifies the 004 numeric lists
|
||||||
|
// the user modes o and w as its third parameter.
|
||||||
|
func TestMyInfoListsUserModes(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tserver := newTestServer(t)
|
||||||
|
token := tserver.createSession("myinfo")
|
||||||
|
|
||||||
|
msgs, _ := tserver.pollMessages(token, 0)
|
||||||
|
|
||||||
|
myInfo := findNumericWithParams(msgs, "004")
|
||||||
|
if myInfo == nil {
|
||||||
|
t.Fatal("expected 004 RPL_MYINFO")
|
||||||
|
}
|
||||||
|
|
||||||
|
params := getNumericParams(myInfo)
|
||||||
|
if len(params) != 4 || params[2] != "ow" {
|
||||||
|
t.Fatalf(
|
||||||
|
"004 params %v, want four with user modes ow third",
|
||||||
|
params,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestNonOpCannotSetModes verifies non-operators
|
// TestNonOpCannotSetModes verifies non-operators
|
||||||
// cannot set +i, +s, +k, +l, +b.
|
// cannot set +i, +s, +k, +l, +b.
|
||||||
func TestNonOpCannotSetModes(t *testing.T) {
|
func TestNonOpCannotSetModes(t *testing.T) {
|
||||||
|
|||||||
@@ -605,7 +605,7 @@ func (c *Conn) deliverWelcome() {
|
|||||||
)
|
)
|
||||||
c.sendNumeric(
|
c.sendNumeric(
|
||||||
irc.RplMyInfo,
|
irc.RplMyInfo,
|
||||||
c.serverSfx, "neoirc", "", "mnst",
|
c.serverSfx, "neoirc", service.UserModes, "mnst",
|
||||||
)
|
)
|
||||||
c.sendNumeric(
|
c.sendNumeric(
|
||||||
irc.RplIsupport,
|
irc.RplIsupport,
|
||||||
|
|||||||
@@ -486,6 +486,22 @@ func TestWelcomeContainsNick(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestMyInfoListsUserModes checks that RPL_MYINFO lists the
|
||||||
|
// user modes o and w, with no empty parameter before the
|
||||||
|
// channel modes.
|
||||||
|
func TestMyInfoListsUserModes(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
env := newTestEnv(t)
|
||||||
|
lines := env.dial(t).register("carol")
|
||||||
|
|
||||||
|
assertContains(
|
||||||
|
t, lines,
|
||||||
|
":test.irc 004 carol test.irc neoirc ow mnst",
|
||||||
|
"RPL_MYINFO with user modes",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
func TestPingPong(t *testing.T) {
|
func TestPingPong(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|||||||
@@ -1169,6 +1169,12 @@ func (s *Service) ApplyUserMode(
|
|||||||
return s.QueryUserMode(ctx, sessionID)
|
return s.QueryUserMode(ctx, sessionID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UserModes lists the user modes the server supports, as
|
||||||
|
// RPL_MYINFO sends them: o (server operator) and w
|
||||||
|
// (receives WALLOPS). parseUserModeString must accept the
|
||||||
|
// same letters.
|
||||||
|
const UserModes = "ow"
|
||||||
|
|
||||||
// parseUserModeString parses a user mode string such as
|
// parseUserModeString parses a user mode string such as
|
||||||
// "+w", "-o" or "+w-o" into the new value of each flag it
|
// "+w", "-o" or "+w-o" into the new value of each flag it
|
||||||
// names, or nil for a flag it does not name; a later letter
|
// names, or nil for a flag it does not name; a later letter
|
||||||
|
|||||||
+2
-2
@@ -144,8 +144,8 @@ Where a schema file and the HTTP API disagree, the HTTP API is right:
|
|||||||
with `ERR_NEEDMOREPARAMS` (`461`). A `body` of `[""]` clears the topic.
|
with `ERR_NEEDMOREPARAMS` (`461`). A `body` of `[""]` clears the topic.
|
||||||
- `003.json` describes the server's creation date. The HTTP API sends the date
|
- `003.json` describes the server's creation date. The HTTP API sends the date
|
||||||
the server last started.
|
the server last started.
|
||||||
- `004.json` has example mode lists. The HTTP API sends an empty string as the
|
- `004.json` has example mode lists. The HTTP API sends `ow` as the user modes
|
||||||
user modes and `ikmnostl` as the channel modes.
|
and `ikmnostl` as the channel modes.
|
||||||
- `322.json` says `LIST` gets one reply per channel. The server leaves out a
|
- `322.json` says `LIST` gets one reply per channel. The server leaves out a
|
||||||
secret channel, one with mode `+s`, unless the user is on it.
|
secret channel, one with mode `+s`, unless the user is on it.
|
||||||
- `332.json` says `RPL_TOPIC` answers a `TOPIC` query. The HTTP API has no such
|
- `332.json` says `RPL_TOPIC` answers a `TOPIC` query. The HTTP API has no such
|
||||||
|
|||||||
Reference in New Issue
Block a user