3
0
mirror of https://github.com/ergochat/ergo.git synced 2024-11-14 07:59:31 +01:00

I think I get IRC protocol now.

This commit is contained in:
Jeremy Latt 2012-12-12 23:27:17 -08:00
parent 26ef45290a
commit 2cbf65564e
9 changed files with 208 additions and 147 deletions

View File

@ -1,2 +1,2 @@
#!/bin/bash #!/bin/bash
env GOPATH="$PWD" go install ergonomadic env GOPATH="$PWD" go install -v ergonomadic

View File

@ -79,6 +79,10 @@ func (ch *Channel) Part(cl *Client, message string) {
delete(ch.members, cl) delete(ch.members, cl)
delete(cl.channels, ch) delete(cl.channels, ch)
if len(ch.members) == 0 {
ch.server.DeleteChannel(ch)
}
} }
func (ch *Channel) PrivMsg(cl *Client, message string) { func (ch *Channel) PrivMsg(cl *Client, message string) {

View File

@ -10,7 +10,6 @@ import (
type Client struct { type Client struct {
conn net.Conn conn net.Conn
send chan<- Reply send chan<- Reply
recv <-chan string
username string username string
realname string realname string
hostname string hostname string
@ -27,39 +26,47 @@ type Client struct {
type ClientSet map[*Client]bool type ClientSet map[*Client]bool
func NewClient(server *Server, conn net.Conn) *Client { func NewClient(server *Server, conn net.Conn) *Client {
read := StringReadChan(conn)
write := StringWriteChan(conn)
send := make(chan Reply)
client := &Client{ client := &Client{
channels: make(ChannelSet), channels: make(ChannelSet),
conn: conn, conn: conn,
hostname: LookupHostname(conn.RemoteAddr()), hostname: LookupHostname(conn.RemoteAddr()),
recv: StringReadChan(conn),
server: server, server: server,
send: send,
} }
client.SetReplyToStringChan()
// Connect the conn to the server.
go client.readConn(read)
// Connect the reply channel to the conn.
go client.writeConn(write, send)
return client return client
} }
func (c *Client) SetReplyToStringChan() { func (c *Client) readConn(recv <-chan string) {
send := make(chan Reply) for str := range recv {
write := StringWriteChan(c.conn) log.Printf("%s > %s", c.Id(), str)
go func() {
for reply := range send {
replyStr := reply.String(c)
log.Printf("%s <- %s", c.Id(), replyStr)
write <- replyStr
}
}()
c.send = send
}
// Adapt `chan string` to a `chan Message`.
func (c *Client) Communicate() {
for str := range c.recv {
m, err := ParseMessage(str) m, err := ParseMessage(str)
if err != nil { if err != nil {
// TODO handle error // TODO handle error
return continue
} }
c.server.recv <- &ClientMessage{c, m}
m.SetClient(c)
c.server.recv <- m
}
}
func (c *Client) writeConn(write chan<- string, send <-chan Reply) {
for reply := range send {
replyStr := reply.String(c)
log.Printf("%s < %s", c.Id(), replyStr)
write <- replyStr
} }
} }

View File

@ -10,6 +10,8 @@ import (
type Message interface { type Message interface {
Handle(s *Server, c *Client) Handle(s *Server, c *Client)
Client() *Client
SetClient(c *Client)
} }
var ( var (
@ -17,9 +19,22 @@ var (
UModeUnknownFlagError = errors.New("unknown umode flag") UModeUnknownFlagError = errors.New("unknown umode flag")
) )
// unknown type BaseMessage struct {
client *Client
}
func (m *BaseMessage) Client() *Client {
return m.client
}
func (m *BaseMessage) SetClient(c *Client) {
m.client = c
}
// unknown <command> [args...]
type UnknownMessage struct { type UnknownMessage struct {
*BaseMessage
command string command string
args []string args []string
} }
@ -30,9 +45,10 @@ func (m *UnknownMessage) Handle(s *Server, c *Client) {
c.send <- ErrUnknownCommand(s, m.command) c.send <- ErrUnknownCommand(s, m.command)
} }
// PING // PING <server1> [ <server2> ]
type PingMessage struct { type PingMessage struct {
*BaseMessage
server string server string
server2 string server2 string
} }
@ -41,7 +57,10 @@ func NewPingMessage(args []string) (Message, error) {
if len(args) < 1 { if len(args) < 1 {
return nil, NotEnoughArgsError return nil, NotEnoughArgsError
} }
msg := &PingMessage{server: args[0]} msg := &PingMessage{
BaseMessage: &BaseMessage{},
server: args[0],
}
if len(args) > 1 { if len(args) > 1 {
msg.server2 = args[1] msg.server2 = args[1]
} }
@ -52,9 +71,10 @@ func (m *PingMessage) Handle(s *Server, c *Client) {
c.send <- RplPong(s) c.send <- RplPong(s)
} }
// PONG // PONG <server> [ <server2> ]
type PongMessage struct { type PongMessage struct {
*BaseMessage
server1 string server1 string
server2 string server2 string
} }
@ -77,6 +97,7 @@ func (m *PongMessage) Handle(s *Server, c *Client) {
// PASS <password> // PASS <password>
type PassMessage struct { type PassMessage struct {
*BaseMessage
password string password string
} }
@ -85,6 +106,7 @@ func NewPassMessage(args []string) (Message, error) {
return nil, NotEnoughArgsError return nil, NotEnoughArgsError
} }
return &PassMessage{ return &PassMessage{
BaseMessage: &BaseMessage{},
password: args[0], password: args[0],
}, nil }, nil
} }
@ -97,9 +119,10 @@ func (m *PassMessage) Handle(s *Server, c *Client) {
} }
} }
// NICK // NICK <nickname>
type NickMessage struct { type NickMessage struct {
*BaseMessage
nickname string nickname string
} }
@ -107,16 +130,20 @@ func NewNickMessage(args []string) (Message, error) {
if len(args) != 1 { if len(args) != 1 {
return nil, NotEnoughArgsError return nil, NotEnoughArgsError
} }
return &NickMessage{args[0]}, nil return &NickMessage{
BaseMessage: &BaseMessage{},
nickname: args[0],
}, nil
} }
func (m *NickMessage) Handle(s *Server, c *Client) { func (m *NickMessage) Handle(s *Server, c *Client) {
s.ChangeNick(c, m.nickname) s.ChangeNick(c, m.nickname)
} }
// USER // USER <user> <mode> <unused> <realname>
type UserMessage struct { type UserMessage struct {
*BaseMessage
user string user string
mode uint8 mode uint8
unused string unused string
@ -128,6 +155,7 @@ func NewUserMessage(args []string) (Message, error) {
return nil, NotEnoughArgsError return nil, NotEnoughArgsError
} }
msg := &UserMessage{ msg := &UserMessage{
BaseMessage: &BaseMessage{},
user: args[0], user: args[0],
unused: args[2], unused: args[2],
realname: args[3], realname: args[3],
@ -146,11 +174,14 @@ func (m *UserMessage) Handle(s *Server, c *Client) {
// QUIT [ <Quit Message> ] // QUIT [ <Quit Message> ]
type QuitMessage struct { type QuitMessage struct {
*BaseMessage
message string message string
} }
func NewQuitMessage(args []string) (Message, error) { func NewQuitMessage(args []string) (Message, error) {
msg := &QuitMessage{} msg := &QuitMessage{
BaseMessage: &BaseMessage{},
}
if len(args) > 0 { if len(args) > 0 {
msg.message = args[0] msg.message = args[0]
} }
@ -164,6 +195,7 @@ func (m *QuitMessage) Handle(s *Server, c *Client) {
// MODE <nickname> *( ( "+" / "-" ) *( "i" / "w" / "o" / "O" / "r" ) ) // MODE <nickname> *( ( "+" / "-" ) *( "i" / "w" / "o" / "O" / "r" ) )
type ModeMessage struct { type ModeMessage struct {
*BaseMessage
nickname string nickname string
modes []string modes []string
} }
@ -203,6 +235,7 @@ func NewModeMessage(args []string) (Message, error) {
} }
msg := &ModeMessage{ msg := &ModeMessage{
BaseMessage: &BaseMessage{},
nickname: args[0], nickname: args[0],
} }
for _, arg := range args[1:] { for _, arg := range args[1:] {
@ -238,13 +271,16 @@ func (m *ChannelModeMessage) Handle(s *Server, c *Client) {
// JOIN ( <channel> *( "," <channel> ) [ <key> *( "," <key> ) ] ) / "0" // JOIN ( <channel> *( "," <channel> ) [ <key> *( "," <key> ) ] ) / "0"
type JoinMessage struct { type JoinMessage struct {
*BaseMessage
channels []string channels []string
keys []string keys []string
zero bool zero bool
} }
func NewJoinMessage(args []string) (Message, error) { func NewJoinMessage(args []string) (Message, error) {
msg := &JoinMessage{} msg := &JoinMessage{
BaseMessage: &BaseMessage{},
}
if len(args) > 0 { if len(args) > 0 {
if args[0] == "0" { if args[0] == "0" {
msg.zero = true msg.zero = true
@ -276,9 +312,10 @@ func (m *JoinMessage) Handle(s *Server, c *Client) {
} }
} }
// PART // PART <channel> *( "," <channel> ) [ <Part Message> ]
type PartMessage struct { type PartMessage struct {
*BaseMessage
channels []string channels []string
message string message string
} }
@ -287,7 +324,11 @@ func NewPartMessage(args []string) (Message, error) {
if len(args) < 1 { if len(args) < 1 {
return nil, NotEnoughArgsError return nil, NotEnoughArgsError
} }
msg := &PartMessage{channels: strings.Split(args[0], ",")} msg := &PartMessage{
BaseMessage: &BaseMessage{},
channels: strings.Split(args[0], ","),
}
if len(args) > 1 { if len(args) > 1 {
msg.message = args[1] msg.message = args[1]
} }
@ -307,9 +348,10 @@ func (m *PartMessage) Handle(s *Server, c *Client) {
} }
} }
// PRIVMSG // PRIVMSG <target> <message>
type PrivMsgMessage struct { type PrivMsgMessage struct {
*BaseMessage
target string target string
message string message string
} }
@ -319,6 +361,7 @@ func NewPrivMsgMessage(args []string) (Message, error) {
return nil, NotEnoughArgsError return nil, NotEnoughArgsError
} }
return &PrivMsgMessage{ return &PrivMsgMessage{
BaseMessage: &BaseMessage{},
target: args[0], target: args[0],
message: args[1], message: args[1],
}, nil }, nil
@ -350,6 +393,7 @@ func (m *PrivMsgMessage) Handle(s *Server, c *Client) {
// TOPIC [newtopic] // TOPIC [newtopic]
type TopicMessage struct { type TopicMessage struct {
*BaseMessage
channel string channel string
topic string topic string
} }
@ -358,7 +402,10 @@ func NewTopicMessage(args []string) (Message, error) {
if len(args) < 1 { if len(args) < 1 {
return nil, NotEnoughArgsError return nil, NotEnoughArgsError
} }
msg := &TopicMessage{channel: args[0]} msg := &TopicMessage{
BaseMessage: &BaseMessage{},
channel: args[0],
}
if len(args) > 1 { if len(args) > 1 {
msg.topic = args[1] msg.topic = args[1]
} }
@ -378,27 +425,25 @@ func (m *TopicMessage) Handle(s *Server, c *Client) {
} }
} }
// OPER <name> <password> // LOGIN <nick> <password>
type OperMessage struct { type LoginMessage struct {
name string *BaseMessage
nick string
password string password string
} }
func NewOperMessage(args []string) (Message, error) { func NewLoginMessage(args []string) (Message, error) {
if len(args) < 2 { if len(args) < 2 {
return nil, NotEnoughArgsError return nil, NotEnoughArgsError
} }
return &OperMessage{ return &LoginMessage{
name: args[0], BaseMessage: &BaseMessage{},
nick: args[0],
password: args[1], password: args[1],
}, nil }, nil
} }
func (m *OperMessage) Handle(s *Server, c *Client) { func (m *LoginMessage) Handle(s *Server, c *Client) {
if s.operators[m.name] == m.password { // TODO
c.send <- RplYoureOper(s)
} else {
c.send <- ErrPasswdMismatch(s)
}
} }

View File

@ -149,4 +149,5 @@ const (
RPL_PART = "PART" RPL_PART = "PART"
RPL_PONG = "PONG" RPL_PONG = "PONG"
RPL_PRIVMSG = "PRIVMSG" RPL_PRIVMSG = "PRIVMSG"
RPL_QUIT = "QUIT"
) )

View File

@ -12,6 +12,7 @@ var (
parseCommandFuncs = map[string]ParseFunc{ parseCommandFuncs = map[string]ParseFunc{
"JOIN": NewJoinMessage, "JOIN": NewJoinMessage,
"MODE": NewModeMessage, "MODE": NewModeMessage,
"LOGIN": NewLoginMessage,
"NICK": NewNickMessage, "NICK": NewNickMessage,
"PART": NewPartMessage, "PART": NewPartMessage,
"PASS": NewPassMessage, "PASS": NewPassMessage,
@ -21,7 +22,6 @@ var (
"QUIT": NewQuitMessage, "QUIT": NewQuitMessage,
"TOPIC": NewTopicMessage, "TOPIC": NewTopicMessage,
"USER": NewUserMessage, "USER": NewUserMessage,
"OPER": NewOperMessage,
} }
) )
@ -29,7 +29,11 @@ func ParseMessage(line string) (Message, error) {
command, args := parseLine(line) command, args := parseLine(line)
constructor, ok := parseCommandFuncs[command] constructor, ok := parseCommandFuncs[command]
if !ok { if !ok {
return &UnknownMessage{command, args}, nil return &UnknownMessage{
BaseMessage: &BaseMessage{},
command: command,
args: args,
}, nil
} }
return constructor(args) return constructor(args)
} }

View File

@ -20,175 +20,178 @@ type BasicReply struct {
message string message string
} }
func NewBasicReply(source Identifier, code string, message string) *BasicReply {
fullMessage := fmt.Sprintf(":%s %s %s\r\n", source.Id(), code, message)
return &BasicReply{source, code, fullMessage}
}
func (reply *BasicReply) String(client *Client) string { func (reply *BasicReply) String(client *Client) string {
return reply.message
}
type NumericReply struct {
*BasicReply
}
func NewNumericReply(source Identifier, code string, message string) *NumericReply {
return &NumericReply{&BasicReply{source, code, message}}
}
func (reply *NumericReply) String(client *Client) string {
return fmt.Sprintf(":%s %s %s %s\r\n", reply.source.Id(), reply.code, client.Nick(), return fmt.Sprintf(":%s %s %s %s\r\n", reply.source.Id(), reply.code, client.Nick(),
reply.message) reply.message)
} }
type ChannelReply struct {
*BasicReply
channel *Channel
}
func (reply *ChannelReply) String(client *Client) string {
return fmt.Sprintf(":%s %s %s %s\r\n", reply.source.Id(), reply.code, reply.channel.name,
reply.message)
}
func NewReply(source Identifier, code string, message string) *BasicReply {
return &BasicReply{source, code, message}
}
// messaging // messaging
func RplPrivMsg(source *Client, message string) Reply { func RplPrivMsg(source *Client, message string) Reply {
return NewReply(source, RPL_PRIVMSG, ":"+message) return NewNumericReply(source, RPL_PRIVMSG, ":"+message)
} }
func RplNick(client *Client, newNick string) Reply { func RplNick(client *Client, newNick string) Reply {
return NewReply(client, RPL_NICK, ":"+newNick) return NewBasicReply(client, RPL_NICK, newNick)
} }
func RplInviteMsg(channel *Channel, inviter *Client) Reply { func RplPrivMsgChannel(channel *Channel, source *Client, message string) Reply {
return NewReply(inviter, RPL_INVITE, channel.name) return NewBasicReply(source, RPL_PRIVMSG, fmt.Sprintf("%s :%s", channel.name, message))
} }
func RplInvitingMsg(channel *Channel, invitee *Client) Reply { func RplJoin(channel *Channel, client *Client) Reply {
return NewReply(channel.server, RPL_INVITING, return NewBasicReply(client, RPL_JOIN, channel.name)
fmt.Sprintf("%s %s", channel.name, invitee.Nick())) }
func RplPart(channel *Channel, client *Client, message string) Reply {
return NewBasicReply(client, RPL_PART, fmt.Sprintf("%s :%s", channel.name, message))
}
func RplPong(server *Server) Reply {
return NewBasicReply(server, RPL_PONG, server.Id())
}
func RplQuit(client *Client, message string) Reply {
return NewBasicReply(client, RPL_QUIT, ":"+message)
} }
// Server Info // Server Info
func RplWelcome(source Identifier, client *Client) Reply { func RplWelcome(source Identifier, client *Client) Reply {
return NewReply(source, RPL_WELCOME, return NewNumericReply(source, RPL_WELCOME,
"Welcome to the Internet Relay Network "+client.Id()) "Welcome to the Internet Relay Network "+client.Id())
} }
func RplYourHost(server *Server, target *Client) Reply { func RplYourHost(server *Server, target *Client) Reply {
return NewReply(server, RPL_YOURHOST, return NewNumericReply(server, RPL_YOURHOST,
fmt.Sprintf("Your host is %s, running version %s", server.hostname, VERSION)) fmt.Sprintf("Your host is %s, running version %s", server.hostname, VERSION))
} }
func RplCreated(server *Server) Reply { func RplCreated(server *Server) Reply {
return NewReply(server, RPL_CREATED, return NewNumericReply(server, RPL_CREATED,
"This server was created "+server.ctime.Format(time.RFC1123)) "This server was created "+server.ctime.Format(time.RFC1123))
} }
func RplMyInfo(server *Server) Reply { func RplMyInfo(server *Server) Reply {
return NewReply(server, RPL_MYINFO, return NewNumericReply(server, RPL_MYINFO,
fmt.Sprintf("%s %s w kn", server.name, VERSION)) fmt.Sprintf("%s %s w kn", server.name, VERSION))
} }
func RplUModeIs(server *Server, client *Client) Reply { func RplUModeIs(server *Server, client *Client) Reply {
return NewReply(server, RPL_UMODEIS, client.UModeString()) return NewNumericReply(server, RPL_UMODEIS, client.UModeString())
} }
// channel operations // numeric replies
func RplPrivMsgChannel(channel *Channel, source *Client, message string) Reply {
return &ChannelReply{NewReply(source, RPL_PRIVMSG, ":"+message), channel}
}
func RplJoin(channel *Channel, client *Client) Reply {
return &ChannelReply{NewReply(client, RPL_JOIN, ""), channel}
}
func RplPart(channel *Channel, client *Client, message string) Reply {
return &ChannelReply{NewReply(client, RPL_PART, ":"+message), channel}
}
func RplNoTopic(channel *Channel) Reply { func RplNoTopic(channel *Channel) Reply {
return NewReply(channel.server, RPL_NOTOPIC, channel.name+" :No topic is set") return NewNumericReply(channel.server, RPL_NOTOPIC, channel.name+" :No topic is set")
} }
func RplTopic(channel *Channel) Reply { func RplTopic(channel *Channel) Reply {
return NewReply(channel.server, RPL_TOPIC, fmt.Sprintf("%s :%s", channel.name, channel.topic)) return NewNumericReply(channel.server, RPL_TOPIC, fmt.Sprintf("%s :%s", channel.name, channel.topic))
} }
// server info func RplInviteMsg(channel *Channel, inviter *Client) Reply {
return NewNumericReply(inviter, RPL_INVITE, channel.name)
}
func RplInvitingMsg(channel *Channel, invitee *Client) Reply {
return NewNumericReply(channel.server, RPL_INVITING,
fmt.Sprintf("%s %s", channel.name, invitee.Nick()))
}
func RplNamReply(channel *Channel) Reply { func RplNamReply(channel *Channel) Reply {
// TODO multiple names and splitting based on message size // TODO multiple names and splitting based on message size
return NewReply(channel.server, RPL_NAMREPLY, return NewNumericReply(channel.server, RPL_NAMREPLY,
fmt.Sprintf("= %s :%s", channel.name, strings.Join(channel.Nicks(), " "))) fmt.Sprintf("= %s :%s", channel.name, strings.Join(channel.Nicks(), " ")))
} }
func RplEndOfNames(source Identifier) Reply { func RplEndOfNames(source Identifier) Reply {
return NewReply(source, RPL_ENDOFNAMES, ":End of NAMES list") return NewNumericReply(source, RPL_ENDOFNAMES, ":End of NAMES list")
} }
func RplPong(server *Server) Reply {
return NewReply(server, RPL_PONG, server.Id())
}
// server functions
func RplYoureOper(server *Server) Reply { func RplYoureOper(server *Server) Reply {
return NewReply(server, RPL_YOUREOPER, ":You are now an IRC operator") return NewNumericReply(server, RPL_YOUREOPER, ":You are now an IRC operator")
} }
// errors // errors
func ErrAlreadyRegistered(source Identifier) Reply { func ErrAlreadyRegistered(source Identifier) Reply {
return NewReply(source, ERR_ALREADYREGISTRED, ":You may not reregister") return NewNumericReply(source, ERR_ALREADYREGISTRED, ":You may not reregister")
} }
func ErrNickNameInUse(source Identifier, nick string) Reply { func ErrNickNameInUse(source Identifier, nick string) Reply {
return NewReply(source, ERR_NICKNAMEINUSE, return NewNumericReply(source, ERR_NICKNAMEINUSE,
nick+" :Nickname is already in use") nick+" :Nickname is already in use")
} }
func ErrUnknownCommand(source Identifier, command string) Reply { func ErrUnknownCommand(source Identifier, command string) Reply {
return NewReply(source, ERR_UNKNOWNCOMMAND, return NewNumericReply(source, ERR_UNKNOWNCOMMAND,
command+" :Unknown command") command+" :Unknown command")
} }
func ErrUsersDontMatch(source Identifier) Reply { func ErrUsersDontMatch(source Identifier) Reply {
return NewReply(source, ERR_USERSDONTMATCH, return NewNumericReply(source, ERR_USERSDONTMATCH,
":Cannot change mode for other users") ":Cannot change mode for other users")
} }
func ErrNeedMoreParams(source Identifier, command string) Reply { func ErrNeedMoreParams(source Identifier, command string) Reply {
return NewReply(source, ERR_NEEDMOREPARAMS, return NewNumericReply(source, ERR_NEEDMOREPARAMS,
command+"%s :Not enough parameters") command+"%s :Not enough parameters")
} }
func ErrNoSuchChannel(source Identifier, channel string) Reply { func ErrNoSuchChannel(source Identifier, channel string) Reply {
return NewReply(source, ERR_NOSUCHCHANNEL, return NewNumericReply(source, ERR_NOSUCHCHANNEL,
channel+" :No such channel") channel+" :No such channel")
} }
func ErrUserOnChannel(channel *Channel, member *Client) Reply { func ErrUserOnChannel(channel *Channel, member *Client) Reply {
return NewReply(channel.server, ERR_USERONCHANNEL, return NewNumericReply(channel.server, ERR_USERONCHANNEL,
fmt.Sprintf("%s %s :is already on channel", member.nick, channel.name)) fmt.Sprintf("%s %s :is already on channel", member.nick, channel.name))
} }
func ErrNotOnChannel(channel *Channel) Reply { func ErrNotOnChannel(channel *Channel) Reply {
return NewReply(channel.server, ERR_NOTONCHANNEL, return NewNumericReply(channel.server, ERR_NOTONCHANNEL,
channel.name+" :You're not on that channel") channel.name+" :You're not on that channel")
} }
func ErrInviteOnlyChannel(channel *Channel) Reply { func ErrInviteOnlyChannel(channel *Channel) Reply {
return NewReply(channel.server, ERR_INVITEONLYCHAN, return NewNumericReply(channel.server, ERR_INVITEONLYCHAN,
channel.name+" :Cannot join channel (+i)") channel.name+" :Cannot join channel (+i)")
} }
func ErrBadChannelKey(channel *Channel) Reply { func ErrBadChannelKey(channel *Channel) Reply {
return NewReply(channel.server, ERR_BADCHANNELKEY, return NewNumericReply(channel.server, ERR_BADCHANNELKEY,
channel.name+" :Cannot join channel (+k)") channel.name+" :Cannot join channel (+k)")
} }
func ErrNoSuchNick(source Identifier, nick string) Reply { func ErrNoSuchNick(source Identifier, nick string) Reply {
return NewReply(source, ERR_NOSUCHNICK, return NewNumericReply(source, ERR_NOSUCHNICK,
nick+" :No such nick/channel") nick+" :No such nick/channel")
} }
func ErrPasswdMismatch(server *Server) Reply { func ErrPasswdMismatch(server *Server) Reply {
return NewReply(server, ERR_PASSWDMISMATCH, ":Password incorrect") return NewNumericReply(server, ERR_PASSWDMISMATCH, ":Password incorrect")
} }
func ErrNoChanModes(channel *Channel) Reply { func ErrNoChanModes(channel *Channel) Reply {
return NewReply(channel.server, ERR_NOCHANMODES, return NewNumericReply(channel.server, ERR_NOCHANMODES,
channel.name+" :Channel doesn't support modes") channel.name+" :Channel doesn't support modes")
} }

View File

@ -10,20 +10,14 @@ type Server struct {
hostname string hostname string
ctime time.Time ctime time.Time
name string name string
recv chan<- *ClientMessage recv chan<- Message
password string
nicks map[string]*Client nicks map[string]*Client
channels map[string]*Channel channels map[string]*Channel
password string
operators map[string]string
}
type ClientMessage struct {
client *Client
message Message
} }
func NewServer(name string) *Server { func NewServer(name string) *Server {
recv := make(chan *ClientMessage) recv := make(chan Message)
server := &Server{ server := &Server{
ctime: time.Now(), ctime: time.Now(),
name: name, name: name,
@ -33,9 +27,8 @@ func NewServer(name string) *Server {
} }
go func() { go func() {
for m := range recv { for m := range recv {
log.Printf("%s -> %T%+v", m.client.Id(), m.message, m.message) m.Client().atime = time.Now()
m.client.atime = time.Now() m.Handle(server, m.Client())
m.message.Handle(server, m.client)
} }
}() }()
return server return server
@ -57,7 +50,7 @@ func (s *Server) Listen(addr string) {
continue continue
} }
log.Print("Server.Listen: accepted ", conn.RemoteAddr()) log.Print("Server.Listen: accepted ", conn.RemoteAddr())
go NewClient(s, conn).Communicate() go NewClient(s, conn)
} }
} }
@ -72,13 +65,10 @@ func (s *Server) GetOrMakeChannel(name string) *Channel {
return channel return channel
} }
func (s *Server) AddOperator(name string, password string) {
s.operators[name] = password
}
// 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) SendToInterestedClients(fromClient *Client, reply Reply) { func (s *Server) SendToInterestedClients(fromClient *Client, reply Reply) {
clients := make(map[*Client]bool)
clients := make(ClientSet)
clients[fromClient] = true clients[fromClient] = true
for channel := range fromClient.channels { for channel := range fromClient.channels {
for client := range channel.members { for client := range channel.members {
@ -98,14 +88,12 @@ func (s *Server) ChangeNick(c *Client, newNick string) {
c.send <- ErrNickNameInUse(s, newNick) c.send <- ErrNickNameInUse(s, newNick)
return return
} }
s.SendToInterestedClients(c, RplNick(c, newNick))
if c.nick != "" { if c.nick != "" {
delete(s.nicks, c.nick) delete(s.nicks, c.nick)
} }
s.nicks[newNick] = c s.nicks[newNick] = c
s.SendToInterestedClients(c, RplNick(c, newNick))
c.nick = newNick c.nick = newNick
s.tryRegister(c) s.tryRegister(c)
@ -136,7 +124,7 @@ func (s *Server) Quit(c *Client, message string) {
channel.Part(c, message) channel.Part(c, message)
} }
delete(s.nicks, c.nick) delete(s.nicks, c.nick)
s.SendToInterestedClients(c, RplQuit(c, message))
c.conn.Close() c.conn.Close()
} }
@ -155,3 +143,7 @@ func (s *Server) ChangeUserMode(c *Client, modes []string) {
func (s *Server) Id() string { func (s *Server) Id() string {
return s.hostname return s.hostname
} }
func (s *Server) DeleteChannel(channel *Channel) {
delete(s.channels, channel.name)
}

5
src/irc/user.go Normal file
View File

@ -0,0 +1,5 @@
package irc
type User struct {
nick string
}