From 4aa1aa371db9b62192e7da320153f7f2b9dfcdfc Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Wed, 3 Jan 2024 10:52:03 -0500 Subject: [PATCH] fix #2113 Persisting always-on clients was panicking if client X believed it was a member of channel Y, but channel Y didn't have a record of client X. --- irc/channel.go | 7 +++++-- irc/client.go | 6 +++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/irc/channel.go b/irc/channel.go index a39035e8..f8c6e5de 100644 --- a/irc/channel.go +++ b/irc/channel.go @@ -545,11 +545,14 @@ func (channel *Channel) ClientStatus(client *Client) (present bool, joinTimeSecs // helper for persisting channel-user modes for always-on clients; // return the channel name and all channel-user modes for a client -func (channel *Channel) alwaysOnStatus(client *Client) (chname string, status alwaysOnChannelStatus) { +func (channel *Channel) alwaysOnStatus(client *Client) (ok bool, chname string, status alwaysOnChannelStatus) { channel.stateMutex.RLock() defer channel.stateMutex.RUnlock() chname = channel.name - data := channel.members[client] + data, ok := channel.members[client] + if !ok { + return + } status.Modes = data.modes.String() status.JoinTime = data.joinTime return diff --git a/irc/client.go b/irc/client.go index 80a60a23..57eb1599 100644 --- a/irc/client.go +++ b/irc/client.go @@ -1803,7 +1803,11 @@ func (client *Client) performWrite(additionalDirtyBits uint) { channels := client.Channels() channelToModes := make(map[string]alwaysOnChannelStatus, len(channels)) for _, channel := range channels { - chname, status := channel.alwaysOnStatus(client) + ok, chname, status := channel.alwaysOnStatus(client) + if !ok { + client.server.logger.Error("internal", "client and channel membership out of sync", chname, client.Nick()) + continue + } channelToModes[chname] = status } client.server.accounts.saveChannels(account, channelToModes)