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 (
|
2012-12-10 05:24:53 +01:00
|
|
|
NotEnoughArgsError = errors.New("not enough arguments")
|
|
|
|
UModeUnknownFlagError = errors.New("unknown umode flag")
|
2012-12-09 21:51:50 +01:00
|
|
|
)
|
|
|
|
|
2012-12-09 07:54:58 +01:00
|
|
|
// unknown
|
|
|
|
|
|
|
|
type UnknownMessage struct {
|
|
|
|
command string
|
2012-12-12 07:34:22 +01:00
|
|
|
args []string
|
2012-12-09 07:54:58 +01:00
|
|
|
}
|
|
|
|
|
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 {
|
2012-12-10 05:24:53 +01:00
|
|
|
return nil, NotEnoughArgsError
|
2012-12-09 21:51:50 +01:00
|
|
|
}
|
|
|
|
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 {
|
2012-12-10 05:24:53 +01:00
|
|
|
return nil, NotEnoughArgsError
|
2012-12-09 21:51:50 +01:00
|
|
|
}
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2012-12-10 05:24:53 +01:00
|
|
|
// PASS <password>
|
|
|
|
|
|
|
|
type PassMessage struct {
|
|
|
|
password string
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewPassMessage(args []string) (Message, error) {
|
|
|
|
if len(args) < 1 {
|
|
|
|
return nil, NotEnoughArgsError
|
|
|
|
}
|
2012-12-12 07:34:22 +01:00
|
|
|
return &PassMessage{
|
|
|
|
password: args[0],
|
|
|
|
}, nil
|
2012-12-10 05:24:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (m *PassMessage) Handle(s *Server, c *Client) {
|
2012-12-12 07:34:22 +01:00
|
|
|
if m.password == s.password {
|
2012-12-10 05:24:53 +01:00
|
|
|
c.serverPass = true
|
|
|
|
} else {
|
2012-12-12 07:34:22 +01:00
|
|
|
c.send <- ErrPasswdMismatch(s)
|
2012-12-10 05:24:53 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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 {
|
2012-12-10 05:24:53 +01:00
|
|
|
return nil, NotEnoughArgsError
|
2012-12-09 21:51:50 +01:00
|
|
|
}
|
|
|
|
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 {
|
2012-12-10 05:24:53 +01:00
|
|
|
return nil, NotEnoughArgsError
|
2012-12-09 21:51:50 +01:00
|
|
|
}
|
|
|
|
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-10 05:24:53 +01:00
|
|
|
// QUIT [ <Quit Message> ]
|
2012-12-09 07:54:58 +01:00
|
|
|
|
|
|
|
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-10 05:24:53 +01:00
|
|
|
// MODE <nickname> *( ( "+" / "-" ) *( "i" / "w" / "o" / "O" / "r" ) )
|
2012-12-09 07:54:58 +01:00
|
|
|
|
|
|
|
type ModeMessage struct {
|
|
|
|
nickname string
|
|
|
|
modes []string
|
2012-04-18 03:16:57 +02:00
|
|
|
}
|
|
|
|
|
2012-12-10 06:46:22 +01:00
|
|
|
type ChannelModeMessage struct {
|
|
|
|
*ModeMessage
|
|
|
|
channel string
|
|
|
|
modeParams []string
|
|
|
|
}
|
|
|
|
|
2012-12-10 05:24:53 +01:00
|
|
|
// mode s is accepted but ignored, like some other modes
|
|
|
|
var MODE_RE = regexp.MustCompile("^[-+][iwroOs]+$")
|
2012-12-10 06:46:22 +01:00
|
|
|
var CHANNEL_RE = regexp.MustCompile("^[+\\&\\!#][:alnum:]+$")
|
|
|
|
var EXTRACT_MODE_RE = regexp.MustCompile("^([-+])?([aimnqpsrtklbeI]+)$")
|
2012-12-09 21:51:50 +01:00
|
|
|
|
|
|
|
func NewModeMessage(args []string) (Message, error) {
|
|
|
|
if len(args) < 1 {
|
2012-12-10 05:24:53 +01:00
|
|
|
return nil, NotEnoughArgsError
|
2012-12-09 21:51:50 +01:00
|
|
|
}
|
2012-12-10 06:46:22 +01:00
|
|
|
|
|
|
|
if (len(args) > 1) && CHANNEL_RE.MatchString(args[1]) {
|
2012-12-12 07:34:22 +01:00
|
|
|
cmsg := new(ChannelModeMessage)
|
|
|
|
cmsg.nickname = args[0]
|
2012-12-10 06:46:22 +01:00
|
|
|
if len(args) > 2 {
|
|
|
|
groups := EXTRACT_MODE_RE.FindStringSubmatch(args[2])
|
|
|
|
cmsg.modes = make([]string, len(groups[2]))
|
|
|
|
i := 0
|
|
|
|
for _, char := range groups[2] {
|
|
|
|
cmsg.modes[i] = fmt.Sprintf("%s%c", groups[1], char)
|
|
|
|
i++
|
|
|
|
}
|
|
|
|
}
|
2012-12-12 07:34:22 +01:00
|
|
|
if len(args) > 3 {
|
2012-12-10 06:46:22 +01:00
|
|
|
cmsg.modeParams = strings.Split(args[3], ",")
|
|
|
|
}
|
2012-12-12 07:34:22 +01:00
|
|
|
return cmsg, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
msg := &ModeMessage{
|
|
|
|
nickname: args[0],
|
2012-12-10 06:46:22 +01:00
|
|
|
}
|
2012-12-09 21:51:50 +01:00
|
|
|
for _, arg := range args[1:] {
|
|
|
|
if !MODE_RE.MatchString(arg) {
|
2012-12-12 07:34:22 +01:00
|
|
|
return nil, UModeUnknownFlagError
|
2012-12-09 21:51:50 +01:00
|
|
|
}
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2012-12-12 08:04:03 +01:00
|
|
|
func (m *ChannelModeMessage) Handle(s *Server, c *Client) {
|
|
|
|
channel := s.channels[m.channel]
|
|
|
|
if channel != nil {
|
|
|
|
c.send <- ErrNoChanModes(channel)
|
|
|
|
} else {
|
|
|
|
c.send <- ErrNoSuchChannel(s, m.channel)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-12-10 05:24:53 +01:00
|
|
|
// JOIN ( <channel> *( "," <channel> ) [ <key> *( "," <key> ) ] ) / "0"
|
2012-12-09 07:54:58 +01:00
|
|
|
|
|
|
|
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 {
|
2012-12-10 05:24:53 +01:00
|
|
|
return nil, NotEnoughArgsError
|
2012-12-09 21:51:50 +01:00
|
|
|
}
|
|
|
|
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 {
|
2012-12-10 05:24:53 +01:00
|
|
|
return nil, NotEnoughArgsError
|
2012-12-09 21:51:50 +01:00
|
|
|
}
|
|
|
|
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() {
|
2012-12-12 08:34:41 +01:00
|
|
|
if channel := s.channels[m.target]; channel != nil {
|
2012-12-09 07:54:58 +01:00
|
|
|
channel.PrivMsg(c, m.message)
|
2012-12-12 08:34:41 +01:00
|
|
|
return
|
2012-12-09 07:54:58 +01:00
|
|
|
}
|
|
|
|
} else {
|
2012-12-12 08:34:41 +01:00
|
|
|
if client := s.nicks[m.target]; client != nil {
|
|
|
|
client.send <- RplPrivMsg(c, m.message)
|
|
|
|
return
|
2012-12-09 07:54:58 +01:00
|
|
|
}
|
|
|
|
}
|
2012-12-12 08:34:41 +01:00
|
|
|
c.send <- ErrNoSuchNick(s, m.target)
|
2012-12-09 07:54:58 +01:00
|
|
|
}
|
|
|
|
|
2012-12-09 23:59:28 +01:00
|
|
|
// TOPIC [newtopic]
|
2012-12-09 07:54:58 +01:00
|
|
|
|
|
|
|
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 {
|
2012-12-10 05:24:53 +01:00
|
|
|
return nil, NotEnoughArgsError
|
2012-12-09 21:51:50 +01:00
|
|
|
}
|
|
|
|
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
|
|
|
|
2012-12-10 05:24:53 +01:00
|
|
|
// OPER <name> <password>
|
|
|
|
|
|
|
|
type OperMessage struct {
|
|
|
|
name string
|
|
|
|
password string
|
|
|
|
}
|
|
|
|
|
2012-12-12 07:34:22 +01:00
|
|
|
func NewOperMessage(args []string) (Message, error) {
|
2012-12-10 05:24:53 +01:00
|
|
|
if len(args) < 2 {
|
|
|
|
return nil, NotEnoughArgsError
|
|
|
|
}
|
|
|
|
return &OperMessage{
|
|
|
|
name: args[0],
|
|
|
|
password: args[1],
|
2012-12-12 07:34:22 +01:00
|
|
|
}, nil
|
2012-12-10 05:24:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (m *OperMessage) Handle(s *Server, c *Client) {
|
|
|
|
if s.operators[m.name] == m.password {
|
|
|
|
c.send <- RplYoureOper(s)
|
|
|
|
} else {
|
|
|
|
c.send <- ErrPasswdMismatch(s)
|
|
|
|
}
|
|
|
|
}
|