mirror of
https://github.com/ergochat/ergo.git
synced 2024-12-22 18:52:41 +01:00
commit
09b74aaa37
@ -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) {
|
||||||
|
@ -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) {
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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}
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user