From 1ef41d6020f6e5c2e243dfa63d5615b0df2597e9 Mon Sep 17 00:00:00 2001 From: Alex Jaspersen Date: Thu, 7 May 2020 00:16:22 +0000 Subject: [PATCH 1/3] Hide +i users from WHO * queries. Fixes #990. --- irc/handlers.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/irc/handlers.go b/irc/handlers.go index dbcb6793..3c7dfb1e 100644 --- a/irc/handlers.go +++ b/irc/handlers.go @@ -2637,7 +2637,9 @@ func whoHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Respo } } else { for mclient := range server.clients.FindAll(mask) { - client.rplWhoReply(nil, mclient, rb) + if !mclient.HasMode(modes.Invisible) || isOper { + client.rplWhoReply(nil, mclient, rb) + } } } From b3cfcc1289b7538c07a56458a97dd9824ab79063 Mon Sep 17 00:00:00 2001 From: Alex Jaspersen Date: Thu, 7 May 2020 03:00:28 +0000 Subject: [PATCH 2/3] Show users in WHO queries if they are friends, i.e. they share a channel. --- irc/client.go | 15 +++++++++++++++ irc/handlers.go | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/irc/client.go b/irc/client.go index b636678a..eb44cc9b 100644 --- a/irc/client.go +++ b/irc/client.go @@ -1022,6 +1022,21 @@ func (client *Client) ModeString() (str string) { return "+" + client.modes.String() } +// IsFriend() returns true if the given otherClient shares a channel with this client, or if they are the same user. +func (client *Client) IsFriend(otherClient *Client) bool { + if client == otherClient { + return true + } + + for _, channel := range client.Channels() { + if channel.hasClient(otherClient) { + return true + } + } + + return false +} + // Friends refers to clients that share a channel with this client. func (client *Client) Friends(capabs ...caps.Capability) (result map[*Session]bool) { result = make(map[*Session]bool) diff --git a/irc/handlers.go b/irc/handlers.go index 3c7dfb1e..fd53dd41 100644 --- a/irc/handlers.go +++ b/irc/handlers.go @@ -2637,7 +2637,7 @@ func whoHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Respo } } else { for mclient := range server.clients.FindAll(mask) { - if !mclient.HasMode(modes.Invisible) || isOper { + if isOper || !mclient.HasMode(modes.Invisible) || mclient.IsFriend(client) { client.rplWhoReply(nil, mclient, rb) } } From a6e4a26cbbcac4f813004862b23d4ec725d89810 Mon Sep 17 00:00:00 2001 From: Alex Jaspersen Date: Fri, 8 May 2020 01:55:47 +0000 Subject: [PATCH 3/3] Performance optimization for determining friends in WHO . Construct a hash set of the user's channels and check that rather than querying channel membership, to reduce the number of locks that need to be acquired. --- irc/client.go | 15 --------------- irc/handlers.go | 22 +++++++++++++++++++++- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/irc/client.go b/irc/client.go index eb44cc9b..b636678a 100644 --- a/irc/client.go +++ b/irc/client.go @@ -1022,21 +1022,6 @@ func (client *Client) ModeString() (str string) { return "+" + client.modes.String() } -// IsFriend() returns true if the given otherClient shares a channel with this client, or if they are the same user. -func (client *Client) IsFriend(otherClient *Client) bool { - if client == otherClient { - return true - } - - for _, channel := range client.Channels() { - if channel.hasClient(otherClient) { - return true - } - } - - return false -} - // Friends refers to clients that share a channel with this client. func (client *Client) Friends(capabs ...caps.Capability) (result map[*Session]bool) { result = make(map[*Session]bool) diff --git a/irc/handlers.go b/irc/handlers.go index fd53dd41..c337f88f 100644 --- a/irc/handlers.go +++ b/irc/handlers.go @@ -2636,8 +2636,28 @@ func whoHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Respo } } } else { + // Construct set of channels the client is in. + userChannels := make(map[*Channel]bool) + for _, channel := range client.Channels() { + userChannels[channel] = true + } + + // Another client is a friend if they share at least one channel, or they are the same client. + isFriend := func(otherClient *Client) bool { + if client == otherClient { + return true + } + + for _, channel := range otherClient.Channels() { + if userChannels[channel] { + return true + } + } + return false + } + for mclient := range server.clients.FindAll(mask) { - if isOper || !mclient.HasMode(modes.Invisible) || mclient.IsFriend(client) { + if isOper || !mclient.HasMode(modes.Invisible) || isFriend(mclient) { client.rplWhoReply(nil, mclient, rb) } }