mirror of
https://github.com/ergochat/ergo.git
synced 2024-12-22 18:52:41 +01:00
organize server command into phases
This commit is contained in:
parent
29df88fb7a
commit
4d9742d033
@ -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),
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -192,3 +192,9 @@ const (
|
||||
ChannelOperator UserChannelMode = 'o'
|
||||
Voice UserChannelMode = 'v'
|
||||
)
|
||||
|
||||
const (
|
||||
Authorization Phase = iota
|
||||
Registration Phase = iota
|
||||
Normal Phase = iota
|
||||
)
|
||||
|
142
irc/server.go
142
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
|
||||
|
19
irc/types.go
19
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)
|
||||
|
Loading…
Reference in New Issue
Block a user