3
0
mirror of https://github.com/ergochat/ergo.git synced 2024-11-22 20:09:41 +01:00

mask lists (ban, except, invite)

This commit is contained in:
Jeremy Latt 2014-03-07 17:09:49 -08:00
parent 5d46e7d7fa
commit d4093e7f8b
4 changed files with 120 additions and 15 deletions

View File

@ -8,7 +8,7 @@ import (
type Channel struct { type Channel struct {
flags ChannelModeSet flags ChannelModeSet
lists map[ChannelMode]UserMaskSet lists map[ChannelMode]*UserMaskSet
key string key string
members MemberSet members MemberSet
name string name string
@ -26,10 +26,10 @@ func IsChannel(target string) bool {
func NewChannel(s *Server, name string) *Channel { func NewChannel(s *Server, name string) *Channel {
channel := &Channel{ channel := &Channel{
flags: make(ChannelModeSet), flags: make(ChannelModeSet),
lists: map[ChannelMode]UserMaskSet{ lists: map[ChannelMode]*UserMaskSet{
BanMask: make(UserMaskSet), BanMask: NewUserMaskSet(),
ExceptMask: make(UserMaskSet), ExceptMask: NewUserMaskSet(),
InviteMask: make(UserMaskSet), InviteMask: NewUserMaskSet(),
}, },
members: make(MemberSet), members: make(MemberSet),
name: strings.ToLower(name), name: strings.ToLower(name),
@ -151,6 +151,19 @@ func (channel *Channel) Join(client *Client, key string) {
return return
} }
isInvited := channel.lists[InviteMask].Match(client.UserHost())
if channel.flags[InviteOnly] && !isInvited {
client.ErrInviteOnlyChan(channel)
return
}
if channel.lists[BanMask].Match(client.UserHost()) &&
!isInvited &&
!channel.lists[ExceptMask].Match(client.UserHost()) {
client.ErrBannedFromChan(channel)
return
}
client.channels.Add(channel) client.channels.Add(channel)
channel.members.Add(client) channel.members.Add(client)
if !channel.flags[Persistent] && (len(channel.members) == 1) { if !channel.flags[Persistent] && (len(channel.members) == 1) {
@ -213,7 +226,7 @@ func (channel *Channel) SetTopic(client *Client, topic string) {
} }
if err := channel.Persist(); err != nil { if err := channel.Persist(); err != nil {
log.Println(err) log.Println("Channel.Persist:", channel, err)
} }
} }
@ -310,15 +323,35 @@ func (channel *Channel) applyModeMember(client *Client, mode ChannelMode,
return false return false
} }
func (channel *Channel) applyModeMask(client *Client, mode ChannelMode, op ModeOp, mask string) bool {
if !channel.ClientIsOperator(client) {
client.ErrChanOPrivIsNeeded(channel)
return false
}
list := channel.lists[mode]
if list == nil {
// This should never happen, but better safe than panicky.
return false
}
if op == Add {
list.Add(mask)
} else if op == Remove {
list.Remove(mask)
}
for lmask := range channel.lists[mode].masks {
client.RplMaskList(mode, channel, lmask)
}
client.RplEndOfMaskList(mode, channel)
return true
}
func (channel *Channel) applyMode(client *Client, change *ChannelModeChange) bool { func (channel *Channel) applyMode(client *Client, change *ChannelModeChange) bool {
switch change.mode { switch change.mode {
case BanMask, ExceptMask, InviteMask: case BanMask, ExceptMask, InviteMask:
// TODO add/remove return channel.applyModeMask(client, change.mode, change.op, change.arg)
for mask := range channel.lists[change.mode] {
client.RplMaskList(change.mode, channel, mask)
}
client.RplEndOfMaskList(change.mode, channel)
case Moderated, NoOutside, OpOnlyTopic, Persistent, Private: case Moderated, NoOutside, OpOnlyTopic, Persistent, Private:
return channel.applyModeFlag(client, change.mode, change.op) return channel.applyModeFlag(client, change.mode, change.op)
@ -390,7 +423,7 @@ func (channel *Channel) Mode(client *Client, changes ChannelModeChanges) {
} }
if err := channel.Persist(); err != nil { if err := channel.Persist(); err != nil {
log.Println(err) log.Println("Channel.Persist:", channel, err)
} }
} }
} }
@ -464,6 +497,13 @@ func (channel *Channel) Invite(invitee *Client, inviter *Client) {
return return
} }
if channel.flags[InviteOnly] {
channel.lists[InviteMask].Add(invitee.UserHost())
if err := channel.Persist(); err != nil {
log.Println("Channel.Persist:", channel, err)
}
}
inviter.RplInviting(invitee, channel.name) inviter.RplInviting(invitee, channel.name)
invitee.Reply(RplInviteMsg(inviter, invitee, channel.name)) invitee.Reply(RplInviteMsg(inviter, invitee, channel.name))
if invitee.flags[Away] { if invitee.flags[Away] {

View File

@ -178,3 +178,60 @@ func (db *ClientDB) Remove(client *Client) {
} }
} }
} }
//
// usermask to regexp
//
type UserMaskSet struct {
masks map[string]bool
regexp *regexp.Regexp
}
func NewUserMaskSet() *UserMaskSet {
return &UserMaskSet{
masks: make(map[string]bool),
}
}
func (set *UserMaskSet) Add(mask string) {
set.masks[mask] = true
set.setRegexp()
}
func (set *UserMaskSet) Remove(mask string) {
delete(set.masks, mask)
set.setRegexp()
}
func (set *UserMaskSet) Match(userhost string) bool {
if set.regexp == nil {
return false
}
return set.regexp.MatchString(userhost)
}
func (set *UserMaskSet) setRegexp() {
if len(set.masks) == 0 {
set.regexp = nil
return
}
maskExprs := make([]string, len(set.masks))
index := 0
for mask := range set.masks {
manyParts := strings.Split(mask, "*")
manyExprs := make([]string, len(manyParts))
for mindex, manyPart := range manyParts {
oneParts := strings.Split(manyPart, "?")
oneExprs := make([]string, len(oneParts))
for oindex, onePart := range oneParts {
oneExprs[oindex] = regexp.QuoteMeta(onePart)
}
manyExprs[mindex] = strings.Join(oneExprs, ".")
}
maskExprs[index] = strings.Join(manyExprs, ".*")
}
expr := "^" + strings.Join(maskExprs, "|") + "$"
set.regexp, _ = regexp.Compile(expr)
}

View File

@ -541,3 +541,13 @@ func (target *Client) ErrInvalidCapCmd(subCommand CapSubCommand) {
target.NumericReply(ERR_INVALIDCAPCMD, target.NumericReply(ERR_INVALIDCAPCMD,
"%s :Invalid CAP subcommand", subCommand) "%s :Invalid CAP subcommand", subCommand)
} }
func (target *Client) ErrBannedFromChan(channel *Channel) {
target.NumericReply(ERR_BANNEDFROMCHAN,
"%s :Cannot join channel (+b)", channel)
}
func (target *Client) ErrInviteOnlyChan(channel *Channel) {
target.NumericReply(ERR_INVITEONLYCHAN,
"%s :Cannot join channel (+i)", channel)
}

View File

@ -9,8 +9,6 @@ import (
// simple types // simple types
// //
type UserMaskSet map[string]bool
type CapSubCommand string type CapSubCommand string
type Capability string type Capability string