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:
parent
29df88fb7a
commit
4d9742d033
@ -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),
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
)
|
||||||
|
134
irc/server.go
134
irc/server.go
@ -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
|
||||||
|
19
irc/types.go
19
irc/types.go
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user