diff --git a/irc/channel.go b/irc/channel.go index 37511159..519d9e5d 100644 --- a/irc/channel.go +++ b/irc/channel.go @@ -5,8 +5,8 @@ import ( ) type Channel struct { - banList []UserMask flags ChannelModeSet + lists map[ChannelMode][]UserMask key string members MemberSet name string @@ -23,8 +23,12 @@ func IsChannel(target string) bool { // string, which must be unique on the server. func NewChannel(s *Server, name string) *Channel { channel := &Channel{ - banList: make([]UserMask, 0), - flags: make(ChannelModeSet), + flags: make(ChannelModeSet), + lists: map[ChannelMode][]UserMask{ + BanMask: []UserMask{}, + ExceptMask: []UserMask{}, + InviteMask: []UserMask{}, + }, members: make(MemberSet), name: name, server: s, @@ -228,31 +232,72 @@ func (channel *Channel) PrivMsg(client *Client, message string) { } } +func (channel *Channel) applyModeFlag(client *Client, mode ChannelMode, + op ModeOp) bool { + if !channel.ClientIsOperator(client) { + client.ErrChanOPrivIsNeeded(channel) + return false + } + + switch op { + case Add: + channel.flags[mode] = true + return true + + case Remove: + delete(channel.flags, mode) + return true + } + return false +} + +func (channel *Channel) applyModeMember(client *Client, mode ChannelMode, + op ModeOp, nick string) bool { + if !channel.ClientIsOperator(client) { + client.ErrChanOPrivIsNeeded(channel) + return false + } + + if nick == "" { + client.ErrNeedMoreParams("MODE") + return false + } + + target := channel.server.clients.Get(nick) + if target == nil { + client.ErrNoSuchNick(nick) + return false + } + + if !channel.members.Has(target) { + client.ErrUserNotInChannel(channel, target) + return false + } + + switch op { + case Add: + channel.members[target][mode] = true + return true + + case Remove: + channel.members[target][mode] = false + return true + } + return false +} + func (channel *Channel) applyMode(client *Client, change ChannelModeChange) bool { switch change.mode { - case BanMask: + case BanMask, ExceptMask, InviteMask: // TODO add/remove - for _, banMask := range channel.banList { - client.RplBanList(channel, banMask) + for _, mask := range channel.lists[change.mode] { + client.RplMaskList(change.mode, channel, mask) } - client.RplEndOfBanList(channel) + client.RplEndOfMaskList(change.mode, 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 - } + return channel.applyModeFlag(client, change.mode, change.op) case Key: if !channel.ClientIsOperator(client) { @@ -289,36 +334,7 @@ func (channel *Channel) applyMode(client *Client, change ChannelModeChange) bool 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 - } + return channel.applyModeMember(client, change.mode, change.op, change.arg) default: client.ErrUnknownMode(change.mode, channel) diff --git a/irc/commands.go b/irc/commands.go index 20b47436..62508052 100644 --- a/irc/commands.go +++ b/irc/commands.go @@ -566,7 +566,7 @@ func NewChannelModeCommand(args []string) (editableCommand, error) { op: op, } switch change.mode { - case Key, BanMask, ExceptionMask, InviteMask, UserLimit, + case Key, BanMask, ExceptMask, InviteMask, UserLimit, ChannelOperator, ChannelCreator, Voice: if len(args) > skipArgs { change.arg = args[skipArgs] diff --git a/irc/constants.go b/irc/constants.go index 921a6920..3bdb2a0e 100644 --- a/irc/constants.go +++ b/irc/constants.go @@ -212,7 +212,7 @@ const ( BanMask ChannelMode = 'b' // arg ChannelCreator ChannelMode = 'O' // flag ChannelOperator ChannelMode = 'o' // arg - ExceptionMask ChannelMode = 'e' // arg + ExceptMask ChannelMode = 'e' // arg InviteMask ChannelMode = 'I' // arg InviteOnly ChannelMode = 'i' // flag Key ChannelMode = 'k' // flag arg diff --git a/irc/reply.go b/irc/reply.go index 18af3434..01168e2f 100644 --- a/irc/reply.go +++ b/irc/reply.go @@ -253,14 +253,60 @@ func (target *Client) RplEndOfWho(name string) { "%s :End of WHO list", name) } -func (target *Client) RplBanList(channel *Channel, ban UserMask) { +func (target *Client) RplMaskList(mode ChannelMode, channel *Channel, mask UserMask) { + switch mode { + case BanMask: + target.RplBanList(channel, mask) + + case ExceptMask: + target.RplExceptList(channel, mask) + + case InviteMask: + target.RplInviteList(channel, mask) + } +} + +func (target *Client) RplEndOfMaskList(mode ChannelMode, channel *Channel) { + switch mode { + case BanMask: + target.RplEndOfBanList(channel) + + case ExceptMask: + target.RplEndOfExceptList(channel) + + case InviteMask: + target.RplEndOfInviteList(channel) + } +} + +func (target *Client) RplBanList(channel *Channel, mask UserMask) { target.NumericReply(RPL_BANLIST, - "%s %s", channel.name, ban) + "%s %s", channel, mask) } func (target *Client) RplEndOfBanList(channel *Channel) { target.NumericReply(RPL_ENDOFBANLIST, - "%s :End of channel ban list", channel.name) + "%s :End of channel ban list", channel) +} + +func (target *Client) RplExceptList(channel *Channel, mask UserMask) { + target.NumericReply(RPL_EXCEPTLIST, + "%s %s", channel, mask) +} + +func (target *Client) RplEndOfExceptList(channel *Channel) { + target.NumericReply(RPL_ENDOFEXCEPTLIST, + "%s :End of channel exception list", channel) +} + +func (target *Client) RplInviteList(channel *Channel, mask UserMask) { + target.NumericReply(RPL_INVITELIST, + "%s %s", channel, mask) +} + +func (target *Client) RplEndOfInviteList(channel *Channel) { + target.NumericReply(RPL_ENDOFINVITELIST, + "%s :End of channel invite list", channel) } func (target *Client) RplNowAway() {