3
0
mirror of https://github.com/ergochat/ergo.git synced 2024-11-13 07:29:30 +01:00

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 loginTimer *time.Timer
nick string nick string
operator bool operator bool
phase Phase
quitTimer *time.Timer quitTimer *time.Timer
realname string realname string
registered bool registered bool
@ -39,6 +40,7 @@ func NewClient(server *Server, conn net.Conn) *Client {
channels: make(ChannelSet), channels: make(ChannelSet),
ctime: now, ctime: now,
hostname: AddrLookupHostname(conn.RemoteAddr()), hostname: AddrLookupHostname(conn.RemoteAddr()),
phase: server.InitPhase(),
replies: make(chan Reply), replies: make(chan Reply),
server: server, server: server,
socket: NewSocket(conn), socket: NewSocket(conn),

View File

@ -9,6 +9,7 @@ import (
type editableCommand interface { type editableCommand interface {
Command Command
SetName(string)
SetClient(*Client) SetClient(*Client)
} }
@ -43,6 +44,7 @@ var (
type BaseCommand struct { type BaseCommand struct {
client *Client client *Client
name string
} }
func (command *BaseCommand) Client() *Client { func (command *BaseCommand) Client() *Client {
@ -53,21 +55,34 @@ func (command *BaseCommand) SetClient(c *Client) {
command.client = c 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 { func (command *BaseCommand) Source() Identifier {
return command.client return command.Client()
} }
func (command *BaseCommand) Reply(reply Reply) { func (command *BaseCommand) Reply(reply Reply) {
command.client.Reply(reply) command.client.Reply(reply)
} }
func ParseCommand(line string) (editableCommand, error) { func ParseCommand(line string) (cmd editableCommand, err error) {
command, args := parseLine(line) command, args := parseLine(line)
constructor := parseCommandFuncs[command] constructor := parseCommandFuncs[command]
if constructor == nil { 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) { func parseArg(line string) (arg string, rest string) {
@ -100,17 +115,15 @@ func parseLine(line string) (command string, args []string) {
type UnknownCommand struct { type UnknownCommand struct {
BaseCommand BaseCommand
command string
args []string args []string
} }
func (cmd *UnknownCommand) String() 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 { func NewUnknownCommand(command string, args []string) *UnknownCommand {
return &UnknownCommand{ return &UnknownCommand{
command: command,
args: args, args: args,
} }
} }

View File

@ -192,3 +192,9 @@ const (
ChannelOperator UserChannelMode = 'o' ChannelOperator UserChannelMode = 'o'
Voice UserChannelMode = 'v' 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() client := command.Client()
if !server.Authorize(client, command) { switch client.phase {
case Authorization:
authCommand, ok := command.(AuthServerCommand)
if !ok {
client.Destroy() client.Destroy()
return return
} }
authCommand.HandleAuthServer(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() client.Touch()
command.HandleServer(server) serverCommand.HandleServer(server)
}
} }
} }
@ -72,24 +91,11 @@ func (server *Server) Command(command Command) {
server.commands <- command server.commands <- command
} }
func (server *Server) Authorize(client *Client, command Command) bool { func (server *Server) InitPhase() Phase {
if client.authorized {
return true
}
if server.password == "" { if server.password == "" {
client.authorized = true return Registration
return true
} }
return Authorization
switch command.(type) {
case *PassCommand, *CapCommand, *ProxyCommand:
// no-op
default: // any other commands void authorization
return false
}
return true
} }
func newListener(config ListenerConfig) (net.Listener, error) { func newListener(config ListenerConfig) (net.Listener, error) {
@ -157,11 +163,14 @@ func (s *Server) GenerateGuestNick() string {
} }
} }
//
// server functionality // server functionality
//
func (s *Server) tryRegister(c *Client) { func (s *Server) tryRegister(c *Client) {
if !c.registered && c.HasNick() && c.HasUsername() { if c.HasNick() && c.HasUsername() {
c.registered = true c.registered = true
c.phase = Normal
c.loginTimer.Stop() c.loginTimer.Stop()
c.Reply( c.Reply(
RplWelcome(s, c), RplWelcome(s, c),
@ -221,29 +230,20 @@ func (s *Server) Nick() string {
} }
// //
// commands // authorization commands
// //
func (m *UnknownCommand) HandleServer(s *Server) { func (msg *ProxyCommand) HandleAuthServer(server *Server) {
m.Client().Reply(ErrUnknownCommand(s, m.command)) msg.Client().hostname = LookupHostname(msg.sourceIP)
} }
func (m *PingCommand) HandleServer(s *Server) { func (msg *CapCommand) HandleAuthServer(server *Server) {
m.Client().Reply(RplPong(s, m.Client())) // TODO
} }
func (m *PongCommand) HandleServer(s *Server) { func (m *PassCommand) HandleAuthServer(s *Server) {
// no-op
}
func (m *PassCommand) HandleServer(s *Server) {
client := m.Client() client := m.Client()
if client.registered || client.authorized {
client.Reply(ErrAlreadyRegistered(s))
return
}
if s.password != m.password { if s.password != m.password {
client.Reply(ErrPasswdMismatch(s)) client.Reply(ErrPasswdMismatch(s))
client.Destroy() client.Destroy()
@ -251,6 +251,52 @@ func (m *PassCommand) HandleServer(s *Server) {
} }
client.authorized = true 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) { func (m *NickCommand) HandleServer(s *Server) {
@ -274,23 +320,13 @@ func (m *NickCommand) HandleServer(s *Server) {
s.clients.Add(c) s.clients.Add(c)
iclients := c.InterestedClients() iclients := c.InterestedClients()
iclients.Add(c)
for iclient := range iclients { for iclient := range iclients {
iclient.Reply(reply) iclient.Reply(reply)
} }
s.tryRegister(c)
} }
func (m *UserMsgCommand) HandleServer(s *Server) { func (m *UserMsgCommand) HandleServer(s *Server) {
c := m.Client() m.Client().Reply(ErrAlreadyRegistered(s))
if c.registered {
c.Reply(ErrAlreadyRegistered(s))
return
}
c.username, c.realname = m.user, m.realname
s.tryRegister(c)
} }
func (m *QuitCommand) HandleServer(server *Server) { func (m *QuitCommand) HandleServer(server *Server) {
@ -468,14 +504,6 @@ func (msg *OperCommand) HandleServer(server *Server) {
client.Reply(RplUModeIs(server, client)) 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) { func (msg *AwayCommand) HandleServer(server *Server) {
client := msg.Client() client := msg.Client()
client.away = msg.away client.away = msg.away

View File

@ -18,6 +18,8 @@ type ModeOp rune
// user mode flags // user mode flags
type UserMode rune type UserMode rune
type Phase uint
func (mode UserMode) String() string { func (mode UserMode) String() string {
return fmt.Sprintf("%c", mode) return fmt.Sprintf("%c", mode)
} }
@ -125,15 +127,28 @@ type Reply interface {
Source() Identifier Source() Identifier
} }
// commands the server understands
// TODO rename ServerCommand
type Command interface { type Command interface {
Name() string
Client() *Client Client() *Client
Source() Identifier Source() Identifier
Reply(Reply) Reply(Reply)
}
type ServerCommand interface {
Command
HandleServer(*Server) HandleServer(*Server)
} }
type AuthServerCommand interface {
Command
HandleAuthServer(*Server)
}
type RegServerCommand interface {
Command
HandleRegServer(*Server)
}
type ChannelCommand interface { type ChannelCommand interface {
Command Command
HandleChannel(channel *Channel) HandleChannel(channel *Channel)