diff --git a/irc/client.go b/irc/client.go index 30cce792..14b033e1 100644 --- a/irc/client.go +++ b/irc/client.go @@ -21,6 +21,7 @@ type Client struct { loginTimer *time.Timer nick string operator bool + phase Phase quitTimer *time.Timer realname string registered bool @@ -39,6 +40,7 @@ func NewClient(server *Server, conn net.Conn) *Client { channels: make(ChannelSet), ctime: now, hostname: AddrLookupHostname(conn.RemoteAddr()), + phase: server.InitPhase(), replies: make(chan Reply), server: server, socket: NewSocket(conn), diff --git a/irc/commands.go b/irc/commands.go index 5313456e..546bbdba 100644 --- a/irc/commands.go +++ b/irc/commands.go @@ -9,6 +9,7 @@ import ( type editableCommand interface { Command + SetName(string) SetClient(*Client) } @@ -43,6 +44,7 @@ var ( type BaseCommand struct { client *Client + name string } func (command *BaseCommand) Client() *Client { @@ -53,21 +55,34 @@ func (command *BaseCommand) SetClient(c *Client) { command.client = c } +func (command *BaseCommand) Name() string { + return command.name +} + +func (command *BaseCommand) SetName(name string) { + command.name = name +} + func (command *BaseCommand) Source() Identifier { - return command.client + return command.Client() } func (command *BaseCommand) Reply(reply Reply) { command.client.Reply(reply) } -func ParseCommand(line string) (editableCommand, error) { +func ParseCommand(line string) (cmd editableCommand, err error) { command, args := parseLine(line) constructor := parseCommandFuncs[command] if constructor == nil { - return NewUnknownCommand(command, args), nil + cmd = NewUnknownCommand(command, args) + } else { + cmd, err = constructor(args) } - return constructor(args) + if cmd != nil { + cmd.SetName(command) + } + return } func parseArg(line string) (arg string, rest string) { @@ -100,18 +115,16 @@ func parseLine(line string) (command string, args []string) { type UnknownCommand struct { BaseCommand - command string - args []string + args []string } func (cmd *UnknownCommand) String() string { - return fmt.Sprintf("UNKNOWN(command=%s, args=%s)", cmd.command, cmd.args) + return fmt.Sprintf("UNKNOWN(command=%s, args=%s)", cmd.Name(), cmd.args) } func NewUnknownCommand(command string, args []string) *UnknownCommand { return &UnknownCommand{ - command: command, - args: args, + args: args, } } diff --git a/irc/constants.go b/irc/constants.go index 718f3d0a..243eeef0 100644 --- a/irc/constants.go +++ b/irc/constants.go @@ -192,3 +192,9 @@ const ( ChannelOperator UserChannelMode = 'o' Voice UserChannelMode = 'v' ) + +const ( + Authorization Phase = iota + Registration Phase = iota + Normal Phase = iota +) diff --git a/irc/server.go b/irc/server.go index 027f5436..ee3b43af 100644 --- a/irc/server.go +++ b/irc/server.go @@ -58,13 +58,32 @@ func (server *Server) receiveCommands() { } client := command.Client() - if !server.Authorize(client, command) { - client.Destroy() - return - } + switch client.phase { + case Authorization: + authCommand, ok := command.(AuthServerCommand) + if !ok { + client.Destroy() + return + } + authCommand.HandleAuthServer(server) - client.Touch() - command.HandleServer(server) + case Registration: + regCommand, ok := command.(RegServerCommand) + if !ok { + client.Destroy() + return + } + regCommand.HandleRegServer(server) + + default: + serverCommand, ok := command.(ServerCommand) + if !ok { + client.Reply(ErrUnknownCommand(server, command.Name())) + return + } + client.Touch() + serverCommand.HandleServer(server) + } } } @@ -72,24 +91,11 @@ func (server *Server) Command(command Command) { server.commands <- command } -func (server *Server) Authorize(client *Client, command Command) bool { - if client.authorized { - return true - } - +func (server *Server) InitPhase() Phase { if server.password == "" { - client.authorized = true - return true + return Registration } - - switch command.(type) { - case *PassCommand, *CapCommand, *ProxyCommand: - // no-op - default: // any other commands void authorization - return false - } - - return true + return Authorization } func newListener(config ListenerConfig) (net.Listener, error) { @@ -157,11 +163,14 @@ func (s *Server) GenerateGuestNick() string { } } +// // server functionality +// func (s *Server) tryRegister(c *Client) { - if !c.registered && c.HasNick() && c.HasUsername() { + if c.HasNick() && c.HasUsername() { c.registered = true + c.phase = Normal c.loginTimer.Stop() c.Reply( RplWelcome(s, c), @@ -221,29 +230,20 @@ func (s *Server) Nick() string { } // -// commands +// authorization commands // -func (m *UnknownCommand) HandleServer(s *Server) { - m.Client().Reply(ErrUnknownCommand(s, m.command)) +func (msg *ProxyCommand) HandleAuthServer(server *Server) { + msg.Client().hostname = LookupHostname(msg.sourceIP) } -func (m *PingCommand) HandleServer(s *Server) { - m.Client().Reply(RplPong(s, m.Client())) +func (msg *CapCommand) HandleAuthServer(server *Server) { + // TODO } -func (m *PongCommand) HandleServer(s *Server) { - // no-op -} - -func (m *PassCommand) HandleServer(s *Server) { +func (m *PassCommand) HandleAuthServer(s *Server) { client := m.Client() - if client.registered || client.authorized { - client.Reply(ErrAlreadyRegistered(s)) - return - } - if s.password != m.password { client.Reply(ErrPasswdMismatch(s)) client.Destroy() @@ -251,6 +251,52 @@ func (m *PassCommand) HandleServer(s *Server) { } client.authorized = true + client.phase = Registration +} + +// +// registration commands +// + +func (m *NickCommand) HandleRegServer(s *Server) { + client := m.Client() + + if m.nickname == "" { + client.Reply(ErrNoNicknameGiven(s)) + return + } + + if s.clients[m.nickname] != nil { + client.Reply(ErrNickNameInUse(s, m.nickname)) + return + } + + client.nick = m.nickname + s.clients.Add(client) + client.Reply(RplNick(client, m.nickname)) + s.tryRegister(client) +} + +func (m *UserMsgCommand) HandleRegServer(s *Server) { + c := m.Client() + c.username, c.realname = m.user, m.realname + s.tryRegister(c) +} + +// +// normal commands +// + +func (m *PassCommand) HandleServer(s *Server) { + m.Client().Reply(ErrAlreadyRegistered(s)) +} + +func (m *PingCommand) HandleServer(s *Server) { + m.Client().Reply(RplPong(s, m.Client())) +} + +func (m *PongCommand) HandleServer(s *Server) { + // no-op } func (m *NickCommand) HandleServer(s *Server) { @@ -274,23 +320,13 @@ func (m *NickCommand) HandleServer(s *Server) { s.clients.Add(c) iclients := c.InterestedClients() - iclients.Add(c) for iclient := range iclients { iclient.Reply(reply) } - - s.tryRegister(c) } func (m *UserMsgCommand) HandleServer(s *Server) { - c := m.Client() - if c.registered { - c.Reply(ErrAlreadyRegistered(s)) - return - } - - c.username, c.realname = m.user, m.realname - s.tryRegister(c) + m.Client().Reply(ErrAlreadyRegistered(s)) } func (m *QuitCommand) HandleServer(server *Server) { @@ -468,14 +504,6 @@ func (msg *OperCommand) HandleServer(server *Server) { client.Reply(RplUModeIs(server, client)) } -func (msg *CapCommand) HandleServer(server *Server) { - // TODO -} - -func (msg *ProxyCommand) HandleServer(server *Server) { - msg.Client().hostname = LookupHostname(msg.sourceIP) -} - func (msg *AwayCommand) HandleServer(server *Server) { client := msg.Client() client.away = msg.away diff --git a/irc/types.go b/irc/types.go index 2cf20b86..618f24ca 100644 --- a/irc/types.go +++ b/irc/types.go @@ -18,6 +18,8 @@ type ModeOp rune // user mode flags type UserMode rune +type Phase uint + func (mode UserMode) String() string { return fmt.Sprintf("%c", mode) } @@ -125,15 +127,28 @@ type Reply interface { Source() Identifier } -// commands the server understands -// TODO rename ServerCommand type Command interface { + Name() string Client() *Client Source() Identifier Reply(Reply) +} + +type ServerCommand interface { + Command HandleServer(*Server) } +type AuthServerCommand interface { + Command + HandleAuthServer(*Server) +} + +type RegServerCommand interface { + Command + HandleRegServer(*Server) +} + type ChannelCommand interface { Command HandleChannel(channel *Channel)