mirror of
https://github.com/ergochat/ergo.git
synced 2024-11-22 20:09:41 +01:00
Massive refactor to support multiple connections and NickServ.
This commit is contained in:
parent
f2aedbaffd
commit
4b0cfa816c
2
build.sh
2
build.sh
@ -1,4 +1,4 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
export GOPATH="$PWD"
|
export GOPATH="$PWD"
|
||||||
go get "code.google.com/p/go.crypto/bcrypt"
|
go get "code.google.com/p/go.crypto/bcrypt"
|
||||||
go install -v ergonomadic genpasswd
|
go install ergonomadic genpasswd
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
package irc
|
package irc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
type Channel struct {
|
type Channel struct {
|
||||||
server *Server
|
server *Server
|
||||||
replies chan<- Reply
|
|
||||||
commands chan<- ChannelCommand
|
commands chan<- ChannelCommand
|
||||||
|
replies chan<- Reply
|
||||||
name string
|
name string
|
||||||
key string
|
key string
|
||||||
topic string
|
topic string
|
||||||
@ -27,28 +31,6 @@ type ChannelCommand interface {
|
|||||||
HandleChannel(channel *Channel)
|
HandleChannel(channel *Channel)
|
||||||
}
|
}
|
||||||
|
|
||||||
type JoinChannelCommand struct {
|
|
||||||
*JoinCommand
|
|
||||||
key string
|
|
||||||
}
|
|
||||||
|
|
||||||
type PartChannelCommand struct {
|
|
||||||
Command
|
|
||||||
message string
|
|
||||||
}
|
|
||||||
|
|
||||||
type GetTopicChannelCommand struct {
|
|
||||||
*TopicCommand
|
|
||||||
}
|
|
||||||
|
|
||||||
type SetTopicChannelCommand struct {
|
|
||||||
*TopicCommand
|
|
||||||
}
|
|
||||||
|
|
||||||
type PrivMsgChannelCommand struct {
|
|
||||||
*PrivMsgCommand
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewChannel creates a new channel from a `Server` and a `name` string, which
|
// NewChannel creates a new channel from a `Server` and a `name` string, which
|
||||||
// must be unique on the server.
|
// must be unique on the server.
|
||||||
func NewChannel(s *Server, name string) *Channel {
|
func NewChannel(s *Server, name string) *Channel {
|
||||||
@ -69,14 +51,17 @@ func NewChannel(s *Server, name string) *Channel {
|
|||||||
// Forward `Reply`s to all `User`s of the `Channel`.
|
// Forward `Reply`s to all `User`s of the `Channel`.
|
||||||
func (ch *Channel) receiveReplies(replies <-chan Reply) {
|
func (ch *Channel) receiveReplies(replies <-chan Reply) {
|
||||||
for reply := range replies {
|
for reply := range replies {
|
||||||
for client := range ch.members {
|
for user := range ch.members {
|
||||||
client.replies <- reply
|
if user != reply.Source() {
|
||||||
|
user.replies <- reply
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch *Channel) receiveCommands(commands <-chan ChannelCommand) {
|
func (ch *Channel) receiveCommands(commands <-chan ChannelCommand) {
|
||||||
for command := range commands {
|
for command := range commands {
|
||||||
|
log.Printf("%s %T %+v", ch.Id(), command, command)
|
||||||
command.HandleChannel(ch)
|
command.HandleChannel(ch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -95,29 +80,46 @@ func (ch *Channel) IsEmpty() bool {
|
|||||||
return len(ch.members) == 0
|
return len(ch.members) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (channel *Channel) GetTopic(replier Replier) {
|
||||||
|
if channel.topic == "" {
|
||||||
|
replier.Replies() <- RplNoTopic(channel)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
replier.Replies() <- RplTopic(channel)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (channel *Channel) Id() string {
|
||||||
|
return channel.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (channel *Channel) PublicId() string {
|
||||||
|
return channel.name
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// commands
|
// commands
|
||||||
//
|
//
|
||||||
|
|
||||||
func (m *JoinChannelCommand) HandleChannel(channel *Channel) {
|
func (m *JoinCommand) HandleChannel(channel *Channel) {
|
||||||
client := m.Client()
|
client := m.Client()
|
||||||
user := client.user
|
user := client.user
|
||||||
|
|
||||||
if channel.key != m.key {
|
if channel.key != m.channels[channel.name] {
|
||||||
client.user.replies <- ErrBadChannelKey(channel)
|
client.user.replies <- ErrBadChannelKey(channel)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
channel.members.Add(client.user)
|
channel.members.Add(user)
|
||||||
client.user.channels.Add(channel)
|
user.channels.Add(channel)
|
||||||
|
|
||||||
channel.replies <- RplJoin(channel, user)
|
channel.replies <- RplJoin(channel, user)
|
||||||
channel.GetTopic(user)
|
channel.GetTopic(user)
|
||||||
client.user.replies <- RplNamReply(channel)
|
user.replies <- RplNamReply(channel)
|
||||||
client.user.replies <- RplEndOfNames(channel.server)
|
user.replies <- RplEndOfNames(channel.server)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *PartChannelCommand) HandleChannel(channel *Channel) {
|
func (m *PartCommand) HandleChannel(channel *Channel) {
|
||||||
user := m.Client().user
|
user := m.Client().user
|
||||||
|
|
||||||
if !channel.members[user] {
|
if !channel.members[user] {
|
||||||
@ -140,25 +142,7 @@ func (m *PartChannelCommand) HandleChannel(channel *Channel) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (channel *Channel) GetTopic(user *User) {
|
func (m *TopicCommand) HandleChannel(channel *Channel) {
|
||||||
if !channel.members[user] {
|
|
||||||
user.replies <- ErrNotOnChannel(channel)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if channel.topic == "" {
|
|
||||||
user.replies <- RplNoTopic(channel)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
user.replies <- RplTopic(channel)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *GetTopicChannelCommand) HandleChannel(channel *Channel) {
|
|
||||||
channel.GetTopic(m.Client().user)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *SetTopicChannelCommand) HandleChannel(channel *Channel) {
|
|
||||||
user := m.Client().user
|
user := m.Client().user
|
||||||
|
|
||||||
if !channel.members[user] {
|
if !channel.members[user] {
|
||||||
@ -166,6 +150,11 @@ func (m *SetTopicChannelCommand) HandleChannel(channel *Channel) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if m.topic == "" {
|
||||||
|
channel.GetTopic(user)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
channel.topic = m.topic
|
channel.topic = m.topic
|
||||||
|
|
||||||
if channel.topic == "" {
|
if channel.topic == "" {
|
||||||
@ -176,6 +165,6 @@ func (m *SetTopicChannelCommand) HandleChannel(channel *Channel) {
|
|||||||
channel.replies <- RplTopic(channel)
|
channel.replies <- RplTopic(channel)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *PrivMsgChannelCommand) HandleChannel(channel *Channel) {
|
func (m *PrivMsgCommand) HandleChannel(channel *Channel) {
|
||||||
channel.replies <- RplPrivMsgChannel(channel, m.Client().user, m.message)
|
channel.replies <- RplPrivMsgChannel(channel, m.Client().user, m.message)
|
||||||
}
|
}
|
||||||
|
@ -7,9 +7,13 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Replier interface {
|
||||||
|
Identifier
|
||||||
|
Replies() chan<- Reply
|
||||||
|
}
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
conn net.Conn
|
conn net.Conn
|
||||||
replies chan<- Reply
|
|
||||||
username string
|
username string
|
||||||
realname string
|
realname string
|
||||||
hostname string
|
hostname string
|
||||||
@ -20,6 +24,12 @@ type Client struct {
|
|||||||
server *Server
|
server *Server
|
||||||
atime time.Time
|
atime time.Time
|
||||||
user *User
|
user *User
|
||||||
|
replies chan<- Reply
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClientMessage interface {
|
||||||
|
Client() *Client
|
||||||
|
SetClient(*Client)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ClientSet map[*Client]bool
|
type ClientSet map[*Client]bool
|
||||||
@ -36,10 +46,7 @@ func NewClient(server *Server, conn net.Conn) *Client {
|
|||||||
replies: replies,
|
replies: replies,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect the conn to the server.
|
|
||||||
go client.readConn(read)
|
go client.readConn(read)
|
||||||
|
|
||||||
// Connect the reply channel to the conn.
|
|
||||||
go client.writeConn(write, replies)
|
go client.writeConn(write, replies)
|
||||||
|
|
||||||
return client
|
return client
|
||||||
@ -51,7 +58,7 @@ func (c *Client) readConn(recv <-chan string) {
|
|||||||
|
|
||||||
m, err := ParseCommand(str)
|
m, err := ParseCommand(str)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO handle error
|
c.replies <- ErrNeedMoreParams(c.server, str)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,6 +75,14 @@ func (c *Client) writeConn(write chan<- string, replies <-chan Reply) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) Replies() chan<- Reply {
|
||||||
|
return c.replies
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Server() *Server {
|
||||||
|
return c.server
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) Nick() string {
|
func (c *Client) Nick() string {
|
||||||
if c.user != nil {
|
if c.user != nil {
|
||||||
return c.user.nick
|
return c.user.nick
|
||||||
|
@ -6,12 +6,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Command interface {
|
|
||||||
Client() *Client
|
|
||||||
SetClient(*Client)
|
|
||||||
Handle(*Server)
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
NotEnoughArgsError = errors.New("not enough arguments")
|
NotEnoughArgsError = errors.New("not enough arguments")
|
||||||
)
|
)
|
||||||
@ -28,7 +22,59 @@ func (base *BaseCommand) SetClient(c *Client) {
|
|||||||
base.client = c
|
base.client = c
|
||||||
}
|
}
|
||||||
|
|
||||||
// unknown <command> [args...]
|
var (
|
||||||
|
ErrParseCommand = errors.New("failed to parse message")
|
||||||
|
parseCommandFuncs = map[string]func([]string) (Command, error){
|
||||||
|
"JOIN": NewJoinCommand,
|
||||||
|
"MODE": NewModeCommand,
|
||||||
|
"NICK": NewNickCommand,
|
||||||
|
"PART": NewPartCommand,
|
||||||
|
"PASS": NewPassCommand,
|
||||||
|
"PING": NewPingCommand,
|
||||||
|
"PONG": NewPongCommand,
|
||||||
|
"PRIVMSG": NewPrivMsgCommand,
|
||||||
|
"QUIT": NewQuitCommand,
|
||||||
|
"TOPIC": NewTopicCommand,
|
||||||
|
"USER": NewUserMsgCommand,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func ParseCommand(line string) (Command, error) {
|
||||||
|
command, args := parseLine(line)
|
||||||
|
constructor := parseCommandFuncs[command]
|
||||||
|
if constructor == nil {
|
||||||
|
return NewUnknownCommand(command, args), nil
|
||||||
|
}
|
||||||
|
return constructor(args)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseArg(line string) (arg string, rest string) {
|
||||||
|
if line == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(line, ":") {
|
||||||
|
arg = line[1:]
|
||||||
|
} else {
|
||||||
|
parts := strings.SplitN(line, " ", 2)
|
||||||
|
arg = parts[0]
|
||||||
|
if len(parts) > 1 {
|
||||||
|
rest = parts[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseLine(line string) (command string, args []string) {
|
||||||
|
args = make([]string, 0)
|
||||||
|
for arg, rest := parseArg(line); arg != ""; arg, rest = parseArg(rest) {
|
||||||
|
args = append(args, arg)
|
||||||
|
}
|
||||||
|
command, args = strings.ToUpper(args[0]), args[1:]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// <command> [args...]
|
||||||
|
|
||||||
type UnknownCommand struct {
|
type UnknownCommand struct {
|
||||||
*BaseCommand
|
*BaseCommand
|
||||||
@ -36,7 +82,7 @@ type UnknownCommand struct {
|
|||||||
args []string
|
args []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUnknownCommand(command string, args []string) Command {
|
func NewUnknownCommand(command string, args []string) *UnknownCommand {
|
||||||
return &UnknownCommand{
|
return &UnknownCommand{
|
||||||
BaseCommand: &BaseCommand{},
|
BaseCommand: &BaseCommand{},
|
||||||
command: command,
|
command: command,
|
||||||
@ -44,10 +90,6 @@ func NewUnknownCommand(command string, args []string) Command {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *UnknownCommand) Handle(s *Server) {
|
|
||||||
m.Client().replies <- ErrUnknownCommand(s, m.command)
|
|
||||||
}
|
|
||||||
|
|
||||||
// PING <server1> [ <server2> ]
|
// PING <server1> [ <server2> ]
|
||||||
|
|
||||||
type PingCommand struct {
|
type PingCommand struct {
|
||||||
@ -128,7 +170,7 @@ func NewNickCommand(args []string) (Command, error) {
|
|||||||
|
|
||||||
// USER <user> <mode> <unused> <realname>
|
// USER <user> <mode> <unused> <realname>
|
||||||
|
|
||||||
type UserCommand struct {
|
type UserMsgCommand struct {
|
||||||
*BaseCommand
|
*BaseCommand
|
||||||
user string
|
user string
|
||||||
mode uint8
|
mode uint8
|
||||||
@ -136,11 +178,11 @@ type UserCommand struct {
|
|||||||
realname string
|
realname string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUserCommand(args []string) (Command, error) {
|
func NewUserMsgCommand(args []string) (Command, error) {
|
||||||
if len(args) != 4 {
|
if len(args) != 4 {
|
||||||
return nil, NotEnoughArgsError
|
return nil, NotEnoughArgsError
|
||||||
}
|
}
|
||||||
msg := &UserCommand{
|
msg := &UserMsgCommand{
|
||||||
BaseCommand: &BaseCommand{},
|
BaseCommand: &BaseCommand{},
|
||||||
user: args[0],
|
user: args[0],
|
||||||
unused: args[2],
|
unused: args[2],
|
||||||
@ -174,26 +216,36 @@ func NewQuitCommand(args []string) (Command, error) {
|
|||||||
|
|
||||||
type JoinCommand struct {
|
type JoinCommand struct {
|
||||||
*BaseCommand
|
*BaseCommand
|
||||||
channels []string
|
channels map[string]string
|
||||||
keys []string
|
|
||||||
zero bool
|
zero bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewJoinCommand(args []string) (Command, error) {
|
func NewJoinCommand(args []string) (Command, error) {
|
||||||
msg := &JoinCommand{
|
msg := &JoinCommand{
|
||||||
BaseCommand: &BaseCommand{},
|
BaseCommand: &BaseCommand{},
|
||||||
|
channels: make(map[string]string),
|
||||||
}
|
}
|
||||||
if len(args) > 0 {
|
|
||||||
if args[0] == "0" {
|
|
||||||
msg.zero = true
|
|
||||||
} else {
|
|
||||||
msg.channels = strings.Split(args[0], ",")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(args) > 1 {
|
if len(args) == 0 {
|
||||||
msg.keys = strings.Split(args[1], ",")
|
return nil, NotEnoughArgsError
|
||||||
|
}
|
||||||
|
|
||||||
|
if args[0] == "0" {
|
||||||
|
msg.zero = true
|
||||||
|
return msg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
channels := strings.Split(args[0], ",")
|
||||||
|
keys := make([]string, len(channels))
|
||||||
|
if len(args) > 1 {
|
||||||
|
for i, key := range strings.Split(args[1], ",") {
|
||||||
|
keys[i] = key
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for i, channel := range channels {
|
||||||
|
msg.channels[channel] = keys[i]
|
||||||
|
}
|
||||||
|
|
||||||
return msg, nil
|
return msg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,40 +320,25 @@ func NewTopicCommand(args []string) (Command, error) {
|
|||||||
return msg, nil
|
return msg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// LOGIN <nick> <password>
|
type ModeCommand struct {
|
||||||
|
|
||||||
type LoginCommand struct {
|
|
||||||
*BaseCommand
|
*BaseCommand
|
||||||
nick string
|
nickname string
|
||||||
password string
|
modes string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLoginCommand(args []string) (Command, error) {
|
func NewModeCommand(args []string) (Command, error) {
|
||||||
if len(args) < 2 {
|
if len(args) == 0 {
|
||||||
return nil, NotEnoughArgsError
|
return nil, NotEnoughArgsError
|
||||||
}
|
}
|
||||||
return &LoginCommand{
|
|
||||||
|
cmd := &ModeCommand{
|
||||||
BaseCommand: &BaseCommand{},
|
BaseCommand: &BaseCommand{},
|
||||||
nick: args[0],
|
nickname: args[0],
|
||||||
password: args[1],
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RESERVE <nick> <password>
|
|
||||||
|
|
||||||
type ReserveCommand struct {
|
|
||||||
*BaseCommand
|
|
||||||
nick string
|
|
||||||
password string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewReserveCommand(args []string) (Command, error) {
|
|
||||||
if len(args) < 2 {
|
|
||||||
return nil, NotEnoughArgsError
|
|
||||||
}
|
}
|
||||||
return &ReserveCommand{
|
|
||||||
BaseCommand: &BaseCommand{},
|
if len(args) > 1 {
|
||||||
nick: args[0],
|
cmd.modes = args[1]
|
||||||
password: args[1],
|
}
|
||||||
}, nil
|
|
||||||
|
return cmd, nil
|
||||||
}
|
}
|
||||||
|
135
src/irc/nickserv.go
Normal file
135
src/irc/nickserv.go
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
package irc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NickServCommand interface {
|
||||||
|
ClientMessage
|
||||||
|
HandleNickServ(*NickServ)
|
||||||
|
}
|
||||||
|
|
||||||
|
type NickServ struct {
|
||||||
|
*Service
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNickServ(s *Server) *NickServ {
|
||||||
|
ns := &NickServ{}
|
||||||
|
ns.Service = NewService(s, "NickServ", func(m *PrivMsgCommand) {
|
||||||
|
m.HandleNickServ(ns)
|
||||||
|
})
|
||||||
|
return ns
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
parseNickServCommandFuncs = map[string]func([]string) (NickServCommand, error){
|
||||||
|
"REGISTER": NewRegisterCommand,
|
||||||
|
"IDENTIFY": NewIdentifyCommand,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
//
|
||||||
|
// commands
|
||||||
|
//
|
||||||
|
|
||||||
|
func (m *PrivMsgCommand) HandleNickServ(ns *NickServ) {
|
||||||
|
command, args := parseLine(m.message)
|
||||||
|
constructor := parseNickServCommandFuncs[command]
|
||||||
|
if constructor == nil {
|
||||||
|
ns.Reply(m.Client(), "Unknown command.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd, err := constructor(args)
|
||||||
|
if err != nil {
|
||||||
|
ns.Reply(m.Client(), "Not enough parameters.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.SetClient(m.Client())
|
||||||
|
log.Printf("%s %T %+v", ns.Id(), cmd, cmd)
|
||||||
|
cmd.HandleNickServ(ns)
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// sub-commands
|
||||||
|
//
|
||||||
|
|
||||||
|
type RegisterCommand struct {
|
||||||
|
*BaseCommand
|
||||||
|
password string
|
||||||
|
email string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRegisterCommand(args []string) (NickServCommand, error) {
|
||||||
|
if len(args) == 0 {
|
||||||
|
return nil, NotEnoughArgsError
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := &RegisterCommand{
|
||||||
|
BaseCommand: &BaseCommand{},
|
||||||
|
password: args[0],
|
||||||
|
}
|
||||||
|
if len(args) > 1 {
|
||||||
|
cmd.email = args[1]
|
||||||
|
}
|
||||||
|
return cmd, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *RegisterCommand) HandleNickServ(ns *NickServ) {
|
||||||
|
client := m.Client()
|
||||||
|
|
||||||
|
if client.user != nil {
|
||||||
|
ns.Reply(client, "You are already registered.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if ns.server.users[client.nick] != nil {
|
||||||
|
ns.Reply(client, "That nick is already registered.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
user := NewUser(client.nick, m.password, ns.server)
|
||||||
|
ns.server.users[client.nick] = user
|
||||||
|
ns.Reply(client, "You have registered.")
|
||||||
|
|
||||||
|
if !user.Login(client, client.nick, m.password) {
|
||||||
|
ns.Reply(client, "Login failed.")
|
||||||
|
}
|
||||||
|
ns.Reply(client, "Logged in.")
|
||||||
|
}
|
||||||
|
|
||||||
|
type IdentifyCommand struct {
|
||||||
|
*BaseCommand
|
||||||
|
password string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewIdentifyCommand(args []string) (NickServCommand, error) {
|
||||||
|
if len(args) == 0 {
|
||||||
|
return nil, NotEnoughArgsError
|
||||||
|
}
|
||||||
|
|
||||||
|
return &IdentifyCommand{
|
||||||
|
BaseCommand: &BaseCommand{},
|
||||||
|
password: args[0],
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *IdentifyCommand) HandleNickServ(ns *NickServ) {
|
||||||
|
client := m.Client()
|
||||||
|
if client.user != nil {
|
||||||
|
ns.Reply(client, "That nick is already registered.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
user := ns.server.users[client.nick]
|
||||||
|
if user == nil {
|
||||||
|
ns.Reply(client, "No such nick.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !user.Login(client, client.nick, m.password) {
|
||||||
|
ns.Reply(client, "Login failed.")
|
||||||
|
}
|
||||||
|
ns.Reply(client, "Logged in.")
|
||||||
|
}
|
@ -1,60 +0,0 @@
|
|||||||
package irc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ParseFunc func([]string) (Command, error)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrParseCommand = errors.New("failed to parse message")
|
|
||||||
parseCommandFuncs = map[string]ParseFunc{
|
|
||||||
"JOIN": NewJoinCommand,
|
|
||||||
"LOGIN": NewLoginCommand,
|
|
||||||
"NICK": NewNickCommand,
|
|
||||||
"PART": NewPartCommand,
|
|
||||||
"PASS": NewPassCommand,
|
|
||||||
"PING": NewPingCommand,
|
|
||||||
"PONG": NewPongCommand,
|
|
||||||
"PRIVMSG": NewPrivMsgCommand,
|
|
||||||
"QUIT": NewQuitCommand,
|
|
||||||
"TOPIC": NewTopicCommand,
|
|
||||||
"USER": NewUserCommand,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func ParseCommand(line string) (Command, error) {
|
|
||||||
command, args := parseLine(line)
|
|
||||||
constructor := parseCommandFuncs[command]
|
|
||||||
if constructor == nil {
|
|
||||||
return NewUnknownCommand(command, args), nil
|
|
||||||
}
|
|
||||||
return constructor(args)
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseArg(line string) (arg string, rest string) {
|
|
||||||
if line == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.HasPrefix(line, ":") {
|
|
||||||
arg = line[1:]
|
|
||||||
} else {
|
|
||||||
parts := strings.SplitN(line, " ", 2)
|
|
||||||
arg = parts[0]
|
|
||||||
if len(parts) > 1 {
|
|
||||||
rest = parts[1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseLine(line string) (command string, args []string) {
|
|
||||||
args = make([]string, 0)
|
|
||||||
for arg, rest := parseArg(line); arg != ""; arg, rest = parseArg(rest) {
|
|
||||||
args = append(args, arg)
|
|
||||||
}
|
|
||||||
command, args = args[0], args[1:]
|
|
||||||
return
|
|
||||||
}
|
|
@ -9,10 +9,12 @@ import (
|
|||||||
type Identifier interface {
|
type Identifier interface {
|
||||||
Id() string
|
Id() string
|
||||||
PublicId() string
|
PublicId() string
|
||||||
|
Nick() string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Reply interface {
|
type Reply interface {
|
||||||
String(client *Client) string
|
String(client *Client) string
|
||||||
|
Source() Identifier
|
||||||
}
|
}
|
||||||
|
|
||||||
type BasicReply struct {
|
type BasicReply struct {
|
||||||
@ -21,7 +23,8 @@ type BasicReply struct {
|
|||||||
message string
|
message string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBasicReply(source Identifier, code string, format string, args ...interface{}) *BasicReply {
|
func NewBasicReply(source Identifier, code string,
|
||||||
|
format string, args ...interface{}) *BasicReply {
|
||||||
message := fmt.Sprintf(format, args...)
|
message := fmt.Sprintf(format, args...)
|
||||||
fullMessage := fmt.Sprintf(":%s %s %s\r\n", source.Id(), code, message)
|
fullMessage := fmt.Sprintf(":%s %s %s\r\n", source.Id(), code, message)
|
||||||
return &BasicReply{source, code, fullMessage}
|
return &BasicReply{source, code, fullMessage}
|
||||||
@ -31,11 +34,16 @@ func (reply *BasicReply) String(client *Client) string {
|
|||||||
return reply.message
|
return reply.message
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (reply *BasicReply) Source() Identifier {
|
||||||
|
return reply.source
|
||||||
|
}
|
||||||
|
|
||||||
type NumericReply struct {
|
type NumericReply struct {
|
||||||
*BasicReply
|
*BasicReply
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewNumericReply(source Identifier, code string, format string, args ...interface{}) *NumericReply {
|
func NewNumericReply(source Identifier, code string,
|
||||||
|
format string, args ...interface{}) *NumericReply {
|
||||||
return &NumericReply{&BasicReply{source, code, fmt.Sprintf(format, args...)}}
|
return &NumericReply{&BasicReply{source, code, fmt.Sprintf(format, args...)}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,7 +55,7 @@ func (reply *NumericReply) String(client *Client) string {
|
|||||||
// messaging replies
|
// messaging replies
|
||||||
|
|
||||||
func RplPrivMsg(source Identifier, target Identifier, message string) Reply {
|
func RplPrivMsg(source Identifier, target Identifier, message string) Reply {
|
||||||
return NewBasicReply(source, RPL_PRIVMSG, "%s :%s", target, message)
|
return NewBasicReply(source, RPL_PRIVMSG, "%s :%s", target.Nick(), message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RplNick(client *Client, newNick string) Reply {
|
func RplNick(client *Client, newNick string) Reply {
|
||||||
|
@ -10,15 +10,22 @@ import (
|
|||||||
type ClientNameMap map[string]*Client
|
type ClientNameMap map[string]*Client
|
||||||
type ChannelNameMap map[string]*Channel
|
type ChannelNameMap map[string]*Channel
|
||||||
type UserNameMap map[string]*User
|
type UserNameMap map[string]*User
|
||||||
|
type ServiceNameMap map[string]*Service
|
||||||
|
|
||||||
|
type Command interface {
|
||||||
|
ClientMessage
|
||||||
|
Handle(*Server)
|
||||||
|
}
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
hostname string
|
hostname string
|
||||||
ctime time.Time
|
ctime time.Time
|
||||||
name string
|
name string
|
||||||
commands chan<- Command
|
|
||||||
password []byte
|
password []byte
|
||||||
users UserNameMap
|
users UserNameMap
|
||||||
channels ChannelNameMap
|
channels ChannelNameMap
|
||||||
|
services ServiceNameMap
|
||||||
|
commands chan<- Command
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServer(name string) *Server {
|
func NewServer(name string) *Server {
|
||||||
@ -29,13 +36,16 @@ func NewServer(name string) *Server {
|
|||||||
commands: commands,
|
commands: commands,
|
||||||
users: make(UserNameMap),
|
users: make(UserNameMap),
|
||||||
channels: make(ChannelNameMap),
|
channels: make(ChannelNameMap),
|
||||||
|
services: make(ServiceNameMap),
|
||||||
}
|
}
|
||||||
go server.receiveCommands(commands)
|
go server.receiveCommands(commands)
|
||||||
|
NewNickServ(server)
|
||||||
return server
|
return server
|
||||||
}
|
}
|
||||||
|
|
||||||
func (server *Server) receiveCommands(commands <-chan Command) {
|
func (server *Server) receiveCommands(commands <-chan Command) {
|
||||||
for command := range commands {
|
for command := range commands {
|
||||||
|
log.Printf("%s %T %+v", server.Id(), command, command)
|
||||||
command.Client().atime = time.Now()
|
command.Client().atime = time.Now()
|
||||||
command.Handle(server)
|
command.Handle(server)
|
||||||
}
|
}
|
||||||
@ -75,10 +85,10 @@ func (s *Server) GetOrMakeChannel(name string) *Channel {
|
|||||||
// Send a message to clients of channels fromClient is a member.
|
// Send a message to clients of channels fromClient is a member.
|
||||||
func (s *Server) InterestedUsers(fromUser *User) UserSet {
|
func (s *Server) InterestedUsers(fromUser *User) UserSet {
|
||||||
users := make(UserSet)
|
users := make(UserSet)
|
||||||
users[fromUser] = true
|
users.Add(fromUser)
|
||||||
for channel := range fromUser.channels {
|
for channel := range fromUser.channels {
|
||||||
for user := range channel.members {
|
for user := range channel.members {
|
||||||
users[user] = true
|
users.Add(user)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,11 +107,6 @@ func (s *Server) tryRegister(c *Client) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) ChangeUserMode(c *Client, modes []string) {
|
|
||||||
// Don't allow any mode changes.
|
|
||||||
c.replies <- RplUModeIs(s, c)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) Id() string {
|
func (s *Server) Id() string {
|
||||||
return s.hostname
|
return s.hostname
|
||||||
}
|
}
|
||||||
@ -110,6 +115,10 @@ func (s *Server) PublicId() string {
|
|||||||
return s.Id()
|
return s.Id()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) Nick() string {
|
||||||
|
return s.name
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) DeleteChannel(channel *Channel) {
|
func (s *Server) DeleteChannel(channel *Channel) {
|
||||||
delete(s.channels, channel.name)
|
delete(s.channels, channel.name)
|
||||||
}
|
}
|
||||||
@ -118,6 +127,10 @@ func (s *Server) DeleteChannel(channel *Channel) {
|
|||||||
// commands
|
// commands
|
||||||
//
|
//
|
||||||
|
|
||||||
|
func (m *UnknownCommand) Handle(s *Server) {
|
||||||
|
m.Client().replies <- ErrUnknownCommand(s, m.command)
|
||||||
|
}
|
||||||
|
|
||||||
func (m *PingCommand) Handle(s *Server) {
|
func (m *PingCommand) Handle(s *Server) {
|
||||||
m.Client().replies <- RplPong(s)
|
m.Client().replies <- RplPong(s)
|
||||||
}
|
}
|
||||||
@ -149,7 +162,7 @@ func (m *NickCommand) Handle(s *Server) {
|
|||||||
c.user.replies <- ErrNoPrivileges(s)
|
c.user.replies <- ErrNoPrivileges(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *UserCommand) Handle(s *Server) {
|
func (m *UserMsgCommand) Handle(s *Server) {
|
||||||
c := m.Client()
|
c := m.Client()
|
||||||
if c.username != "" {
|
if c.username != "" {
|
||||||
c.replies <- ErrAlreadyRegistered(s)
|
c.replies <- ErrAlreadyRegistered(s)
|
||||||
@ -162,19 +175,25 @@ func (m *UserCommand) Handle(s *Server) {
|
|||||||
|
|
||||||
func (m *QuitCommand) Handle(s *Server) {
|
func (m *QuitCommand) Handle(s *Server) {
|
||||||
c := m.Client()
|
c := m.Client()
|
||||||
reply := RplQuit(c, m.message)
|
|
||||||
for user := range s.InterestedUsers(c.user) {
|
user := c.user
|
||||||
user.replies <- reply
|
if user != nil {
|
||||||
|
reply := RplQuit(c, m.message)
|
||||||
|
for user := range s.InterestedUsers(c.user) {
|
||||||
|
user.replies <- reply
|
||||||
|
}
|
||||||
}
|
}
|
||||||
c.conn.Close()
|
c.conn.Close()
|
||||||
user := c.user
|
if user == nil {
|
||||||
user.LogoutClient(c)
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
user.LogoutClient(c)
|
||||||
if !user.HasClients() {
|
if !user.HasClients() {
|
||||||
cmd := &PartChannelCommand{
|
cmd := &PartCommand{
|
||||||
Command: m,
|
BaseCommand: &BaseCommand{c},
|
||||||
}
|
}
|
||||||
for channel := range c.user.channels {
|
for channel := range user.channels {
|
||||||
channel.commands <- cmd
|
channel.commands <- cmd
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -182,27 +201,39 @@ func (m *QuitCommand) Handle(s *Server) {
|
|||||||
|
|
||||||
func (m *JoinCommand) Handle(s *Server) {
|
func (m *JoinCommand) Handle(s *Server) {
|
||||||
c := m.Client()
|
c := m.Client()
|
||||||
|
|
||||||
|
if c.user == nil {
|
||||||
|
for name := range m.channels {
|
||||||
|
c.replies <- ErrNoSuchChannel(s, name)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if m.zero {
|
if m.zero {
|
||||||
cmd := &PartChannelCommand{
|
cmd := &PartCommand{
|
||||||
Command: m,
|
BaseCommand: &BaseCommand{c},
|
||||||
}
|
}
|
||||||
for channel := range c.user.channels {
|
for channel := range c.user.channels {
|
||||||
channel.commands <- cmd
|
channel.commands <- cmd
|
||||||
}
|
}
|
||||||
} else {
|
return
|
||||||
for i, name := range m.channels {
|
}
|
||||||
key := ""
|
|
||||||
if len(m.keys) > i {
|
|
||||||
key = m.keys[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
s.GetOrMakeChannel(name).commands <- &JoinChannelCommand{m, key}
|
for name := range m.channels {
|
||||||
}
|
s.GetOrMakeChannel(name).commands <- m
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *PartCommand) Handle(s *Server) {
|
func (m *PartCommand) Handle(s *Server) {
|
||||||
user := m.Client().user
|
user := m.Client().user
|
||||||
|
|
||||||
|
if user == nil {
|
||||||
|
for _, chname := range m.channels {
|
||||||
|
m.Client().replies <- ErrNoSuchChannel(s, chname)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
for _, chname := range m.channels {
|
for _, chname := range m.channels {
|
||||||
channel := s.channels[chname]
|
channel := s.channels[chname]
|
||||||
|
|
||||||
@ -211,82 +242,60 @@ func (m *PartCommand) Handle(s *Server) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
channel.commands <- &PartChannelCommand{m, m.message}
|
channel.commands <- m
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *TopicCommand) Handle(s *Server) {
|
func (m *TopicCommand) Handle(s *Server) {
|
||||||
user := m.Client().user
|
user := m.Client().user
|
||||||
|
|
||||||
|
if user == nil {
|
||||||
|
m.Client().replies <- ErrNoSuchChannel(s, m.channel)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
channel := s.channels[m.channel]
|
channel := s.channels[m.channel]
|
||||||
if channel == nil {
|
if channel == nil {
|
||||||
user.replies <- ErrNoSuchChannel(s, m.channel)
|
user.replies <- ErrNoSuchChannel(s, m.channel)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.topic == "" {
|
channel.commands <- m
|
||||||
channel.commands <- &GetTopicChannelCommand{m}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
channel.commands <- &SetTopicChannelCommand{m}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *PrivMsgCommand) Handle(s *Server) {
|
func (m *PrivMsgCommand) Handle(s *Server) {
|
||||||
|
service := s.services[m.target]
|
||||||
|
if service != nil {
|
||||||
|
service.commands <- m
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
user := m.Client().user
|
user := m.Client().user
|
||||||
|
if user == nil {
|
||||||
|
m.Client().replies <- ErrNoSuchNick(s, m.target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if m.TargetIsChannel() {
|
if m.TargetIsChannel() {
|
||||||
channel := s.channels[m.target]
|
channel := s.channels[m.target]
|
||||||
if channel == nil {
|
if channel == nil {
|
||||||
user.replies <- ErrNoSuchNick(s, m.target)
|
user.replies <- ErrNoSuchChannel(s, m.target)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
channel.commands <- &PrivMsgChannelCommand{m}
|
channel.commands <- m
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
target := s.users[m.target]
|
target := s.users[m.target]
|
||||||
if target != nil {
|
if target == nil {
|
||||||
target.replies <- ErrNoSuchNick(s, m.target)
|
user.replies <- ErrNoSuchNick(s, m.target)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
target.replies <- RplPrivMsg(user, target, m.message)
|
target.commands <- m
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *LoginCommand) Handle(s *Server) {
|
func (m *ModeCommand) Handle(s *Server) {
|
||||||
client := m.Client()
|
m.Client().replies <- RplUModeIs(s, m.Client())
|
||||||
if client.user != nil {
|
|
||||||
client.replies <- ErrAlreadyRegistered(s)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
user := s.users[m.nick]
|
|
||||||
if user == nil {
|
|
||||||
client.replies <- ErrNoSuchNick(s, m.nick)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !user.Login(client, m.nick, m.password) {
|
|
||||||
client.replies <- ErrRestricted(s)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
client.replies <- RplNick(client, m.nick)
|
|
||||||
// TODO join channels
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *ReserveCommand) Handle(s *Server) {
|
|
||||||
client := m.Client()
|
|
||||||
if client.user != nil {
|
|
||||||
client.replies <- ErrAlreadyRegistered(s)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.users[m.nick] != nil {
|
|
||||||
client.replies <- ErrNickNameInUse(s, m.nick)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
s.users[m.nick] = NewUser(m.nick, m.password, s)
|
|
||||||
}
|
}
|
||||||
|
64
src/irc/service.go
Normal file
64
src/irc/service.go
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
package irc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ServiceCommand interface {
|
||||||
|
Command
|
||||||
|
HandleService(*Service)
|
||||||
|
}
|
||||||
|
|
||||||
|
type PrivMsgCommandFunc func(*PrivMsgCommand)
|
||||||
|
|
||||||
|
type Service struct {
|
||||||
|
server *Server
|
||||||
|
name string
|
||||||
|
commands chan<- ServiceCommand
|
||||||
|
Handle PrivMsgCommandFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewService(s *Server, name string, Handle PrivMsgCommandFunc) *Service {
|
||||||
|
commands := make(chan ServiceCommand)
|
||||||
|
service := &Service{
|
||||||
|
server: s,
|
||||||
|
name: name,
|
||||||
|
commands: commands,
|
||||||
|
Handle: Handle,
|
||||||
|
}
|
||||||
|
go service.receiveCommands(commands)
|
||||||
|
s.services[name] = service
|
||||||
|
return service
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service *Service) receiveCommands(commands <-chan ServiceCommand) {
|
||||||
|
for command := range commands {
|
||||||
|
log.Printf("%s %T %+V", service.Id(), command, command)
|
||||||
|
command.HandleService(service)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service *Service) Id() string {
|
||||||
|
return fmt.Sprintf("%s!%s@%s", service.name, service.name, service.server.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service *Service) PublicId() string {
|
||||||
|
return service.Id()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service *Service) Nick() string {
|
||||||
|
return service.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service *Service) Reply(client *Client, message string) {
|
||||||
|
client.replies <- RplPrivMsg(service, client, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// commands
|
||||||
|
//
|
||||||
|
|
||||||
|
func (m *PrivMsgCommand) HandleService(s *Service) {
|
||||||
|
s.Handle(m)
|
||||||
|
}
|
@ -3,16 +3,22 @@ package irc
|
|||||||
import (
|
import (
|
||||||
"code.google.com/p/go.crypto/bcrypt"
|
"code.google.com/p/go.crypto/bcrypt"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type UserCommand interface {
|
||||||
|
Command
|
||||||
|
HandleUser(*User)
|
||||||
|
}
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
nick string
|
nick string
|
||||||
hash []byte
|
hash []byte
|
||||||
server *Server
|
server *Server
|
||||||
replies chan<- Reply
|
|
||||||
commands <-chan Command
|
|
||||||
clients ClientSet
|
clients ClientSet
|
||||||
channels ChannelSet
|
channels ChannelSet
|
||||||
|
commands chan<- UserCommand
|
||||||
|
replies chan<- Reply
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserSet map[*User]bool
|
type UserSet map[*User]bool
|
||||||
@ -30,21 +36,32 @@ func NewUser(nick string, password string, server *Server) *User {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic("bcrypt failed; cannot generate password hash")
|
panic("bcrypt failed; cannot generate password hash")
|
||||||
}
|
}
|
||||||
|
commands := make(chan UserCommand)
|
||||||
replies := make(chan Reply)
|
replies := make(chan Reply)
|
||||||
user := &User{
|
user := &User{
|
||||||
nick: nick,
|
nick: nick,
|
||||||
hash: hash,
|
hash: hash,
|
||||||
server: server,
|
server: server,
|
||||||
clients: make(ClientSet),
|
clients: make(ClientSet),
|
||||||
replies: replies,
|
channels: make(ChannelSet),
|
||||||
|
replies: replies,
|
||||||
}
|
}
|
||||||
|
go user.receiveCommands(commands)
|
||||||
go user.receiveReplies(replies)
|
go user.receiveReplies(replies)
|
||||||
return user
|
return user
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (user *User) receiveCommands(commands <-chan UserCommand) {
|
||||||
|
for command := range commands {
|
||||||
|
log.Printf("%s %T %+v", user.Id(), command, command)
|
||||||
|
command.HandleUser(user)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Distribute replies to clients.
|
// Distribute replies to clients.
|
||||||
func (user *User) receiveReplies(replies <-chan Reply) {
|
func (user *User) receiveReplies(replies <-chan Reply) {
|
||||||
for reply := range replies {
|
for reply := range replies {
|
||||||
|
log.Printf("%s %T %+v", user.Id(), reply, reply)
|
||||||
for client := range user.clients {
|
for client := range user.clients {
|
||||||
client.replies <- reply
|
client.replies <- reply
|
||||||
}
|
}
|
||||||
@ -82,8 +99,11 @@ func (user *User) Login(c *Client, nick string, password string) bool {
|
|||||||
|
|
||||||
user.clients[c] = true
|
user.clients[c] = true
|
||||||
c.user = user
|
c.user = user
|
||||||
c.replies <- RplNick(c, user.nick)
|
for channel := range user.channels {
|
||||||
// TODO join channels
|
channel.GetTopic(c)
|
||||||
|
c.replies <- RplNamReply(channel)
|
||||||
|
c.replies <- RplEndOfNames(channel.server)
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,3 +118,15 @@ func (user *User) LogoutClient(c *Client) bool {
|
|||||||
func (user *User) HasClients() bool {
|
func (user *User) HasClients() bool {
|
||||||
return len(user.clients) > 0
|
return len(user.clients) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (user *User) Replies() chan<- Reply {
|
||||||
|
return user.replies
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// commands
|
||||||
|
//
|
||||||
|
|
||||||
|
func (m *PrivMsgCommand) HandleUser(user *User) {
|
||||||
|
user.replies <- RplPrivMsg(m.Client(), user, m.message)
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user