diff --git a/irc/channel.go b/irc/channel.go index b014aba9..8c5c9296 100644 --- a/irc/channel.go +++ b/irc/channel.go @@ -366,9 +366,11 @@ func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *Resp channel.stateMutex.RLock() chname := channel.name + chcfname := channel.nameCasefolded founder := channel.registeredFounder channel.stateMutex.RUnlock() account := client.Account() + nickMaskCasefolded := client.NickMaskCasefolded() hasPrivs := isSajoin || (founder != "" && founder == account) if !hasPrivs && channel.IsFull() { @@ -381,15 +383,15 @@ func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *Resp return } - isInvited := channel.lists[modes.InviteMask].Match(client.nickMaskCasefolded) + isInvited := client.CheckInvited(chcfname) || channel.lists[modes.InviteMask].Match(nickMaskCasefolded) if !hasPrivs && channel.flags.HasMode(modes.InviteOnly) && !isInvited { rb.Add(nil, client.server.name, ERR_INVITEONLYCHAN, chname, fmt.Sprintf(client.t("Cannot join channel (+%s)"), "i")) return } - if !hasPrivs && channel.lists[modes.BanMask].Match(client.nickMaskCasefolded) && + if !hasPrivs && channel.lists[modes.BanMask].Match(nickMaskCasefolded) && !isInvited && - !channel.lists[modes.ExceptMask].Match(client.nickMaskCasefolded) { + !channel.lists[modes.ExceptMask].Match(nickMaskCasefolded) { rb.Add(nil, client.server.name, ERR_BANNEDFROMCHAN, chname, fmt.Sprintf(client.t("Cannot join channel (+%s)"), "b")) return } @@ -965,12 +967,8 @@ func (channel *Channel) Invite(invitee *Client, inviter *Client, rb *ResponseBuf return } - //TODO(dan): handle this more nicely, keep a list of last X invited channels on invitee rather than explicitly modifying the invite list? if channel.flags.HasMode(modes.InviteOnly) { - nmc := invitee.NickCasefolded() - channel.stateMutex.Lock() - channel.lists[modes.InviteMask].Add(nmc) - channel.stateMutex.Unlock() + invitee.Invite(channel.NameCasefolded()) } for _, member := range channel.Members() { diff --git a/irc/client.go b/irc/client.go index dfadcb64..2ee4ba89 100644 --- a/irc/client.go +++ b/irc/client.go @@ -69,6 +69,7 @@ type Client struct { hops int hostname string idletimer *IdleTimer + invitedTo map[string]bool isDestroyed bool isQuitting bool languages []string @@ -1102,3 +1103,26 @@ func (client *Client) generateResumeToken() (token string, err error) { return client.resumeToken, err } + +// Records that the client has been invited to join an invite-only channel +func (client *Client) Invite(casefoldedChannel string) { + client.stateMutex.Lock() + defer client.stateMutex.Unlock() + + if client.invitedTo == nil { + client.invitedTo = make(map[string]bool) + } + + client.invitedTo[casefoldedChannel] = true +} + +// Checks that the client was invited to join a given channel +func (client *Client) CheckInvited(casefoldedChannel string) (invited bool) { + client.stateMutex.Lock() + defer client.stateMutex.Unlock() + + invited = client.invitedTo[casefoldedChannel] + // joining an invited channel "uses up" your invite, so you can't rejoin on kick + delete(client.invitedTo, casefoldedChannel) + return +} diff --git a/irc/getters.go b/irc/getters.go index ad072574..52a3b2d7 100644 --- a/irc/getters.go +++ b/irc/getters.go @@ -84,6 +84,12 @@ func (client *Client) NickCasefolded() string { return client.nickCasefolded } +func (client *Client) NickMaskCasefolded() string { + client.stateMutex.RLock() + defer client.stateMutex.RUnlock() + return client.nickMaskCasefolded +} + func (client *Client) Username() string { client.stateMutex.RLock() defer client.stateMutex.RUnlock()