organize server command into phases

This commit is contained in:
Jeremy Latt 2014-02-14 18:28:36 -08:00
parent 29df88fb7a
commit 4d9742d033
5 changed files with 132 additions and 68 deletions

View File

@ -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),

View File

@ -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,
}
}

View File

@ -192,3 +192,9 @@ const (
ChannelOperator UserChannelMode = 'o'
Voice UserChannelMode = 'v'
)
const (
Authorization Phase = iota
Registration Phase = iota
Normal Phase = iota
)

View File

@ -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

View File

@ -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)