2012-04-18 03:16:57 +02:00
|
|
|
package irc
|
|
|
|
|
2012-12-09 21:51:50 +01:00
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"regexp"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
2012-04-18 05:24:26 +02:00
|
|
|
type Message interface {
|
|
|
|
Handle(s *Server, c *Client)
|
|
|
|
}
|
|
|
|
|
2012-12-09 21:51:50 +01:00
|
|
|
var (
|
|
|
|
ErrNotEnoughArgs = errors.New("not enough arguments")
|
|
|
|
ErrUModeUnknownFlag = errors.New("unknown umode flag")
|
2012-12-09 22:47:02 +01:00
|
|
|
parseCommandFuncs = map[string]ParseFunc{
|
|
|
|
"INVITE": NewInviteMessage,
|
|
|
|
"JOIN": NewJoinMessage,
|
|
|
|
"MODE": NewModeMessage,
|
|
|
|
"NICK": NewNickMessage,
|
|
|
|
"PART": NewPartMessage,
|
|
|
|
"PING": NewPingMessage,
|
|
|
|
"PONG": NewPongMessage,
|
|
|
|
"PRIVMSG": NewPrivMsgMessage,
|
|
|
|
"QUIT": NewQuitMessage,
|
|
|
|
"TOPIC": NewTopicMessage,
|
|
|
|
"USER": NewUserMessage,
|
|
|
|
}
|
2012-12-09 21:51:50 +01:00
|
|
|
)
|
|
|
|
|
2012-12-09 07:54:58 +01:00
|
|
|
// unknown
|
|
|
|
|
|
|
|
type UnknownMessage struct {
|
|
|
|
command string
|
|
|
|
}
|
|
|
|
|
2012-12-09 21:51:50 +01:00
|
|
|
// NB: no constructor, created on demand in parser for invalid messages.
|
|
|
|
|
2012-12-09 07:54:58 +01:00
|
|
|
func (m *UnknownMessage) Handle(s *Server, c *Client) {
|
|
|
|
c.send <- ErrUnknownCommand(s, m.command)
|
|
|
|
}
|
|
|
|
|
|
|
|
// PING
|
|
|
|
|
|
|
|
type PingMessage struct {
|
|
|
|
server string
|
|
|
|
server2 string
|
|
|
|
}
|
|
|
|
|
2012-12-09 21:51:50 +01:00
|
|
|
func NewPingMessage(args []string) (Message, error) {
|
|
|
|
if len(args) < 1 {
|
|
|
|
return nil, ErrNotEnoughArgs
|
|
|
|
}
|
|
|
|
msg := &PingMessage{server: args[0]}
|
|
|
|
if len(args) > 1 {
|
|
|
|
msg.server2 = args[1]
|
|
|
|
}
|
|
|
|
return msg, nil
|
|
|
|
}
|
|
|
|
|
2012-12-09 07:54:58 +01:00
|
|
|
func (m *PingMessage) Handle(s *Server, c *Client) {
|
|
|
|
c.send <- RplPong(s)
|
|
|
|
}
|
|
|
|
|
|
|
|
// PONG
|
|
|
|
|
|
|
|
type PongMessage struct {
|
|
|
|
server1 string
|
|
|
|
server2 string
|
|
|
|
}
|
|
|
|
|
2012-12-09 21:51:50 +01:00
|
|
|
func NewPongMessage(args []string) (Message, error) {
|
|
|
|
if len(args) < 1 {
|
|
|
|
return nil, ErrNotEnoughArgs
|
|
|
|
}
|
|
|
|
message := &PongMessage{server1: args[0]}
|
|
|
|
if len(args) > 1 {
|
|
|
|
message.server2 = args[1]
|
|
|
|
}
|
|
|
|
return message, nil
|
|
|
|
}
|
|
|
|
|
2012-12-09 07:54:58 +01:00
|
|
|
func (m *PongMessage) Handle(s *Server, c *Client) {
|
2012-12-09 21:51:50 +01:00
|
|
|
// no-op
|
2012-12-09 07:54:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// NICK
|
|
|
|
|
|
|
|
type NickMessage struct {
|
|
|
|
nickname string
|
|
|
|
}
|
|
|
|
|
2012-12-09 21:51:50 +01:00
|
|
|
func NewNickMessage(args []string) (Message, error) {
|
|
|
|
if len(args) != 1 {
|
|
|
|
return nil, ErrNotEnoughArgs
|
|
|
|
}
|
|
|
|
return &NickMessage{args[0]}, nil
|
|
|
|
}
|
|
|
|
|
2012-04-18 03:16:57 +02:00
|
|
|
func (m *NickMessage) Handle(s *Server, c *Client) {
|
2012-12-09 07:54:58 +01:00
|
|
|
s.ChangeNick(c, m.nickname)
|
|
|
|
}
|
|
|
|
|
|
|
|
// USER
|
|
|
|
|
|
|
|
type UserMessage struct {
|
|
|
|
user string
|
|
|
|
mode uint8
|
|
|
|
unused string
|
|
|
|
realname string
|
2012-04-18 03:16:57 +02:00
|
|
|
}
|
|
|
|
|
2012-12-09 21:51:50 +01:00
|
|
|
func NewUserMessage(args []string) (Message, error) {
|
|
|
|
if len(args) != 4 {
|
|
|
|
return nil, ErrNotEnoughArgs
|
|
|
|
}
|
|
|
|
msg := &UserMessage{
|
|
|
|
user: args[0],
|
|
|
|
unused: args[2],
|
|
|
|
realname: args[3],
|
|
|
|
}
|
|
|
|
mode, err := strconv.ParseUint(args[1], 10, 8)
|
|
|
|
if err == nil {
|
|
|
|
msg.mode = uint8(mode)
|
|
|
|
}
|
|
|
|
return msg, nil
|
|
|
|
}
|
|
|
|
|
2012-04-18 03:16:57 +02:00
|
|
|
func (m *UserMessage) Handle(s *Server, c *Client) {
|
2012-12-09 21:51:50 +01:00
|
|
|
s.UserLogin(c, m.user, m.realname)
|
2012-04-18 03:16:57 +02:00
|
|
|
}
|
|
|
|
|
2012-12-09 07:54:58 +01:00
|
|
|
// QUIT
|
|
|
|
|
|
|
|
type QuitMessage struct {
|
|
|
|
message string
|
2012-04-18 03:16:57 +02:00
|
|
|
}
|
|
|
|
|
2012-12-09 21:51:50 +01:00
|
|
|
func NewQuitMessage(args []string) (Message, error) {
|
|
|
|
msg := &QuitMessage{}
|
|
|
|
if len(args) > 0 {
|
|
|
|
msg.message = args[0]
|
|
|
|
}
|
|
|
|
return msg, nil
|
|
|
|
}
|
|
|
|
|
2012-12-09 07:54:58 +01:00
|
|
|
func (m *QuitMessage) Handle(s *Server, c *Client) {
|
|
|
|
s.Quit(c, m.message)
|
2012-04-18 03:16:57 +02:00
|
|
|
}
|
|
|
|
|
2012-12-09 07:54:58 +01:00
|
|
|
// MODE
|
|
|
|
|
|
|
|
type ModeMessage struct {
|
|
|
|
nickname string
|
|
|
|
modes []string
|
2012-04-18 03:16:57 +02:00
|
|
|
}
|
|
|
|
|
2012-12-09 21:51:50 +01:00
|
|
|
var MODE_RE = regexp.MustCompile("^[-+][a-zA-Z]+$")
|
|
|
|
|
|
|
|
func NewModeMessage(args []string) (Message, error) {
|
|
|
|
if len(args) < 1 {
|
|
|
|
return nil, ErrNotEnoughArgs
|
|
|
|
}
|
|
|
|
msg := &ModeMessage{
|
|
|
|
nickname: args[0],
|
|
|
|
}
|
|
|
|
for _, arg := range args[1:] {
|
|
|
|
if !MODE_RE.MatchString(arg) {
|
|
|
|
return nil, ErrUModeUnknownFlag
|
|
|
|
}
|
|
|
|
prefix := arg[0]
|
|
|
|
for _, c := range arg[1:] {
|
|
|
|
mode := fmt.Sprintf("%c%c", prefix, c)
|
|
|
|
msg.modes = append(msg.modes, mode)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return msg, nil
|
|
|
|
}
|
|
|
|
|
2012-04-18 05:24:26 +02:00
|
|
|
func (m *ModeMessage) Handle(s *Server, c *Client) {
|
|
|
|
if m.nickname != c.nick {
|
2012-12-09 07:54:58 +01:00
|
|
|
c.send <- ErrUsersDontMatch(s)
|
2012-04-18 05:24:26 +02:00
|
|
|
return
|
|
|
|
}
|
2012-12-09 07:54:58 +01:00
|
|
|
s.ChangeUserMode(c, m.modes)
|
|
|
|
}
|
|
|
|
|
|
|
|
// JOIN
|
|
|
|
|
|
|
|
type JoinMessage struct {
|
|
|
|
channels []string
|
|
|
|
keys []string
|
|
|
|
zero bool
|
|
|
|
}
|
|
|
|
|
2012-12-09 21:51:50 +01:00
|
|
|
func NewJoinMessage(args []string) (Message, error) {
|
|
|
|
msg := &JoinMessage{}
|
|
|
|
if len(args) > 0 {
|
|
|
|
if args[0] == "0" {
|
|
|
|
msg.zero = true
|
|
|
|
} else {
|
|
|
|
msg.channels = strings.Split(args[0], ",")
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(args) > 1 {
|
|
|
|
msg.keys = strings.Split(args[1], ",")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return msg, nil
|
|
|
|
}
|
|
|
|
|
2012-12-09 07:54:58 +01:00
|
|
|
func (m *JoinMessage) Handle(s *Server, c *Client) {
|
|
|
|
if m.zero {
|
|
|
|
for channel := range c.channels {
|
|
|
|
channel.Part(c, "")
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for i, name := range m.channels {
|
|
|
|
key := ""
|
|
|
|
if len(m.keys) > i {
|
|
|
|
key = m.keys[i]
|
|
|
|
}
|
|
|
|
|
|
|
|
s.GetOrMakeChannel(name).Join(c, key)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// PART
|
|
|
|
|
|
|
|
type PartMessage struct {
|
|
|
|
channels []string
|
|
|
|
message string
|
|
|
|
}
|
|
|
|
|
2012-12-09 21:51:50 +01:00
|
|
|
func NewPartMessage(args []string) (Message, error) {
|
|
|
|
if len(args) < 1 {
|
|
|
|
return nil, ErrNotEnoughArgs
|
|
|
|
}
|
|
|
|
msg := &PartMessage{channels: strings.Split(args[0], ",")}
|
|
|
|
if len(args) > 1 {
|
|
|
|
msg.message = args[1]
|
|
|
|
}
|
|
|
|
return msg, nil
|
|
|
|
}
|
|
|
|
|
2012-12-09 07:54:58 +01:00
|
|
|
func (m *PartMessage) Handle(s *Server, c *Client) {
|
|
|
|
for _, chname := range m.channels {
|
|
|
|
channel := s.channels[chname]
|
|
|
|
|
|
|
|
if channel == nil {
|
|
|
|
c.send <- ErrNoSuchChannel(s, chname)
|
|
|
|
continue
|
2012-04-18 05:24:26 +02:00
|
|
|
}
|
2012-12-09 07:54:58 +01:00
|
|
|
|
|
|
|
channel.Part(c, m.message)
|
2012-04-18 05:24:26 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-12-09 07:54:58 +01:00
|
|
|
// PRIVMSG
|
|
|
|
|
|
|
|
type PrivMsgMessage struct {
|
|
|
|
target string
|
|
|
|
message string
|
|
|
|
}
|
|
|
|
|
2012-12-09 21:51:50 +01:00
|
|
|
func NewPrivMsgMessage(args []string) (Message, error) {
|
|
|
|
if len(args) < 2 {
|
|
|
|
return nil, ErrNotEnoughArgs
|
|
|
|
}
|
|
|
|
return &PrivMsgMessage{
|
|
|
|
target: args[0],
|
|
|
|
message: args[1],
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2012-12-09 07:54:58 +01:00
|
|
|
func (m *PrivMsgMessage) TargetIsChannel() bool {
|
|
|
|
switch m.target[0] {
|
|
|
|
case '&', '#', '+', '!':
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *PrivMsgMessage) Handle(s *Server, c *Client) {
|
|
|
|
if m.TargetIsChannel() {
|
|
|
|
channel := s.channels[m.target]
|
|
|
|
if channel != nil {
|
|
|
|
channel.PrivMsg(c, m.message)
|
|
|
|
} else {
|
|
|
|
c.send <- ErrNoSuchNick(s, m.target)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
client := s.nicks[m.target]
|
|
|
|
if client != nil {
|
|
|
|
client.send <- RplPrivMsg(client, m.message)
|
|
|
|
} else {
|
|
|
|
c.send <- ErrNoSuchNick(s, m.target)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TOPIC
|
|
|
|
|
|
|
|
type TopicMessage struct {
|
|
|
|
channel string
|
|
|
|
topic string
|
|
|
|
}
|
|
|
|
|
2012-12-09 21:51:50 +01:00
|
|
|
func NewTopicMessage(args []string) (Message, error) {
|
|
|
|
if len(args) < 1 {
|
|
|
|
return nil, ErrNotEnoughArgs
|
|
|
|
}
|
|
|
|
msg := &TopicMessage{channel: args[0]}
|
|
|
|
if len(args) > 1 {
|
|
|
|
msg.topic = args[1]
|
|
|
|
}
|
|
|
|
return msg, nil
|
|
|
|
}
|
|
|
|
|
2012-12-09 07:54:58 +01:00
|
|
|
func (m *TopicMessage) Handle(s *Server, c *Client) {
|
|
|
|
channel := s.channels[m.channel]
|
|
|
|
if channel == nil {
|
|
|
|
c.send <- ErrNoSuchChannel(s, m.channel)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if m.topic == "" {
|
|
|
|
channel.GetTopic(c)
|
|
|
|
} else {
|
|
|
|
channel.ChangeTopic(c, m.topic)
|
2012-04-18 03:16:57 +02:00
|
|
|
}
|
|
|
|
}
|
2012-12-09 22:47:02 +01:00
|
|
|
|
|
|
|
// INVITE <nickname> <channel>
|
|
|
|
|
|
|
|
type InviteMessage struct {
|
|
|
|
nickname string
|
|
|
|
channel string
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewInviteMessage(args []string) (Message, error) {
|
|
|
|
if len(args) < 2 {
|
|
|
|
return nil, ErrNotEnoughArgs
|
|
|
|
}
|
|
|
|
return &InviteMessage{
|
|
|
|
nickname: args[0],
|
|
|
|
channel: args[1],
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *InviteMessage) Handle(s *Server, c *Client) {
|
|
|
|
channel := s.channels[m.channel]
|
|
|
|
if channel == nil {
|
|
|
|
c.send <- ErrNoSuchNick(s, m.channel)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
invitee := s.nicks[m.nickname]
|
|
|
|
if invitee == nil {
|
|
|
|
c.send <- ErrNoSuchNick(s, m.nickname)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
channel.Invite(c, invitee)
|
|
|
|
}
|