1 Commits
Author SHA1 Message Date
clawbot d31fd529e2 Make an IRC listener connection follow its session's nick (closes #184)
check / check (push) Waiting to run
An IRC listener connection kept its own copy of its nick, so after the session's nick was changed from another client it went on sending under the old nick, which another user could then take. Now, whenever the relay passes on a NICK line and after the connection's own NICK is stored, the connection takes the nick its session holds in the database, read by session ID under the connection's lock. Taking the nick from the database rather than from the line means a connection can never follow another user's rename and send as them. Handlers read the nick under the same lock.

Untested: an HTTP API rename landing while the connection's own NICK is being stored; that order cannot be produced reliably.

Model: opus-5-5
2026-10-21 17:56:02 +02:00
3 changed files with 22 additions and 14 deletions
+4 -3
View File
@@ -92,9 +92,10 @@ func (c *Conn) handleNick(
return
}
c.mu.Lock()
c.nick = newNick
c.mu.Unlock()
// Take the nick the session holds, not newNick: another
// client of the session may have changed it again since
// ChangeNick stored newNick.
c.loadSessionNick(ctx)
// Echo NICK change to the client on wire.
c.send(FormatMessage(oldMask, "NICK", newNick))
+16
View File
@@ -313,6 +313,22 @@ func (c *Conn) currentNick() string {
return c.nick
}
// loadSessionNick sets c.nick to the nick the connection's
// session holds in the database, read by session ID. The
// connection's own NICK and the relay passing on a NICK line
// both call it, and it reads and sets the nick under c.mu, so
// whichever runs last leaves the connection on its session's
// nick. If the read fails, c.nick is left as it is.
func (c *Conn) loadSessionNick(ctx context.Context) {
c.mu.Lock()
defer c.mu.Unlock()
nick, err := c.database.GetSessionNick(ctx, c.sessionID)
if err == nil {
c.nick = nick
}
}
// send writes a formatted IRC line to the connection.
func (c *Conn) send(line string) {
c.sendWithin(writeTimeout, line)
+2 -11
View File
@@ -234,22 +234,13 @@ func (c *Conn) deliverPart(msg *db.IRCMessage, text string) {
// change may be its own session's, made from another client,
// so the connection first takes the nick its session holds
// now. It does not go by the line's old nick: the connection
// may have taken that nick after another user gave it up. It
// reads the nick under c.mu, so that a NICK sent on the
// connection meanwhile is not undone.
// may have taken that nick after another user gave it up.
func (c *Conn) deliverNickChange(
ctx context.Context,
msg *db.IRCMessage,
newNick string,
) {
c.mu.Lock()
nick, err := c.database.GetSessionNick(ctx, c.sessionID)
if err == nil {
c.nick = nick
}
c.mu.Unlock()
c.loadSessionNick(ctx)
prefix := msg.From + "!" + msg.From + "@*"