diff --git a/irc/commands.go b/irc/commands.go index 7e479a94..750edd41 100644 --- a/irc/commands.go +++ b/irc/commands.go @@ -5,7 +5,6 @@ import ( "fmt" "strconv" "strings" - "unicode/utf8" ) type editableCommand interface { @@ -374,15 +373,11 @@ func NewTopicCommand(args []string) (editableCommand, error) { type ModeChange struct { mode UserMode - add bool // false => remove + op ModeOp } func (change *ModeChange) String() string { - sig := "+" - if !change.add { - sig = "-" - } - return fmt.Sprintf("%s%s", sig, change.mode) + return fmt.Sprintf("%s%s", change.op, change.mode) } type ModeCommand struct { @@ -391,21 +386,32 @@ type ModeCommand struct { changes []ModeChange } -func (cmd *ModeCommand) String() string { - return fmt.Sprintf("MODE(nickname=%s, changes=%s)", cmd.nickname, cmd.changes) +// MODE *( ( "+" / "-" ) *( "i" / "w" / "o" / "O" / "r" ) ) +func NewUserModeCommand(args []string) (editableCommand, error) { + cmd := &ModeCommand{ + nickname: args[0], + changes: make([]ModeChange, 0), + } + + for _, modeChange := range args[1:] { + op := ModeOp(modeChange[0]) + if (op != Add) && (op != Remove) { + return nil, ErrParseCommand + } + + for _, mode := range modeChange[1:] { + cmd.changes = append(cmd.changes, ModeChange{ + mode: UserMode(mode), + op: op, + }) + } + } + + return cmd, nil } -func stringToRunes(str string) <-chan rune { - runes := make(chan rune) - go func() { - for len(str) > 0 { - rune, size := utf8.DecodeRuneInString(str) - runes <- rune - str = str[size:] - } - close(runes) - }() - return runes +func (cmd *ModeCommand) String() string { + return fmt.Sprintf("MODE(nickname=%s, changes=%s)", cmd.nickname, cmd.changes) } type ChannelModeOp struct { @@ -434,23 +440,22 @@ func NewChannelModeCommand(args []string) (editableCommand, error) { for len(args) > 0 { modeArg := args[0] - op := List - switch modeArg[0] { - case '+': - op = Add - modeArg = modeArg[1:] - case '-': - op = Remove + + op := ModeOp(modeArg[0]) + if (op == Add) || (op == Remove) { modeArg = modeArg[1:] + } else { + op = List } + skipArgs := 1 - for mode := range stringToRunes(modeArg) { + for _, mode := range modeArg { modeOp := ChannelModeOp{ mode: ChannelMode(mode), op: op, } switch modeOp.mode { - case Key, BanMask, ExceptionMask, InviteMask: + case Key, BanMask, ExceptionMask, InviteMask, UserLimit: if len(args) > skipArgs { modeOp.arg = args[skipArgs] skipArgs += 1 @@ -468,35 +473,6 @@ func (msg *ChannelModeCommand) String() string { return fmt.Sprintf("MODE(channel=%s, modeOps=%s)", msg.channel, msg.modeOps) } -// MODE *( ( "+" / "-" ) *( "i" / "w" / "o" / "O" / "r" ) ) -func NewUserModeCommand(args []string) (editableCommand, error) { - cmd := &ModeCommand{ - nickname: args[0], - changes: make([]ModeChange, - utf8.RuneCountInString(strings.Join(args[1:], ""))-len(args[1:])), - } - - index := 0 - for _, arg := range args[1:] { - modeChange := stringToRunes(arg) - sig := <-modeChange - if sig != '+' && sig != '-' { - return nil, ErrParseCommand - } - - add := sig == '+' - for mode := range modeChange { - cmd.changes[index] = ModeChange{ - mode: UserMode(mode), - add: add, - } - index += 1 - } - } - - return cmd, nil -} - func NewModeCommand(args []string) (editableCommand, error) { if len(args) == 0 { return nil, NotEnoughArgsError @@ -543,7 +519,7 @@ func (msg *WhoisCommand) String() string { type WhoCommand struct { BaseCommand - mask string + mask Mask operatorOnly bool } @@ -552,7 +528,7 @@ func NewWhoCommand(args []string) (editableCommand, error) { cmd := &WhoCommand{} if len(args) > 0 { - cmd.mask = args[0] + cmd.mask = Mask(args[0]) } if (len(args) > 1) && (args[1] == "o") { diff --git a/irc/constants.go b/irc/constants.go index e460f29b..8362997e 100644 --- a/irc/constants.go +++ b/irc/constants.go @@ -158,32 +158,32 @@ const ( RPL_PRIVMSG = "PRIVMSG" RPL_QUIT = "QUIT" - List ModeOp = 'l' - Add ModeOp = 'a' - Remove ModeOp = 'r' + Add ModeOp = '+' + List ModeOp = '=' + Remove ModeOp = '-' Away UserMode = 'a' Invisible UserMode = 'i' - WallOps UserMode = 'w' - Restricted UserMode = 'r' - Operator UserMode = 'o' LocalOperator UserMode = 'O' + Operator UserMode = 'o' + Restricted UserMode = 'r' ServerNotice UserMode = 's' + WallOps UserMode = 'w' Anonymous ChannelMode = 'a' + BanMask ChannelMode = 'b' // arg + ExceptionMask ChannelMode = 'e' // arg + InviteMask ChannelMode = 'i' // arg InviteOnly ChannelMode = 'i' + Key ChannelMode = 'k' // arg Moderated ChannelMode = 'm' NoOutside ChannelMode = 'n' - Quiet ChannelMode = 'q' - Private ChannelMode = 'p' - Secret ChannelMode = 's' - ReOp ChannelMode = 'r' OpOnlyTopic ChannelMode = 't' - Key ChannelMode = 'k' - UserLimit ChannelMode = 'l' - BanMask ChannelMode = 'b' - ExceptionMask ChannelMode = 'e' - InviteMask ChannelMode = 'i' + Private ChannelMode = 'p' + Quiet ChannelMode = 'q' + ReOp ChannelMode = 'r' + Secret ChannelMode = 's' + UserLimit ChannelMode = 'l' // arg ChannelCreator UserChannelMode = 'O' ChannelOperator UserChannelMode = 'o' diff --git a/irc/server.go b/irc/server.go index 090e3af2..3961b94d 100644 --- a/irc/server.go +++ b/irc/server.go @@ -271,7 +271,12 @@ func (m *ModeCommand) HandleServer(s *Server) { if client.Nick() == m.nickname { for _, change := range m.changes { if change.mode == Invisible { - client.invisible = change.add + switch change.op { + case Add: + client.invisible = true + case Remove: + client.invisible = false + } } } client.replies <- RplUModeIs(s, client) @@ -320,21 +325,22 @@ func (msg *WhoCommand) HandleServer(server *Server) { client := msg.Client() // TODO implement wildcard matching - if msg.mask == "" { + mask := string(msg.mask) + if mask == "" { for _, channel := range server.channels { whoChannel(client, server, channel) } - } else if IsChannel(msg.mask) { - channel := server.channels[msg.mask] + } else if IsChannel(mask) { + channel := server.channels[mask] if channel != nil { whoChannel(client, server, channel) } } else { - mclient := server.clients[msg.mask] + mclient := server.clients[mask] if mclient != nil { client.replies <- RplWhoReply(server, mclient.channels.First(), mclient) } } - client.replies <- RplEndOfWho(server, msg.mask) + client.replies <- RplEndOfWho(server, mask) } diff --git a/irc/types.go b/irc/types.go index 30966435..cdc16f87 100644 --- a/irc/types.go +++ b/irc/types.go @@ -4,16 +4,31 @@ import ( "fmt" ) +// // simple types +// -type ModeOp rune -type UserMode rune -type ChannelMode rune -type UserChannelMode rune +// a string with wildcards type Mask string -// interfaces +// add, remove, list modes +type ModeOp rune +// user mode flags +type UserMode rune + +// channel mode flags +type ChannelMode rune + +// user-channel mode flags +type UserChannelMode rune + +// +// interfaces +// + +// commands the server understands +// TODO rename ServerCommand type Command interface { Client() *Client Source() Identifier @@ -21,7 +36,9 @@ type Command interface { HandleServer(*Server) } +// // structs +// type UserMask struct { nickname Mask