implement SAJOIN, allow channel founders to join unconditionally

This commit is contained in:
Shivaram Lingamneni 2018-05-25 02:46:36 -04:00
parent 13aba9e64c
commit 495705f538
6 changed files with 60 additions and 12 deletions

View File

@ -350,32 +350,36 @@ func (channel *Channel) IsEmpty() bool {
}
// Join joins the given client to this channel (if they can be joined).
//TODO(dan): /SAJOIN and maybe a ForceJoin function?
func (channel *Channel) Join(client *Client, key string, rb *ResponseBuffer) {
func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *ResponseBuffer) {
if channel.hasClient(client) {
// already joined, no message needs to be sent
return
}
chname := channel.Name()
channel.stateMutex.RLock()
chname := channel.name
founder := channel.registeredFounder
channel.stateMutex.RUnlock()
account := client.Account()
hasPrivs := isSajoin || (founder != "" && founder == account)
if channel.IsFull() {
if !hasPrivs && channel.IsFull() {
rb.Add(nil, client.server.name, ERR_CHANNELISFULL, chname, fmt.Sprintf(client.t("Cannot join channel (+%s)"), "l"))
return
}
if !channel.CheckKey(key) {
if !hasPrivs && !channel.CheckKey(key) {
rb.Add(nil, client.server.name, ERR_BADCHANNELKEY, chname, fmt.Sprintf(client.t("Cannot join channel (+%s)"), "k"))
return
}
isInvited := channel.lists[modes.InviteMask].Match(client.nickMaskCasefolded)
if channel.flags.HasMode(modes.InviteOnly) && !isInvited {
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 channel.lists[modes.BanMask].Match(client.nickMaskCasefolded) &&
if !hasPrivs && channel.lists[modes.BanMask].Match(client.nickMaskCasefolded) &&
!isInvited &&
!channel.lists[modes.ExceptMask].Match(client.nickMaskCasefolded) {
rb.Add(nil, client.server.name, ERR_BANNEDFROMCHAN, chname, fmt.Sprintf(client.t("Cannot join channel (+%s)"), "b"))
@ -389,7 +393,6 @@ func (channel *Channel) Join(client *Client, key string, rb *ResponseBuffer) {
defer channel.joinPartMutex.Unlock()
func() {
account := client.Account()
channel.stateMutex.Lock()
defer channel.stateMutex.Unlock()
@ -779,7 +782,7 @@ func (channel *Channel) Kick(client *Client, target *Client, comment string, rb
return
}
if !channel.ClientHasPrivsOver(client, target) {
rb.Add(nil, client.server.name, ERR_CHANOPRIVSNEEDED, channel.name, client.t("You're not a channel operator"))
rb.Add(nil, client.server.name, ERR_CHANOPRIVSNEEDED, channel.name, client.t("You don't have enough channel privileges"))
return
}

View File

@ -45,7 +45,7 @@ func (cm *ChannelManager) Get(name string) *Channel {
}
// Join causes `client` to join the channel named `name`, creating it if necessary.
func (cm *ChannelManager) Join(client *Client, name string, key string, rb *ResponseBuffer) error {
func (cm *ChannelManager) Join(client *Client, name string, key string, isSajoin bool, rb *ResponseBuffer) error {
server := client.server
casefoldedName, err := CasefoldChannel(name)
if err != nil || len(casefoldedName) > server.Limits().ChannelLen {
@ -74,7 +74,7 @@ func (cm *ChannelManager) Join(client *Client, name string, key string, rb *Resp
entry.pendingJoins += 1
cm.Unlock()
entry.channel.Join(client, key, rb)
entry.channel.Join(client, key, isSajoin, rb)
cm.maybeCleanup(entry.channel, true)

View File

@ -229,6 +229,11 @@ func init() {
usablePreReg: true,
minParams: 1,
},
"SAJOIN": {
handler: sajoinHandler,
minParams: 1,
capabs: []string{"sajoin"},
},
"SANICK": {
handler: sanickHandler,
minParams: 2,

View File

@ -889,7 +889,7 @@ func joinHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
if len(keys) > i {
key = keys[i]
}
err := server.channels.Join(client, name, key, rb)
err := server.channels.Join(client, name, key, false, rb)
if err == errNoSuchChannel {
rb.Add(nil, server.name, ERR_NOSUCHCHANNEL, client.Nick(), name, client.t("No such channel"))
}
@ -897,6 +897,38 @@ func joinHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
return false
}
// SAJOIN [nick] #channel{,#channel}
func sajoinHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
var target *Client
var channelString string
if strings.HasPrefix(msg.Params[0], "#") {
target = client
channelString = msg.Params[0]
} else {
if len(msg.Params) == 1 {
rb.Add(nil, server.name, ERR_NEEDMOREPARAMS, client.Nick(), "KICK", client.t("Not enough parameters"))
return false
} else {
target = server.clients.Get(msg.Params[0])
if target == nil {
rb.Add(nil, server.name, ERR_NOSUCHNICK, client.Nick(), msg.Params[0], "No such nick")
return false
}
channelString = msg.Params[1]
rb = NewResponseBuffer(target)
}
}
channels := strings.Split(channelString, ",")
for _, chname := range channels {
server.channels.Join(target, chname, "", true, rb)
}
if client != target {
rb.Send()
}
return false
}
// KICK <channel>{,<channel>} <user>{,<user>} [<comment>]
func kickHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
channels := strings.Split(msg.Params[0], ",")

View File

@ -400,6 +400,13 @@ Renames the given channel with the given reason, if possible.
For example:
RENAME #ircv2 #ircv3 :Protocol upgrades!`,
},
"sajoin": {
oper: true,
text: `SAJOIN [nick] #channel{,#channel}
Forcibly joins a user to a channel, ignoring restrictions like bans, user limits
and channel keys. If [nick] is omitted, it defaults to the operator.`,
},
"sanick": {
oper: true,

View File

@ -281,6 +281,7 @@ oper-classes:
- "oper:rehash"
- "oper:die"
- "unregister"
- "sajoin"
- "samode"
- "vhosts"