From b7ec121c19d221799b10bcf60a92b7e602345887 Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Wed, 13 Feb 2019 14:38:10 -0500 Subject: [PATCH] fix some KICK issues reported by bogdomania 1. KICK without privileges incorrectly returned ERR_CANNOTSENDTOCHAN 2. Halfops should be able to kick voice and unprivileged, but not other halfops --- irc/channel.go | 51 +++++++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/irc/channel.go b/irc/channel.go index e7c31043..21d7afb6 100644 --- a/irc/channel.go +++ b/irc/channel.go @@ -241,12 +241,7 @@ func (channel *Channel) Names(client *Client, rb *ResponseBuffer) { rb.Add(nil, client.server.name, RPL_ENDOFNAMES, client.nick, channel.name, client.t("End of NAMES list")) } -// ClientIsAtLeast returns whether the client has at least the given channel privilege. -func (channel *Channel) ClientIsAtLeast(client *Client, permission modes.Mode) bool { - channel.stateMutex.RLock() - clientModes := channel.members[client] - channel.stateMutex.RUnlock() - +func channelUserModeIsAtLeast(clientModes *modes.ModeSet, permission modes.Mode) bool { if clientModes == nil { return false } @@ -264,6 +259,14 @@ func (channel *Channel) ClientIsAtLeast(client *Client, permission modes.Mode) b return false } +// ClientIsAtLeast returns whether the client has at least the given channel privilege. +func (channel *Channel) ClientIsAtLeast(client *Client, permission modes.Mode) bool { + channel.stateMutex.RLock() + clientModes := channel.members[client] + channel.stateMutex.RUnlock() + return channelUserModeIsAtLeast(clientModes, permission) +} + func (channel *Channel) ClientPrefixes(client *Client, isMultiPrefix bool) string { channel.stateMutex.RLock() defer channel.stateMutex.RUnlock() @@ -277,24 +280,26 @@ func (channel *Channel) ClientPrefixes(client *Client, isMultiPrefix bool) strin func (channel *Channel) ClientHasPrivsOver(client *Client, target *Client) bool { channel.stateMutex.RLock() - defer channel.stateMutex.RUnlock() - clientModes := channel.members[client] targetModes := channel.members[target] - result := false - for _, mode := range modes.ChannelPrivModes { - if clientModes.HasMode(mode) { - result = true - // admins cannot kick other admins - if mode == modes.ChannelAdmin && targetModes.HasMode(modes.ChannelAdmin) { - result = false - } - break - } else if targetModes.HasMode(mode) { - break - } + channel.stateMutex.RUnlock() + + if clientModes.HasMode(modes.ChannelFounder) { + // founder can kick anyone + return true + } else if clientModes.HasMode(modes.ChannelAdmin) { + // admins cannot kick other admins + return !channelUserModeIsAtLeast(targetModes, modes.ChannelAdmin) + } else if clientModes.HasMode(modes.ChannelOperator) { + // operators *can* kick other operators + return !channelUserModeIsAtLeast(targetModes, modes.ChannelAdmin) + } else if clientModes.HasMode(modes.Halfop) { + // halfops cannot kick other halfops + return !channelUserModeIsAtLeast(targetModes, modes.Halfop) + } else { + // voice and unprivileged cannot kick anyone + return false } - return result } func (channel *Channel) hasClient(client *Client) bool { @@ -952,10 +957,6 @@ func (channel *Channel) Kick(client *Client, target *Client, comment string, rb rb.Add(nil, client.server.name, ERR_NOTONCHANNEL, channel.name, client.t("You're not on that channel")) return } - if !channel.ClientIsAtLeast(client, modes.ChannelOperator) { - rb.Add(nil, client.server.name, ERR_CANNOTSENDTOCHAN, channel.name, client.t("Cannot send to channel")) - return - } if !channel.hasClient(target) { rb.Add(nil, client.server.name, ERR_USERNOTINCHANNEL, client.nick, channel.name, client.t("They aren't on that channel")) return