mirror of
https://github.com/ergochat/ergo.git
synced 2024-11-25 21:39:25 +01:00
I think I get IRC protocol now.
This commit is contained in:
parent
26ef45290a
commit
2cbf65564e
2
build.sh
2
build.sh
@ -1,2 +1,2 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
env GOPATH="$PWD" go install ergonomadic
|
env GOPATH="$PWD" go install -v ergonomadic
|
||||||
|
@ -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) {
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,7 +106,8 @@ func NewPassMessage(args []string) (Message, error) {
|
|||||||
return nil, NotEnoughArgsError
|
return nil, NotEnoughArgsError
|
||||||
}
|
}
|
||||||
return &PassMessage{
|
return &PassMessage{
|
||||||
password: args[0],
|
BaseMessage: &BaseMessage{},
|
||||||
|
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,9 +155,10 @@ func NewUserMessage(args []string) (Message, error) {
|
|||||||
return nil, NotEnoughArgsError
|
return nil, NotEnoughArgsError
|
||||||
}
|
}
|
||||||
msg := &UserMessage{
|
msg := &UserMessage{
|
||||||
user: args[0],
|
BaseMessage: &BaseMessage{},
|
||||||
unused: args[2],
|
user: args[0],
|
||||||
realname: args[3],
|
unused: args[2],
|
||||||
|
realname: args[3],
|
||||||
}
|
}
|
||||||
mode, err := strconv.ParseUint(args[1], 10, 8)
|
mode, err := strconv.ParseUint(args[1], 10, 8)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@ -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,7 +235,8 @@ func NewModeMessage(args []string) (Message, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
msg := &ModeMessage{
|
msg := &ModeMessage{
|
||||||
nickname: args[0],
|
BaseMessage: &BaseMessage{},
|
||||||
|
nickname: args[0],
|
||||||
}
|
}
|
||||||
for _, arg := range args[1:] {
|
for _, arg := range args[1:] {
|
||||||
if !MODE_RE.MatchString(arg) {
|
if !MODE_RE.MatchString(arg) {
|
||||||
@ -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,8 +361,9 @@ func NewPrivMsgMessage(args []string) (Message, error) {
|
|||||||
return nil, NotEnoughArgsError
|
return nil, NotEnoughArgsError
|
||||||
}
|
}
|
||||||
return &PrivMsgMessage{
|
return &PrivMsgMessage{
|
||||||
target: args[0],
|
BaseMessage: &BaseMessage{},
|
||||||
message: args[1],
|
target: args[0],
|
||||||
|
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{},
|
||||||
password: args[1],
|
nick: args[0],
|
||||||
|
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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"
|
||||||
)
|
)
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
131
src/irc/reply.go
131
src/irc/reply.go
@ -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")
|
||||||
}
|
}
|
||||||
|
@ -7,23 +7,17 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
hostname string
|
hostname string
|
||||||
ctime time.Time
|
ctime time.Time
|
||||||
name string
|
name string
|
||||||
recv chan<- *ClientMessage
|
recv chan<- Message
|
||||||
nicks map[string]*Client
|
password string
|
||||||
channels map[string]*Channel
|
nicks map[string]*Client
|
||||||
password string
|
channels map[string]*Channel
|
||||||
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
5
src/irc/user.go
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
package irc
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
nick string
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user