mirror of
https://github.com/ergochat/ergo.git
synced 2024-12-22 18:52:41 +01:00
flesh out channel modes
- deprecate 's' mode - add user limit - don't leak key in mode messages to non-members - begin refactoring Mode()
This commit is contained in:
parent
21337cda7f
commit
c7298c55b9
268
irc/channel.go
268
irc/channel.go
@ -1,13 +1,18 @@
|
|||||||
package irc
|
package irc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
type Channel struct {
|
type Channel struct {
|
||||||
banList []UserMask
|
banList []UserMask
|
||||||
flags ChannelModeSet
|
flags ChannelModeSet
|
||||||
key string
|
key string
|
||||||
members MemberSet
|
members MemberSet
|
||||||
name string
|
name string
|
||||||
server *Server
|
server *Server
|
||||||
topic string
|
topic string
|
||||||
|
userLimit uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsChannel(target string) bool {
|
func IsChannel(target string) bool {
|
||||||
@ -71,11 +76,20 @@ func (channel *Channel) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// <mode> <mode params>
|
// <mode> <mode params>
|
||||||
func (channel *Channel) ModeString() (str string) {
|
func (channel *Channel) ModeString(client *Client) (str string) {
|
||||||
if channel.key != "" {
|
isMember := client.flags[Operator] || channel.members.Has(client)
|
||||||
|
showKey := isMember && (channel.key != "")
|
||||||
|
showUserLimit := channel.userLimit > 0
|
||||||
|
|
||||||
|
// flags with args
|
||||||
|
if showKey {
|
||||||
str += Key.String()
|
str += Key.String()
|
||||||
}
|
}
|
||||||
|
if showUserLimit {
|
||||||
|
str += UserLimit.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// flags
|
||||||
for mode := range channel.flags {
|
for mode := range channel.flags {
|
||||||
str += mode.String()
|
str += mode.String()
|
||||||
}
|
}
|
||||||
@ -84,21 +98,40 @@ func (channel *Channel) ModeString() (str string) {
|
|||||||
str = "+" + str
|
str = "+" + str
|
||||||
}
|
}
|
||||||
|
|
||||||
if channel.key != "" {
|
// args for flags with args: The order must match above to keep
|
||||||
|
// positional arguments in place.
|
||||||
|
if showKey {
|
||||||
str += " " + channel.key
|
str += " " + channel.key
|
||||||
}
|
}
|
||||||
|
if showUserLimit {
|
||||||
|
str += " " + strconv.FormatUint(channel.userLimit, 10)
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (channel *Channel) IsFull() bool {
|
||||||
|
return (channel.userLimit > 0) &&
|
||||||
|
(uint64(len(channel.members)) >= channel.userLimit)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (channel *Channel) CheckKey(key string) bool {
|
||||||
|
return (channel.key == "") || (channel.key == key)
|
||||||
|
}
|
||||||
|
|
||||||
func (channel *Channel) Join(client *Client, key string) {
|
func (channel *Channel) Join(client *Client, key string) {
|
||||||
if (channel.key != "") && (channel.key != key) {
|
if channel.members.Has(client) {
|
||||||
client.ErrBadChannelKey(channel)
|
// already joined, no message?
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if channel.members[client] != nil {
|
if channel.IsFull() {
|
||||||
// already joined, no message?
|
client.ErrChannelIsFull(channel)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !channel.CheckKey(key) {
|
||||||
|
client.ErrBadChannelKey(channel)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,7 +183,7 @@ func (channel *Channel) GetTopic(client *Client) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (channel *Channel) SetTopic(client *Client, topic string) {
|
func (channel *Channel) SetTopic(client *Client, topic string) {
|
||||||
if !channel.members.Has(client) {
|
if !(client.flags[Operator] || channel.members.Has(client)) {
|
||||||
client.ErrNotOnChannel(channel)
|
client.ErrNotOnChannel(channel)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -168,8 +201,22 @@ func (channel *Channel) SetTopic(client *Client, topic string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (channel *Channel) PrivMsg(client *Client, message string) {
|
func (channel *Channel) CanSpeak(client *Client) bool {
|
||||||
|
if client.flags[Operator] {
|
||||||
|
return true
|
||||||
|
}
|
||||||
if channel.flags[NoOutside] && !channel.members.Has(client) {
|
if channel.flags[NoOutside] && !channel.members.Has(client) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if channel.flags[Moderated] && !(channel.members.HasMode(client, Voice) ||
|
||||||
|
channel.members.HasMode(client, ChannelOperator)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (channel *Channel) PrivMsg(client *Client, message string) {
|
||||||
|
if !channel.CanSpeak(client) {
|
||||||
client.ErrCannotSendToChan(channel)
|
client.ErrCannotSendToChan(channel)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -181,6 +228,105 @@ func (channel *Channel) PrivMsg(client *Client, message string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (channel *Channel) applyMode(client *Client, change ChannelModeChange) bool {
|
||||||
|
switch change.mode {
|
||||||
|
case BanMask:
|
||||||
|
// TODO add/remove
|
||||||
|
|
||||||
|
for _, banMask := range channel.banList {
|
||||||
|
client.RplBanList(channel, banMask)
|
||||||
|
}
|
||||||
|
client.RplEndOfBanList(channel)
|
||||||
|
|
||||||
|
case Moderated, NoOutside, OpOnlyTopic, Private:
|
||||||
|
if !channel.ClientIsOperator(client) {
|
||||||
|
client.ErrChanOPrivIsNeeded(channel)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
switch change.op {
|
||||||
|
case Add:
|
||||||
|
channel.flags[change.mode] = true
|
||||||
|
return true
|
||||||
|
|
||||||
|
case Remove:
|
||||||
|
delete(channel.flags, change.mode)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
case Key:
|
||||||
|
if !channel.ClientIsOperator(client) {
|
||||||
|
client.ErrChanOPrivIsNeeded(channel)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
switch change.op {
|
||||||
|
case Add:
|
||||||
|
if change.arg == "" {
|
||||||
|
client.ErrNeedMoreParams("MODE")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
channel.key = change.arg
|
||||||
|
return true
|
||||||
|
|
||||||
|
case Remove:
|
||||||
|
channel.key = ""
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
case UserLimit:
|
||||||
|
limit, err := strconv.ParseUint(change.arg, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
client.ErrNeedMoreParams("MODE")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if limit == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
channel.userLimit = limit
|
||||||
|
return true
|
||||||
|
|
||||||
|
case ChannelOperator, Voice:
|
||||||
|
if !channel.ClientIsOperator(client) {
|
||||||
|
client.ErrChanOPrivIsNeeded(channel)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if change.arg == "" {
|
||||||
|
client.ErrNeedMoreParams("MODE")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
target := channel.server.clients.Get(change.arg)
|
||||||
|
if target == nil {
|
||||||
|
client.ErrNoSuchNick(change.arg)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !channel.members.Has(target) {
|
||||||
|
client.ErrUserNotInChannel(channel, target)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
switch change.op {
|
||||||
|
case Add:
|
||||||
|
channel.members[target][change.mode] = true
|
||||||
|
return true
|
||||||
|
|
||||||
|
case Remove:
|
||||||
|
channel.members[target][change.mode] = false
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
client.ErrUnknownMode(change.mode, channel)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (channel *Channel) Mode(client *Client, changes ChannelModeChanges) {
|
func (channel *Channel) Mode(client *Client, changes ChannelModeChanges) {
|
||||||
if len(changes) == 0 {
|
if len(changes) == 0 {
|
||||||
client.RplChannelModeIs(channel)
|
client.RplChannelModeIs(channel)
|
||||||
@ -188,100 +334,22 @@ func (channel *Channel) Mode(client *Client, changes ChannelModeChanges) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
applied := make(ChannelModeChanges, 0)
|
applied := make(ChannelModeChanges, 0)
|
||||||
|
|
||||||
for _, change := range changes {
|
for _, change := range changes {
|
||||||
switch change.mode {
|
if channel.applyMode(client, change) {
|
||||||
case BanMask:
|
applied = append(applied, change)
|
||||||
// TODO add/remove
|
|
||||||
|
|
||||||
for _, banMask := range channel.banList {
|
|
||||||
client.RplBanList(channel, banMask)
|
|
||||||
}
|
|
||||||
client.RplEndOfBanList(channel)
|
|
||||||
|
|
||||||
case NoOutside, Private, Secret, OpOnlyTopic:
|
|
||||||
if !channel.ClientIsOperator(client) {
|
|
||||||
client.ErrChanOPrivIsNeeded(channel)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
switch change.op {
|
|
||||||
case Add:
|
|
||||||
channel.flags[change.mode] = true
|
|
||||||
applied = append(applied, change)
|
|
||||||
|
|
||||||
case Remove:
|
|
||||||
delete(channel.flags, change.mode)
|
|
||||||
applied = append(applied, change)
|
|
||||||
}
|
|
||||||
|
|
||||||
case Key:
|
|
||||||
if !channel.ClientIsOperator(client) {
|
|
||||||
client.ErrChanOPrivIsNeeded(channel)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
switch change.op {
|
|
||||||
case Add:
|
|
||||||
if change.arg == "" {
|
|
||||||
client.ErrNeedMoreParams("MODE")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
channel.key = change.arg
|
|
||||||
applied = append(applied, change)
|
|
||||||
|
|
||||||
case Remove:
|
|
||||||
channel.key = ""
|
|
||||||
applied = append(applied, change)
|
|
||||||
}
|
|
||||||
|
|
||||||
case ChannelOperator, Voice:
|
|
||||||
if !channel.ClientIsOperator(client) {
|
|
||||||
client.ErrChanOPrivIsNeeded(channel)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if change.arg == "" {
|
|
||||||
client.ErrNeedMoreParams("MODE")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
target := channel.server.clients.Get(change.arg)
|
|
||||||
if target == nil {
|
|
||||||
client.ErrNoSuchNick(change.arg)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if !channel.members.Has(target) {
|
|
||||||
client.ErrUserNotInChannel(channel, target)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
switch change.op {
|
|
||||||
case Add:
|
|
||||||
channel.members[target][change.mode] = true
|
|
||||||
applied = append(applied, change)
|
|
||||||
|
|
||||||
case Remove:
|
|
||||||
channel.members[target][change.mode] = false
|
|
||||||
applied = append(applied, change)
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
client.ErrUnknownMode(change.mode, channel)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(applied) > 0 {
|
if len(applied) > 0 {
|
||||||
|
reply := RplChannelMode(client, channel, applied)
|
||||||
for member := range channel.members {
|
for member := range channel.members {
|
||||||
member.Reply(RplChannelMode(client, channel, applied))
|
member.Reply(reply)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (channel *Channel) Notice(client *Client, message string) {
|
func (channel *Channel) Notice(client *Client, message string) {
|
||||||
if channel.flags[NoOutside] && !channel.members.Has(client) {
|
if !channel.CanSpeak(client) {
|
||||||
client.ErrCannotSendToChan(channel)
|
client.ErrCannotSendToChan(channel)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -299,7 +367,7 @@ func (channel *Channel) Quit(client *Client) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (channel *Channel) Kick(client *Client, target *Client, comment string) {
|
func (channel *Channel) Kick(client *Client, target *Client, comment string) {
|
||||||
if !client.flags[Operator] && !channel.members.Has(client) {
|
if !(client.flags[Operator] || channel.members.Has(client)) {
|
||||||
client.ErrNotOnChannel(channel)
|
client.ErrNotOnChannel(channel)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -222,7 +222,7 @@ const (
|
|||||||
Private ChannelMode = 'p' // flag
|
Private ChannelMode = 'p' // flag
|
||||||
Quiet ChannelMode = 'q' // flag
|
Quiet ChannelMode = 'q' // flag
|
||||||
ReOp ChannelMode = 'r' // flag
|
ReOp ChannelMode = 'r' // flag
|
||||||
Secret ChannelMode = 's' // flag
|
Secret ChannelMode = 's' // flag, deprecated
|
||||||
UserLimit ChannelMode = 'l' // flag arg
|
UserLimit ChannelMode = 'l' // flag arg
|
||||||
Voice ChannelMode = 'v' // arg
|
Voice ChannelMode = 'v' // arg
|
||||||
)
|
)
|
||||||
|
@ -215,7 +215,7 @@ func (target *Client) RplEndOfWhois() {
|
|||||||
|
|
||||||
func (target *Client) RplChannelModeIs(channel *Channel) {
|
func (target *Client) RplChannelModeIs(channel *Channel) {
|
||||||
target.NumericReply(RPL_CHANNELMODEIS,
|
target.NumericReply(RPL_CHANNELMODEIS,
|
||||||
"%s %s", channel, channel.ModeString())
|
"%s %s", channel, channel.ModeString(target))
|
||||||
}
|
}
|
||||||
|
|
||||||
// <channel> <user> <host> <server> <nick> ( "H" / "G" ) ["*"] [ ( "@" / "+" ) ]
|
// <channel> <user> <host> <server> <nick> ( "H" / "G" ) ["*"] [ ( "@" / "+" ) ]
|
||||||
@ -421,3 +421,8 @@ func (target *Client) ErrUnknownMode(mode ChannelMode, channel *Channel) {
|
|||||||
target.NumericReply(ERR_UNKNOWNMODE,
|
target.NumericReply(ERR_UNKNOWNMODE,
|
||||||
"%s :is unknown mode char to me for %s", mode, channel)
|
"%s :is unknown mode char to me for %s", mode, channel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (target *Client) ErrChannelIsFull(channel *Channel) {
|
||||||
|
target.NumericReply(ERR_CHANNELISFULL,
|
||||||
|
"%s :Cannot join channel (+l)", channel)
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user