mirror of
https://github.com/ergochat/ergo.git
synced 2024-12-22 10:42:52 +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
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type Channel struct {
|
||||
banList []UserMask
|
||||
flags ChannelModeSet
|
||||
key string
|
||||
members MemberSet
|
||||
name string
|
||||
server *Server
|
||||
topic string
|
||||
banList []UserMask
|
||||
flags ChannelModeSet
|
||||
key string
|
||||
members MemberSet
|
||||
name string
|
||||
server *Server
|
||||
topic string
|
||||
userLimit uint64
|
||||
}
|
||||
|
||||
func IsChannel(target string) bool {
|
||||
@ -71,11 +76,20 @@ func (channel *Channel) String() string {
|
||||
}
|
||||
|
||||
// <mode> <mode params>
|
||||
func (channel *Channel) ModeString() (str string) {
|
||||
if channel.key != "" {
|
||||
func (channel *Channel) ModeString(client *Client) (str string) {
|
||||
isMember := client.flags[Operator] || channel.members.Has(client)
|
||||
showKey := isMember && (channel.key != "")
|
||||
showUserLimit := channel.userLimit > 0
|
||||
|
||||
// flags with args
|
||||
if showKey {
|
||||
str += Key.String()
|
||||
}
|
||||
if showUserLimit {
|
||||
str += UserLimit.String()
|
||||
}
|
||||
|
||||
// flags
|
||||
for mode := range channel.flags {
|
||||
str += mode.String()
|
||||
}
|
||||
@ -84,21 +98,40 @@ func (channel *Channel) ModeString() (str string) {
|
||||
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
|
||||
}
|
||||
if showUserLimit {
|
||||
str += " " + strconv.FormatUint(channel.userLimit, 10)
|
||||
}
|
||||
|
||||
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) {
|
||||
if (channel.key != "") && (channel.key != key) {
|
||||
client.ErrBadChannelKey(channel)
|
||||
if channel.members.Has(client) {
|
||||
// already joined, no message?
|
||||
return
|
||||
}
|
||||
|
||||
if channel.members[client] != nil {
|
||||
// already joined, no message?
|
||||
if channel.IsFull() {
|
||||
client.ErrChannelIsFull(channel)
|
||||
return
|
||||
}
|
||||
|
||||
if !channel.CheckKey(key) {
|
||||
client.ErrBadChannelKey(channel)
|
||||
return
|
||||
}
|
||||
|
||||
@ -150,7 +183,7 @@ func (channel *Channel) GetTopic(client *Client) {
|
||||
}
|
||||
|
||||
func (channel *Channel) SetTopic(client *Client, topic string) {
|
||||
if !channel.members.Has(client) {
|
||||
if !(client.flags[Operator] || channel.members.Has(client)) {
|
||||
client.ErrNotOnChannel(channel)
|
||||
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) {
|
||||
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)
|
||||
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) {
|
||||
if len(changes) == 0 {
|
||||
client.RplChannelModeIs(channel)
|
||||
@ -188,100 +334,22 @@ func (channel *Channel) Mode(client *Client, changes ChannelModeChanges) {
|
||||
}
|
||||
|
||||
applied := make(ChannelModeChanges, 0)
|
||||
|
||||
for _, change := range changes {
|
||||
switch change.mode {
|
||||
case BanMask:
|
||||
// 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 channel.applyMode(client, change) {
|
||||
applied = append(applied, change)
|
||||
}
|
||||
}
|
||||
|
||||
if len(applied) > 0 {
|
||||
reply := RplChannelMode(client, channel, applied)
|
||||
for member := range channel.members {
|
||||
member.Reply(RplChannelMode(client, channel, applied))
|
||||
member.Reply(reply)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (channel *Channel) Notice(client *Client, message string) {
|
||||
if channel.flags[NoOutside] && !channel.members.Has(client) {
|
||||
if !channel.CanSpeak(client) {
|
||||
client.ErrCannotSendToChan(channel)
|
||||
return
|
||||
}
|
||||
@ -299,7 +367,7 @@ func (channel *Channel) Quit(client *Client) {
|
||||
}
|
||||
|
||||
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)
|
||||
return
|
||||
}
|
||||
|
@ -222,7 +222,7 @@ const (
|
||||
Private ChannelMode = 'p' // flag
|
||||
Quiet ChannelMode = 'q' // flag
|
||||
ReOp ChannelMode = 'r' // flag
|
||||
Secret ChannelMode = 's' // flag
|
||||
Secret ChannelMode = 's' // flag, deprecated
|
||||
UserLimit ChannelMode = 'l' // flag arg
|
||||
Voice ChannelMode = 'v' // arg
|
||||
)
|
||||
|
@ -215,7 +215,7 @@ func (target *Client) RplEndOfWhois() {
|
||||
|
||||
func (target *Client) RplChannelModeIs(channel *Channel) {
|
||||
target.NumericReply(RPL_CHANNELMODEIS,
|
||||
"%s %s", channel, channel.ModeString())
|
||||
"%s %s", channel, channel.ModeString(target))
|
||||
}
|
||||
|
||||
// <channel> <user> <host> <server> <nick> ( "H" / "G" ) ["*"] [ ( "@" / "+" ) ]
|
||||
@ -421,3 +421,8 @@ func (target *Client) ErrUnknownMode(mode ChannelMode, channel *Channel) {
|
||||
target.NumericReply(ERR_UNKNOWNMODE,
|
||||
"%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