3
0
mirror of https://github.com/ergochat/ergo.git synced 2024-11-26 13:59:44 +01:00
This commit is contained in:
Shivaram Lingamneni 2020-06-30 19:24:56 -04:00
parent 79764cd549
commit 067e62ad5e
6 changed files with 83 additions and 57 deletions

View File

@ -660,7 +660,7 @@ func (channel *Channel) AddHistoryItem(item history.Item, account string) (err e
} }
// Join joins the given client to this channel (if they can be joined). // Join joins the given client to this channel (if they can be joined).
func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *ResponseBuffer) { func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *ResponseBuffer) error {
details := client.Details() details := client.Details()
channel.stateMutex.RLock() channel.stateMutex.RLock()
@ -676,39 +676,43 @@ func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *Resp
if alreadyJoined { if alreadyJoined {
// no message needs to be sent // no message needs to be sent
return return nil
} }
// the founder can always join (even if they disabled auto +q on join); // 0. SAJOIN always succeeds
// anyone who automatically receives halfop or higher can always join // 1. the founder can always join (even if they disabled auto +q on join)
hasPrivs := isSajoin || (founder != "" && founder == details.account) || (persistentMode != 0 && persistentMode != modes.Voice) // 2. anyone who automatically receives halfop or higher can always join
// 3. people invited with INVITE can join
if !hasPrivs && limit != 0 && chcount >= limit { hasPrivs := isSajoin || (founder != "" && founder == details.account) ||
rb.Add(nil, client.server.name, ERR_CHANNELISFULL, details.nick, chname, fmt.Sprintf(client.t("Cannot join channel (+%s)"), "l")) (persistentMode != 0 && persistentMode != modes.Voice) ||
return client.CheckInvited(chcfname)
if !hasPrivs {
if limit != 0 && chcount >= limit {
return errLimitExceeded
} }
if !hasPrivs && chkey != "" && !utils.SecretTokensMatch(chkey, key) { if chkey != "" && !utils.SecretTokensMatch(chkey, key) {
rb.Add(nil, client.server.name, ERR_BADCHANNELKEY, details.nick, chname, fmt.Sprintf(client.t("Cannot join channel (+%s)"), "k")) return errWrongChannelKey
return
} }
isInvited := client.CheckInvited(chcfname) || channel.lists[modes.InviteMask].Match(details.nickMaskCasefolded) if channel.flags.HasMode(modes.InviteOnly) &&
if !hasPrivs && channel.flags.HasMode(modes.InviteOnly) && !isInvited { !channel.lists[modes.InviteMask].Match(details.nickMaskCasefolded) {
rb.Add(nil, client.server.name, ERR_INVITEONLYCHAN, details.nick, chname, fmt.Sprintf(client.t("Cannot join channel (+%s)"), "i")) return errInviteOnly
return
} }
if !hasPrivs && channel.lists[modes.BanMask].Match(details.nickMaskCasefolded) && if channel.lists[modes.BanMask].Match(details.nickMaskCasefolded) &&
!isInvited && !channel.lists[modes.ExceptMask].Match(details.nickMaskCasefolded) &&
!channel.lists[modes.ExceptMask].Match(details.nickMaskCasefolded) { !channel.lists[modes.InviteMask].Match(details.nickMaskCasefolded) {
rb.Add(nil, client.server.name, ERR_BANNEDFROMCHAN, details.nick, chname, fmt.Sprintf(client.t("Cannot join channel (+%s)"), "b")) return errBanned
return
} }
if !hasPrivs && channel.flags.HasMode(modes.RegisteredOnly) && details.account == "" && !isInvited { if channel.flags.HasMode(modes.RegisteredOnly) && details.account == "" {
rb.Add(nil, client.server.name, ERR_NEEDREGGEDNICK, details.nick, chname, client.t("You must be registered to join that channel")) return errRegisteredOnly
return }
}
if joinErr := client.addChannel(channel, rb == nil); joinErr != nil {
return joinErr
} }
client.server.logger.Debug("join", fmt.Sprintf("%s joined channel %s", details.nick, chname)) client.server.logger.Debug("join", fmt.Sprintf("%s joined channel %s", details.nick, chname))
@ -753,10 +757,8 @@ func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *Resp
channel.AddHistoryItem(histItem, details.account) channel.AddHistoryItem(histItem, details.account)
} }
client.addChannel(channel, rb == nil)
if rb == nil { if rb == nil {
return return nil
} }
var modestr string var modestr string
@ -799,6 +801,7 @@ func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *Resp
rb.Flush(true) rb.Flush(true)
channel.autoReplayHistory(client, rb, message.Msgid) channel.autoReplayHistory(client, rb, message.Msgid)
return nil
} }
func (channel *Channel) autoReplayHistory(client *Client, rb *ResponseBuffer, skipMsgid string) { func (channel *Channel) autoReplayHistory(client *Client, rb *ResponseBuffer, skipMsgid string) {
@ -1437,9 +1440,7 @@ func (channel *Channel) Invite(invitee *Client, inviter *Client, rb *ResponseBuf
return return
} }
if channel.flags.HasMode(modes.InviteOnly) {
invitee.Invite(channel.NameCasefolded()) invitee.Invite(channel.NameCasefolded())
}
for _, member := range channel.Members() { for _, member := range channel.Members() {
if member == inviter || member == invitee || !channel.ClientIsAtLeast(member, modes.Halfop) { if member == inviter || member == invitee || !channel.ClientIsAtLeast(member, modes.Halfop) {

View File

@ -130,11 +130,11 @@ func (cm *ChannelManager) Join(client *Client, name string, key string, isSajoin
} }
channel.EnsureLoaded() channel.EnsureLoaded()
channel.Join(client, key, isSajoin, rb) err = channel.Join(client, key, isSajoin, rb)
cm.maybeCleanup(channel, true) cm.maybeCleanup(channel, true)
return nil return err
} }
func (cm *ChannelManager) maybeCleanup(channel *Channel, afterJoin bool) { func (cm *ChannelManager) maybeCleanup(channel *Channel, afterJoin bool) {

View File

@ -62,7 +62,7 @@ type Client struct {
exitedSnomaskSent bool exitedSnomaskSent bool
modes modes.ModeSet modes modes.ModeSet
hostname string hostname string
invitedTo map[string]bool invitedTo StringSet
isSTSOnly bool isSTSOnly bool
languages []string languages []string
lastActive time.Time // last time they sent a command that wasn't PONG or similar lastActive time.Time // last time they sent a command that wasn't PONG or similar
@ -1595,15 +1595,24 @@ func (session *Session) Notice(text string) {
// `simulated` is for the fake join of an always-on client // `simulated` is for the fake join of an always-on client
// (we just read the channel name from the database, there's no need to write it back) // (we just read the channel name from the database, there's no need to write it back)
func (client *Client) addChannel(channel *Channel, simulated bool) { func (client *Client) addChannel(channel *Channel, simulated bool) (err error) {
config := client.server.Config()
client.stateMutex.Lock() client.stateMutex.Lock()
client.channels[channel] = true
alwaysOn := client.alwaysOn alwaysOn := client.alwaysOn
if client.destroyed {
err = errClientDestroyed
} else if client.oper == nil && len(client.channels) >= config.Channels.MaxChannelsPerClient {
err = errTooManyChannels
} else {
client.channels[channel] = empty{} // success
}
client.stateMutex.Unlock() client.stateMutex.Unlock()
if alwaysOn && !simulated { if err == nil && alwaysOn && !simulated {
client.markDirty(IncludeChannels) client.markDirty(IncludeChannels)
} }
return
} }
func (client *Client) removeChannel(channel *Channel) { func (client *Client) removeChannel(channel *Channel) {
@ -1623,10 +1632,10 @@ func (client *Client) Invite(casefoldedChannel string) {
defer client.stateMutex.Unlock() defer client.stateMutex.Unlock()
if client.invitedTo == nil { if client.invitedTo == nil {
client.invitedTo = make(map[string]bool) client.invitedTo = make(StringSet)
} }
client.invitedTo[casefoldedChannel] = true client.invitedTo.Add(casefoldedChannel)
} }
// Checks that the client was invited to join a given channel // Checks that the client was invited to join a given channel
@ -1634,7 +1643,7 @@ func (client *Client) CheckInvited(casefoldedChannel string) (invited bool) {
client.stateMutex.Lock() client.stateMutex.Lock()
defer client.stateMutex.Unlock() defer client.stateMutex.Unlock()
invited = client.invitedTo[casefoldedChannel] invited = client.invitedTo.Has(casefoldedChannel)
// joining an invited channel "uses up" your invite, so you can't rejoin on kick // joining an invited channel "uses up" your invite, so you can't rejoin on kick
delete(client.invitedTo, casefoldedChannel) delete(client.invitedTo, casefoldedChannel)
return return

View File

@ -67,6 +67,11 @@ var (
errInvalidMultilineBatch = errors.New("Invalid multiline batch") errInvalidMultilineBatch = errors.New("Invalid multiline batch")
errTimedOut = errors.New("Operation timed out") errTimedOut = errors.New("Operation timed out")
errInvalidUtf8 = errors.New("Message rejected for invalid utf8") errInvalidUtf8 = errors.New("Message rejected for invalid utf8")
errClientDestroyed = errors.New("Client was already destroyed")
errTooManyChannels = errors.New("You have joined too many channels")
errWrongChannelKey = errors.New("Cannot join password-protected channel without the password")
errInviteOnly = errors.New("Cannot join invite-only channel without an invite")
errRegisteredOnly = errors.New("Cannot join registered-only channel without an account")
) )
// Socket Errors // Socket Errors

View File

@ -1142,16 +1142,10 @@ func joinHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
keys = strings.Split(msg.Params[1], ",") keys = strings.Split(msg.Params[1], ",")
} }
config := server.Config()
oper := client.Oper()
for i, name := range channels { for i, name := range channels {
if name == "" { if name == "" {
continue // #679 continue // #679
} }
if config.Channels.MaxChannelsPerClient <= client.NumChannels() && oper == nil {
rb.Add(nil, server.name, ERR_TOOMANYCHANNELS, client.Nick(), name, client.t("You have joined too many channels"))
return false
}
var key string var key string
if len(keys) > i { if len(keys) > i {
key = keys[i] key = keys[i]
@ -1165,18 +1159,35 @@ func joinHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
} }
func sendJoinError(client *Client, name string, rb *ResponseBuffer, err error) { func sendJoinError(client *Client, name string, rb *ResponseBuffer, err error) {
var errMsg string var code, errMsg, forbiddingMode string
switch err { switch err {
case errInsufficientPrivs: case errInsufficientPrivs:
errMsg = `Only server operators can create new channels` code, errMsg = ERR_NOSUCHCHANNEL, `Only server operators can create new channels`
case errConfusableIdentifier: case errConfusableIdentifier:
errMsg = `That channel name is too close to the name of another channel` code, errMsg = ERR_NOSUCHCHANNEL, `That channel name is too close to the name of another channel`
case errChannelPurged: case errChannelPurged:
errMsg = err.Error() code, errMsg = ERR_NOSUCHCHANNEL, err.Error()
case errTooManyChannels:
code, errMsg = ERR_TOOMANYCHANNELS, `You have joined too many channels`
case errLimitExceeded:
code, forbiddingMode = ERR_CHANNELISFULL, "l"
case errWrongChannelKey:
code, forbiddingMode = ERR_BADCHANNELKEY, "k"
case errInviteOnly:
code, forbiddingMode = ERR_INVITEONLYCHAN, "i"
case errBanned:
code, forbiddingMode = ERR_BANNEDFROMCHAN, "b"
case errRegisteredOnly:
code, errMsg = ERR_NEEDREGGEDNICK, `You must be registered to join that channel`
default: default:
errMsg = `No such channel` code, errMsg = ERR_NOSUCHCHANNEL, `No such channel`
} }
rb.Add(nil, client.server.name, ERR_NOSUCHCHANNEL, client.Nick(), utils.SafeErrorParam(name), client.t(errMsg)) if forbiddingMode != "" {
errMsg = fmt.Sprintf(client.t("Cannot join channel (+%s)"), forbiddingMode)
} else {
errMsg = client.t(errMsg)
}
rb.Add(nil, client.server.name, code, client.Nick(), utils.SafeErrorParam(name), errMsg)
} }
// SAJOIN [nick] #channel{,#channel} // SAJOIN [nick] #channel{,#channel}

View File

@ -69,4 +69,4 @@ func (members MemberSet) AnyHasMode(mode modes.Mode) bool {
} }
// ChannelSet is a set of channels. // ChannelSet is a set of channels.
type ChannelSet map[*Channel]bool type ChannelSet map[*Channel]empty