mirror of
https://github.com/ergochat/ergo.git
synced 2024-11-29 07:29:31 +01:00
get rid of mutexes in favor of channel-base syncing
This commit is contained in:
parent
74b8221db7
commit
e411dafda7
139
irc/channel.go
139
irc/channel.go
@ -2,17 +2,14 @@ package irc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
"sync"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Channel struct {
|
type Channel struct {
|
||||||
banList []UserMask
|
banList []UserMask
|
||||||
commands chan<- ChannelCommand
|
commands chan<- ChannelCommand
|
||||||
destroyed bool
|
flags ChannelModeSet
|
||||||
flags map[ChannelMode]bool
|
|
||||||
key string
|
key string
|
||||||
members ClientSet
|
members MemberSet
|
||||||
mutex *sync.Mutex
|
|
||||||
name string
|
name string
|
||||||
replies chan<- Reply
|
replies chan<- Reply
|
||||||
server *Server
|
server *Server
|
||||||
@ -38,9 +35,8 @@ func NewChannel(s *Server, name string) *Channel {
|
|||||||
channel := &Channel{
|
channel := &Channel{
|
||||||
banList: make([]UserMask, 0),
|
banList: make([]UserMask, 0),
|
||||||
commands: commands,
|
commands: commands,
|
||||||
flags: make(map[ChannelMode]bool),
|
flags: make(ChannelModeSet),
|
||||||
members: make(ClientSet),
|
members: make(MemberSet),
|
||||||
mutex: &sync.Mutex{},
|
|
||||||
name: name,
|
name: name,
|
||||||
replies: replies,
|
replies: replies,
|
||||||
server: s,
|
server: s,
|
||||||
@ -50,19 +46,17 @@ func NewChannel(s *Server, name string) *Channel {
|
|||||||
return channel
|
return channel
|
||||||
}
|
}
|
||||||
|
|
||||||
func (channel *Channel) Destroy() {
|
type DestroyChannel struct {
|
||||||
if channel.IsDestroyed() {
|
BaseCommand
|
||||||
return
|
channel *Channel
|
||||||
}
|
}
|
||||||
|
|
||||||
channel.withMutex(func() {
|
func (channel *Channel) Destroy() {
|
||||||
channel.destroyed = true
|
channel.server.Command(&DestroyChannel{
|
||||||
channel.members = make(ClientSet)
|
channel: channel,
|
||||||
})
|
|
||||||
|
|
||||||
channel.server.withMutex(func() {
|
|
||||||
channel.server.channels.Remove(channel)
|
|
||||||
})
|
})
|
||||||
|
close(channel.commands)
|
||||||
|
close(channel.replies)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (channel *Channel) Command(command ChannelCommand) {
|
func (channel *Channel) Command(command ChannelCommand) {
|
||||||
@ -75,13 +69,6 @@ func (channel *Channel) Reply(reply Reply) {
|
|||||||
|
|
||||||
func (channel *Channel) receiveCommands(commands <-chan ChannelCommand) {
|
func (channel *Channel) receiveCommands(commands <-chan ChannelCommand) {
|
||||||
for command := range commands {
|
for command := range commands {
|
||||||
if channel.IsDestroyed() {
|
|
||||||
if DEBUG_CHANNEL {
|
|
||||||
log.Printf("%s → %s %s dropped", command.Source(), channel, command)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if DEBUG_CHANNEL {
|
if DEBUG_CHANNEL {
|
||||||
log.Printf("%s → %s %s", command.Source(), channel, command)
|
log.Printf("%s → %s %s", command.Source(), channel, command)
|
||||||
}
|
}
|
||||||
@ -89,40 +76,19 @@ func (channel *Channel) receiveCommands(commands <-chan ChannelCommand) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsPrivMsg(reply Reply) bool {
|
|
||||||
strReply, ok := reply.(*StringReply)
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return strReply.code == "PRIVMSG"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (channel *Channel) IsDestroyed() bool {
|
|
||||||
channel.mutex.Lock()
|
|
||||||
defer channel.mutex.Unlock()
|
|
||||||
return channel.destroyed
|
|
||||||
}
|
|
||||||
|
|
||||||
func (channel *Channel) receiveReplies(replies <-chan Reply) {
|
func (channel *Channel) receiveReplies(replies <-chan Reply) {
|
||||||
for reply := range replies {
|
for reply := range replies {
|
||||||
if channel.IsDestroyed() {
|
|
||||||
if DEBUG_CHANNEL {
|
|
||||||
log.Printf("%s ← %s %s dropped", channel, reply.Source(), reply)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if DEBUG_CHANNEL {
|
if DEBUG_CHANNEL {
|
||||||
log.Printf("%s ← %s %s", channel, reply.Source(), reply)
|
log.Printf("%s ← %s %s", channel, reply.Source(), reply)
|
||||||
}
|
}
|
||||||
channel.withMutex(func() {
|
|
||||||
for client := range channel.members {
|
for client := range channel.members {
|
||||||
if IsPrivMsg(reply) && (reply.Source() == Identifier(client)) {
|
if (reply.Code() == ReplyCode(PRIVMSG)) &&
|
||||||
|
(reply.Source() == Identifier(client)) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
client.Reply(reply)
|
client.Reply(reply)
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,25 +164,9 @@ func (channel *Channel) ModeString() (str string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (channel *Channel) withMutex(f func()) {
|
type JoinChannel struct {
|
||||||
channel.mutex.Lock()
|
BaseCommand
|
||||||
defer channel.mutex.Unlock()
|
channel *Channel
|
||||||
f()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (channel *Channel) Join(client *Client) {
|
|
||||||
channel.withMutex(func() {
|
|
||||||
channel.members.Add(client)
|
|
||||||
if len(channel.members) == 1 {
|
|
||||||
channel.members[client][ChannelCreator] = true
|
|
||||||
channel.members[client][ChannelOperator] = true
|
|
||||||
}
|
|
||||||
client.channels.Add(channel)
|
|
||||||
})
|
|
||||||
|
|
||||||
channel.Reply(RplJoin(client, channel))
|
|
||||||
channel.GetTopic(client)
|
|
||||||
channel.GetUsers(client)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -230,7 +180,33 @@ func (m *JoinCommand) HandleChannel(channel *Channel) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
channel.Join(client)
|
channel.members.Add(client)
|
||||||
|
if len(channel.members) == 1 {
|
||||||
|
channel.members[client][ChannelCreator] = true
|
||||||
|
channel.members[client][ChannelOperator] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
client.commands <- &JoinChannel{
|
||||||
|
channel: channel,
|
||||||
|
}
|
||||||
|
|
||||||
|
addClient := &AddFriend{
|
||||||
|
client: client,
|
||||||
|
}
|
||||||
|
for member := range channel.members {
|
||||||
|
client.commands <- &AddFriend{
|
||||||
|
client: member,
|
||||||
|
}
|
||||||
|
member.commands <- addClient
|
||||||
|
}
|
||||||
|
|
||||||
|
channel.Reply(RplJoin(client, channel))
|
||||||
|
channel.GetTopic(client)
|
||||||
|
channel.GetUsers(client)
|
||||||
|
}
|
||||||
|
|
||||||
|
type PartChannel struct {
|
||||||
|
channel *Channel
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *PartCommand) HandleChannel(channel *Channel) {
|
func (m *PartCommand) HandleChannel(channel *Channel) {
|
||||||
@ -244,9 +220,15 @@ func (m *PartCommand) HandleChannel(channel *Channel) {
|
|||||||
channel.Reply(RplPart(client, channel, m.Message()))
|
channel.Reply(RplPart(client, channel, m.Message()))
|
||||||
|
|
||||||
channel.members.Remove(client)
|
channel.members.Remove(client)
|
||||||
client.channels.Remove(channel)
|
client.commands <- &PartChannel{
|
||||||
|
channel: channel,
|
||||||
|
}
|
||||||
|
for member := range channel.members {
|
||||||
|
member.commands <- &RemoveFriend{
|
||||||
|
client: client,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO persistent channels
|
|
||||||
if channel.IsEmpty() {
|
if channel.IsEmpty() {
|
||||||
channel.Destroy()
|
channel.Destroy()
|
||||||
}
|
}
|
||||||
@ -387,3 +369,18 @@ func (m *NoticeCommand) HandleChannel(channel *Channel) {
|
|||||||
}
|
}
|
||||||
channel.Reply(RplNotice(client, channel, m.message))
|
channel.Reply(RplNotice(client, channel, m.message))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (msg *QuitCommand) HandleChannel(channel *Channel) {
|
||||||
|
client := msg.Client()
|
||||||
|
removeClient := &RemoveFriend{
|
||||||
|
client: client,
|
||||||
|
}
|
||||||
|
for member := range channel.members {
|
||||||
|
member.commands <- removeClient
|
||||||
|
}
|
||||||
|
channel.members.Remove(client)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *DestroyClient) HandleChannel(channel *Channel) {
|
||||||
|
channel.members.Remove(msg.client)
|
||||||
|
}
|
||||||
|
135
irc/client.go
135
irc/client.go
@ -4,7 +4,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -13,13 +12,13 @@ type Client struct {
|
|||||||
away bool
|
away bool
|
||||||
awayMessage string
|
awayMessage string
|
||||||
channels ChannelSet
|
channels ChannelSet
|
||||||
|
commands chan ClientCommand
|
||||||
ctime time.Time
|
ctime time.Time
|
||||||
destroyed bool
|
friends map[*Client]uint
|
||||||
hostname string
|
hostname string
|
||||||
idleTimer *time.Timer
|
idleTimer *time.Timer
|
||||||
invisible bool
|
invisible bool
|
||||||
loginTimer *time.Timer
|
loginTimer *time.Timer
|
||||||
mutex *sync.Mutex
|
|
||||||
nick string
|
nick string
|
||||||
operator bool
|
operator bool
|
||||||
phase Phase
|
phase Phase
|
||||||
@ -36,16 +35,18 @@ func NewClient(server *Server, conn net.Conn) *Client {
|
|||||||
client := &Client{
|
client := &Client{
|
||||||
atime: now,
|
atime: now,
|
||||||
channels: make(ChannelSet),
|
channels: make(ChannelSet),
|
||||||
|
commands: make(chan ClientCommand),
|
||||||
ctime: now,
|
ctime: now,
|
||||||
|
friends: make(map[*Client]uint),
|
||||||
hostname: AddrLookupHostname(conn.RemoteAddr()),
|
hostname: AddrLookupHostname(conn.RemoteAddr()),
|
||||||
phase: server.InitPhase(),
|
phase: server.InitPhase(),
|
||||||
replies: make(chan Reply),
|
replies: make(chan Reply),
|
||||||
server: server,
|
server: server,
|
||||||
socket: NewSocket(conn),
|
socket: NewSocket(conn),
|
||||||
mutex: &sync.Mutex{},
|
|
||||||
}
|
}
|
||||||
client.loginTimer = time.AfterFunc(LOGIN_TIMEOUT, client.Destroy)
|
|
||||||
|
|
||||||
|
client.loginTimer = time.AfterFunc(LOGIN_TIMEOUT, client.Destroy)
|
||||||
|
go client.readClientCommands()
|
||||||
go client.readCommands()
|
go client.readCommands()
|
||||||
go client.writeReplies()
|
go client.writeReplies()
|
||||||
|
|
||||||
@ -53,10 +54,6 @@ func NewClient(server *Server, conn net.Conn) *Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) Touch() {
|
func (client *Client) Touch() {
|
||||||
if client.IsDestroyed() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
client.atime = time.Now()
|
client.atime = time.Now()
|
||||||
|
|
||||||
if client.quitTimer != nil {
|
if client.quitTimer != nil {
|
||||||
@ -89,10 +86,6 @@ func (client *Client) ConnectionTimeout() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) ConnectionClosed() {
|
func (client *Client) ConnectionClosed() {
|
||||||
if client.IsDestroyed() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
msg := &QuitCommand{
|
msg := &QuitCommand{
|
||||||
message: "connection closed",
|
message: "connection closed",
|
||||||
}
|
}
|
||||||
@ -100,6 +93,12 @@ func (client *Client) ConnectionClosed() {
|
|||||||
client.server.Command(msg)
|
client.server.Command(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (client *Client) readClientCommands() {
|
||||||
|
for command := range client.commands {
|
||||||
|
command.HandleClient(client)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) readCommands() {
|
func (c *Client) readCommands() {
|
||||||
for line := range c.socket.Read() {
|
for line := range c.socket.Read() {
|
||||||
m, err := ParseCommand(line)
|
m, err := ParseCommand(line)
|
||||||
@ -126,13 +125,6 @@ func (c *Client) readCommands() {
|
|||||||
|
|
||||||
func (client *Client) writeReplies() {
|
func (client *Client) writeReplies() {
|
||||||
for reply := range client.replies {
|
for reply := range client.replies {
|
||||||
if client.IsDestroyed() {
|
|
||||||
if DEBUG_CLIENT {
|
|
||||||
log.Printf("%s ← %s dropped", client, reply)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if DEBUG_CLIENT {
|
if DEBUG_CLIENT {
|
||||||
log.Printf("%s ← %s", client, reply)
|
log.Printf("%s ← %s", client, reply)
|
||||||
}
|
}
|
||||||
@ -143,24 +135,12 @@ func (client *Client) writeReplies() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) IsDestroyed() bool {
|
type DestroyClient struct {
|
||||||
client.mutex.Lock()
|
BaseCommand
|
||||||
defer client.mutex.Unlock()
|
client *Client
|
||||||
return client.destroyed
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) Destroy() {
|
func (client *Client) Destroy() {
|
||||||
if client.IsDestroyed() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if DEBUG_CLIENT {
|
|
||||||
log.Printf("%s destroying", client)
|
|
||||||
}
|
|
||||||
|
|
||||||
client.mutex.Lock()
|
|
||||||
client.destroyed = true
|
|
||||||
|
|
||||||
client.socket.Close()
|
client.socket.Close()
|
||||||
|
|
||||||
if client.idleTimer != nil {
|
if client.idleTimer != nil {
|
||||||
@ -171,14 +151,15 @@ func (client *Client) Destroy() {
|
|||||||
client.quitTimer.Stop()
|
client.quitTimer.Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
client.channels = make(ChannelSet) // clear channel list
|
cmd := &DestroyClient{
|
||||||
client.server.clients.Remove(client)
|
client: client,
|
||||||
|
|
||||||
client.mutex.Unlock()
|
|
||||||
|
|
||||||
if DEBUG_CLIENT {
|
|
||||||
log.Printf("%s destroyed", client)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for channel := range client.channels {
|
||||||
|
channel.Command(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
client.server.Command(cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) Reply(reply Reply) {
|
func (client *Client) Reply(reply Reply) {
|
||||||
@ -199,16 +180,6 @@ func (client *Client) HasUsername() bool {
|
|||||||
return client.username != ""
|
return client.username != ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) InterestedClients() ClientSet {
|
|
||||||
clients := make(ClientSet)
|
|
||||||
for channel := range client.channels {
|
|
||||||
for member := range channel.members {
|
|
||||||
clients.Add(member)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return clients
|
|
||||||
}
|
|
||||||
|
|
||||||
// <mode>
|
// <mode>
|
||||||
func (c *Client) ModeString() (str string) {
|
func (c *Client) ModeString() (str string) {
|
||||||
if c.invisible {
|
if c.invisible {
|
||||||
@ -246,3 +217,63 @@ func (c *Client) Id() string {
|
|||||||
func (c *Client) String() string {
|
func (c *Client) String() string {
|
||||||
return c.UserHost()
|
return c.UserHost()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// commands
|
||||||
|
//
|
||||||
|
|
||||||
|
type AddFriend struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *AddFriend) HandleClient(client *Client) {
|
||||||
|
client.friends[msg.client] += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
type RemoveFriend struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *RemoveFriend) HandleClient(client *Client) {
|
||||||
|
client.friends[msg.client] -= 1
|
||||||
|
if client.friends[msg.client] <= 0 {
|
||||||
|
delete(client.friends, msg.client)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *JoinChannel) HandleClient(client *Client) {
|
||||||
|
client.channels.Add(msg.channel)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *PartChannel) HandleClient(client *Client) {
|
||||||
|
client.channels.Remove(msg.channel)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *NickCommand) HandleClient(client *Client) {
|
||||||
|
// Make reply before changing nick.
|
||||||
|
reply := RplNick(client, msg.nickname)
|
||||||
|
|
||||||
|
client.nick = msg.nickname
|
||||||
|
|
||||||
|
for friend := range client.friends {
|
||||||
|
friend.Reply(reply)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *QuitCommand) HandleClient(client *Client) {
|
||||||
|
if len(client.friends) > 0 {
|
||||||
|
reply := RplQuit(client, msg.message)
|
||||||
|
for friend := range client.friends {
|
||||||
|
if friend == client {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
friend.Reply(reply)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for channel := range client.channels {
|
||||||
|
channel.commands <- msg
|
||||||
|
}
|
||||||
|
|
||||||
|
client.Destroy()
|
||||||
|
}
|
||||||
|
286
irc/constants.go
286
irc/constants.go
@ -23,143 +23,157 @@ const (
|
|||||||
IDLE_TIMEOUT = time.Minute // how long before a client is considered idle
|
IDLE_TIMEOUT = time.Minute // how long before a client is considered idle
|
||||||
QUIT_TIMEOUT = time.Minute // how long after idle before a client is kicked
|
QUIT_TIMEOUT = time.Minute // how long after idle before a client is kicked
|
||||||
|
|
||||||
|
// string codes
|
||||||
|
PRIVMSG StringCode = "PRIVMSG"
|
||||||
|
NOTICE StringCode = "NOTICE"
|
||||||
|
NICK StringCode = "NICK"
|
||||||
|
JOIN StringCode = "JOIN"
|
||||||
|
PART StringCode = "PART"
|
||||||
|
MODE StringCode = "MODE"
|
||||||
|
TOPIC StringCode = "TOPIC"
|
||||||
|
PING StringCode = "PING"
|
||||||
|
PONG StringCode = "PONG"
|
||||||
|
QUIT StringCode = "QUIT"
|
||||||
|
ERROR StringCode = "ERROR"
|
||||||
|
INVITE StringCode = "INVITE"
|
||||||
|
|
||||||
// numeric codes
|
// numeric codes
|
||||||
RPL_WELCOME Numeric = 1
|
RPL_WELCOME NumericCode = 1
|
||||||
RPL_YOURHOST Numeric = 2
|
RPL_YOURHOST NumericCode = 2
|
||||||
RPL_CREATED Numeric = 3
|
RPL_CREATED NumericCode = 3
|
||||||
RPL_MYINFO Numeric = 4
|
RPL_MYINFO NumericCode = 4
|
||||||
RPL_BOUNCE Numeric = 5
|
RPL_BOUNCE NumericCode = 5
|
||||||
RPL_TRACELINK Numeric = 200
|
RPL_TRACELINK NumericCode = 200
|
||||||
RPL_TRACECONNECTING Numeric = 201
|
RPL_TRACECONNECTING NumericCode = 201
|
||||||
RPL_TRACEHANDSHAKE Numeric = 202
|
RPL_TRACEHANDSHAKE NumericCode = 202
|
||||||
RPL_TRACEUNKNOWN Numeric = 203
|
RPL_TRACEUNKNOWN NumericCode = 203
|
||||||
RPL_TRACEOPERATOR Numeric = 204
|
RPL_TRACEOPERATOR NumericCode = 204
|
||||||
RPL_TRACEUSER Numeric = 205
|
RPL_TRACEUSER NumericCode = 205
|
||||||
RPL_TRACESERVER Numeric = 206
|
RPL_TRACESERVER NumericCode = 206
|
||||||
RPL_TRACESERVICE Numeric = 207
|
RPL_TRACESERVICE NumericCode = 207
|
||||||
RPL_TRACENEWTYPE Numeric = 208
|
RPL_TRACENEWTYPE NumericCode = 208
|
||||||
RPL_TRACECLASS Numeric = 209
|
RPL_TRACECLASS NumericCode = 209
|
||||||
RPL_TRACERECONNECT Numeric = 210
|
RPL_TRACERECONNECT NumericCode = 210
|
||||||
RPL_STATSLINKINFO Numeric = 211
|
RPL_STATSLINKINFO NumericCode = 211
|
||||||
RPL_STATSCOMMANDS Numeric = 212
|
RPL_STATSCOMMANDS NumericCode = 212
|
||||||
RPL_ENDOFSTATS Numeric = 219
|
RPL_ENDOFSTATS NumericCode = 219
|
||||||
RPL_UMODEIS Numeric = 221
|
RPL_UMODEIS NumericCode = 221
|
||||||
RPL_SERVLIST Numeric = 234
|
RPL_SERVLIST NumericCode = 234
|
||||||
RPL_SERVLISTEND Numeric = 235
|
RPL_SERVLISTEND NumericCode = 235
|
||||||
RPL_STATSUPTIME Numeric = 242
|
RPL_STATSUPTIME NumericCode = 242
|
||||||
RPL_STATSOLINE Numeric = 243
|
RPL_STATSOLINE NumericCode = 243
|
||||||
RPL_LUSERCLIENT Numeric = 251
|
RPL_LUSERCLIENT NumericCode = 251
|
||||||
RPL_LUSEROP Numeric = 252
|
RPL_LUSEROP NumericCode = 252
|
||||||
RPL_LUSERUNKNOWN Numeric = 253
|
RPL_LUSERUNKNOWN NumericCode = 253
|
||||||
RPL_LUSERCHANNELS Numeric = 254
|
RPL_LUSERCHANNELS NumericCode = 254
|
||||||
RPL_LUSERME Numeric = 255
|
RPL_LUSERME NumericCode = 255
|
||||||
RPL_ADMINME Numeric = 256
|
RPL_ADMINME NumericCode = 256
|
||||||
RPL_ADMINLOC1 Numeric = 257
|
RPL_ADMINLOC1 NumericCode = 257
|
||||||
RPL_ADMINLOC2 Numeric = 258
|
RPL_ADMINLOC2 NumericCode = 258
|
||||||
RPL_ADMINEMAIL Numeric = 259
|
RPL_ADMINEMAIL NumericCode = 259
|
||||||
RPL_TRACELOG Numeric = 261
|
RPL_TRACELOG NumericCode = 261
|
||||||
RPL_TRACEEND Numeric = 262
|
RPL_TRACEEND NumericCode = 262
|
||||||
RPL_TRYAGAIN Numeric = 263
|
RPL_TRYAGAIN NumericCode = 263
|
||||||
RPL_AWAY Numeric = 301
|
RPL_AWAY NumericCode = 301
|
||||||
RPL_USERHOST Numeric = 302
|
RPL_USERHOST NumericCode = 302
|
||||||
RPL_ISON Numeric = 303
|
RPL_ISON NumericCode = 303
|
||||||
RPL_UNAWAY Numeric = 305
|
RPL_UNAWAY NumericCode = 305
|
||||||
RPL_NOWAWAY Numeric = 306
|
RPL_NOWAWAY NumericCode = 306
|
||||||
RPL_WHOISUSER Numeric = 311
|
RPL_WHOISUSER NumericCode = 311
|
||||||
RPL_WHOISSERVER Numeric = 312
|
RPL_WHOISSERVER NumericCode = 312
|
||||||
RPL_WHOISOPERATOR Numeric = 313
|
RPL_WHOISOPERATOR NumericCode = 313
|
||||||
RPL_WHOWASUSER Numeric = 314
|
RPL_WHOWASUSER NumericCode = 314
|
||||||
RPL_ENDOFWHO Numeric = 315
|
RPL_ENDOFWHO NumericCode = 315
|
||||||
RPL_WHOISIDLE Numeric = 317
|
RPL_WHOISIDLE NumericCode = 317
|
||||||
RPL_ENDOFWHOIS Numeric = 318
|
RPL_ENDOFWHOIS NumericCode = 318
|
||||||
RPL_WHOISCHANNELS Numeric = 319
|
RPL_WHOISCHANNELS NumericCode = 319
|
||||||
RPL_LIST Numeric = 322
|
RPL_LIST NumericCode = 322
|
||||||
RPL_LISTEND Numeric = 323
|
RPL_LISTEND NumericCode = 323
|
||||||
RPL_CHANNELMODEIS Numeric = 324
|
RPL_CHANNELMODEIS NumericCode = 324
|
||||||
RPL_UNIQOPIS Numeric = 325
|
RPL_UNIQOPIS NumericCode = 325
|
||||||
RPL_NOTOPIC Numeric = 331
|
RPL_NOTOPIC NumericCode = 331
|
||||||
RPL_TOPIC Numeric = 332
|
RPL_TOPIC NumericCode = 332
|
||||||
RPL_INVITING Numeric = 341
|
RPL_INVITING NumericCode = 341
|
||||||
RPL_SUMMONING Numeric = 342
|
RPL_SUMMONING NumericCode = 342
|
||||||
RPL_INVITELIST Numeric = 346
|
RPL_INVITELIST NumericCode = 346
|
||||||
RPL_ENDOFINVITELIST Numeric = 347
|
RPL_ENDOFINVITELIST NumericCode = 347
|
||||||
RPL_EXCEPTLIST Numeric = 348
|
RPL_EXCEPTLIST NumericCode = 348
|
||||||
RPL_ENDOFEXCEPTLIST Numeric = 349
|
RPL_ENDOFEXCEPTLIST NumericCode = 349
|
||||||
RPL_VERSION Numeric = 351
|
RPL_VERSION NumericCode = 351
|
||||||
RPL_WHOREPLY Numeric = 352
|
RPL_WHOREPLY NumericCode = 352
|
||||||
RPL_NAMREPLY Numeric = 353
|
RPL_NAMREPLY NumericCode = 353
|
||||||
RPL_LINKS Numeric = 364
|
RPL_LINKS NumericCode = 364
|
||||||
RPL_ENDOFLINKS Numeric = 365
|
RPL_ENDOFLINKS NumericCode = 365
|
||||||
RPL_ENDOFNAMES Numeric = 366
|
RPL_ENDOFNAMES NumericCode = 366
|
||||||
RPL_BANLIST Numeric = 367
|
RPL_BANLIST NumericCode = 367
|
||||||
RPL_ENDOFBANLIST Numeric = 368
|
RPL_ENDOFBANLIST NumericCode = 368
|
||||||
RPL_ENDOFWHOWAS Numeric = 369
|
RPL_ENDOFWHOWAS NumericCode = 369
|
||||||
RPL_INFO Numeric = 371
|
RPL_INFO NumericCode = 371
|
||||||
RPL_MOTD Numeric = 372
|
RPL_MOTD NumericCode = 372
|
||||||
RPL_ENDOFINFO Numeric = 374
|
RPL_ENDOFINFO NumericCode = 374
|
||||||
RPL_MOTDSTART Numeric = 375
|
RPL_MOTDSTART NumericCode = 375
|
||||||
RPL_ENDOFMOTD Numeric = 376
|
RPL_ENDOFMOTD NumericCode = 376
|
||||||
RPL_YOUREOPER Numeric = 381
|
RPL_YOUREOPER NumericCode = 381
|
||||||
RPL_REHASHING Numeric = 382
|
RPL_REHASHING NumericCode = 382
|
||||||
RPL_YOURESERVICE Numeric = 383
|
RPL_YOURESERVICE NumericCode = 383
|
||||||
RPL_TIME Numeric = 391
|
RPL_TIME NumericCode = 391
|
||||||
RPL_USERSSTART Numeric = 392
|
RPL_USERSSTART NumericCode = 392
|
||||||
RPL_USERS Numeric = 393
|
RPL_USERS NumericCode = 393
|
||||||
RPL_ENDOFUSERS Numeric = 394
|
RPL_ENDOFUSERS NumericCode = 394
|
||||||
RPL_NOUSERS Numeric = 395
|
RPL_NOUSERS NumericCode = 395
|
||||||
ERR_NOSUCHNICK Numeric = 401
|
ERR_NOSUCHNICK NumericCode = 401
|
||||||
ERR_NOSUCHSERVER Numeric = 402
|
ERR_NOSUCHSERVER NumericCode = 402
|
||||||
ERR_NOSUCHCHANNEL Numeric = 403
|
ERR_NOSUCHCHANNEL NumericCode = 403
|
||||||
ERR_CANNOTSENDTOCHAN Numeric = 404
|
ERR_CANNOTSENDTOCHAN NumericCode = 404
|
||||||
ERR_TOOMANYCHANNELS Numeric = 405
|
ERR_TOOMANYCHANNELS NumericCode = 405
|
||||||
ERR_WASNOSUCHNICK Numeric = 406
|
ERR_WASNOSUCHNICK NumericCode = 406
|
||||||
ERR_TOOMANYTARGETS Numeric = 407
|
ERR_TOOMANYTARGETS NumericCode = 407
|
||||||
ERR_NOSUCHSERVICE Numeric = 408
|
ERR_NOSUCHSERVICE NumericCode = 408
|
||||||
ERR_NOORIGIN Numeric = 409
|
ERR_NOORIGIN NumericCode = 409
|
||||||
ERR_NORECIPIENT Numeric = 411
|
ERR_NORECIPIENT NumericCode = 411
|
||||||
ERR_NOTEXTTOSEND Numeric = 412
|
ERR_NOTEXTTOSEND NumericCode = 412
|
||||||
ERR_NOTOPLEVEL Numeric = 413
|
ERR_NOTOPLEVEL NumericCode = 413
|
||||||
ERR_WILDTOPLEVEL Numeric = 414
|
ERR_WILDTOPLEVEL NumericCode = 414
|
||||||
ERR_BADMASK Numeric = 415
|
ERR_BADMASK NumericCode = 415
|
||||||
ERR_UNKNOWNCOMMAND Numeric = 421
|
ERR_UNKNOWNCOMMAND NumericCode = 421
|
||||||
ERR_NOMOTD Numeric = 422
|
ERR_NOMOTD NumericCode = 422
|
||||||
ERR_NOADMININFO Numeric = 423
|
ERR_NOADMININFO NumericCode = 423
|
||||||
ERR_FILEERROR Numeric = 424
|
ERR_FILEERROR NumericCode = 424
|
||||||
ERR_NONICKNAMEGIVEN Numeric = 431
|
ERR_NONICKNAMEGIVEN NumericCode = 431
|
||||||
ERR_ERRONEUSNICKNAME Numeric = 432
|
ERR_ERRONEUSNICKNAME NumericCode = 432
|
||||||
ERR_NICKNAMEINUSE Numeric = 433
|
ERR_NICKNAMEINUSE NumericCode = 433
|
||||||
ERR_NICKCOLLISION Numeric = 436
|
ERR_NICKCOLLISION NumericCode = 436
|
||||||
ERR_UNAVAILRESOURCE Numeric = 437
|
ERR_UNAVAILRESOURCE NumericCode = 437
|
||||||
ERR_USERNOTINCHANNEL Numeric = 441
|
ERR_USERNOTINCHANNEL NumericCode = 441
|
||||||
ERR_NOTONCHANNEL Numeric = 442
|
ERR_NOTONCHANNEL NumericCode = 442
|
||||||
ERR_USERONCHANNEL Numeric = 443
|
ERR_USERONCHANNEL NumericCode = 443
|
||||||
ERR_NOLOGIN Numeric = 444
|
ERR_NOLOGIN NumericCode = 444
|
||||||
ERR_SUMMONDISABLED Numeric = 445
|
ERR_SUMMONDISABLED NumericCode = 445
|
||||||
ERR_USERSDISABLED Numeric = 446
|
ERR_USERSDISABLED NumericCode = 446
|
||||||
ERR_NOTREGISTERED Numeric = 451
|
ERR_NOTREGISTERED NumericCode = 451
|
||||||
ERR_NEEDMOREPARAMS Numeric = 461
|
ERR_NEEDMOREPARAMS NumericCode = 461
|
||||||
ERR_ALREADYREGISTRED Numeric = 462
|
ERR_ALREADYREGISTRED NumericCode = 462
|
||||||
ERR_NOPERMFORHOST Numeric = 463
|
ERR_NOPERMFORHOST NumericCode = 463
|
||||||
ERR_PASSWDMISMATCH Numeric = 464
|
ERR_PASSWDMISMATCH NumericCode = 464
|
||||||
ERR_YOUREBANNEDCREEP Numeric = 465
|
ERR_YOUREBANNEDCREEP NumericCode = 465
|
||||||
ERR_YOUWILLBEBANNED Numeric = 466
|
ERR_YOUWILLBEBANNED NumericCode = 466
|
||||||
ERR_KEYSET Numeric = 467
|
ERR_KEYSET NumericCode = 467
|
||||||
ERR_CHANNELISFULL Numeric = 471
|
ERR_CHANNELISFULL NumericCode = 471
|
||||||
ERR_UNKNOWNMODE Numeric = 472
|
ERR_UNKNOWNMODE NumericCode = 472
|
||||||
ERR_INVITEONLYCHAN Numeric = 473
|
ERR_INVITEONLYCHAN NumericCode = 473
|
||||||
ERR_BANNEDFROMCHAN Numeric = 474
|
ERR_BANNEDFROMCHAN NumericCode = 474
|
||||||
ERR_BADCHANNELKEY Numeric = 475
|
ERR_BADCHANNELKEY NumericCode = 475
|
||||||
ERR_BADCHANMASK Numeric = 476
|
ERR_BADCHANMASK NumericCode = 476
|
||||||
ERR_NOCHANMODES Numeric = 477
|
ERR_NOCHANMODES NumericCode = 477
|
||||||
ERR_BANLISTFULL Numeric = 478
|
ERR_BANLISTFULL NumericCode = 478
|
||||||
ERR_NOPRIVILEGES Numeric = 481
|
ERR_NOPRIVILEGES NumericCode = 481
|
||||||
ERR_CHANOPRIVSNEEDED Numeric = 482
|
ERR_CHANOPRIVSNEEDED NumericCode = 482
|
||||||
ERR_CANTKILLSERVER Numeric = 483
|
ERR_CANTKILLSERVER NumericCode = 483
|
||||||
ERR_RESTRICTED Numeric = 484
|
ERR_RESTRICTED NumericCode = 484
|
||||||
ERR_UNIQOPPRIVSNEEDED Numeric = 485
|
ERR_UNIQOPPRIVSNEEDED NumericCode = 485
|
||||||
ERR_NOOPERHOST Numeric = 491
|
ERR_NOOPERHOST NumericCode = 491
|
||||||
ERR_UMODEUNKNOWNFLAG Numeric = 501
|
ERR_UMODEUNKNOWNFLAG NumericCode = 501
|
||||||
ERR_USERSDONTMATCH Numeric = 502
|
ERR_USERSDONTMATCH NumericCode = 502
|
||||||
|
|
||||||
Add ModeOp = '+'
|
Add ModeOp = '+'
|
||||||
List ModeOp = '='
|
List ModeOp = '='
|
||||||
|
59
irc/reply.go
59
irc/reply.go
@ -15,11 +15,16 @@ func joinedLen(names []string) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type BaseReply struct {
|
type BaseReply struct {
|
||||||
|
code ReplyCode
|
||||||
id string
|
id string
|
||||||
message string
|
message string
|
||||||
source Identifier
|
source Identifier
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (reply *BaseReply) Code() ReplyCode {
|
||||||
|
return reply.code
|
||||||
|
}
|
||||||
|
|
||||||
func (reply *BaseReply) SetSource(source Identifier) {
|
func (reply *BaseReply) SetSource(source Identifier) {
|
||||||
reply.id = source.Id()
|
reply.id = source.Id()
|
||||||
reply.source = source
|
reply.source = source
|
||||||
@ -31,16 +36,14 @@ func (reply *BaseReply) Source() Identifier {
|
|||||||
|
|
||||||
type StringReply struct {
|
type StringReply struct {
|
||||||
BaseReply
|
BaseReply
|
||||||
code string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewStringReply(source Identifier, code string,
|
func NewStringReply(source Identifier, code StringCode,
|
||||||
format string, args ...interface{}) *StringReply {
|
format string, args ...interface{}) *StringReply {
|
||||||
reply := &StringReply{
|
reply := &StringReply{}
|
||||||
code: code,
|
reply.code = code
|
||||||
}
|
|
||||||
reply.SetSource(source)
|
|
||||||
reply.message = fmt.Sprintf(format, args...)
|
reply.message = fmt.Sprintf(format, args...)
|
||||||
|
reply.SetSource(source)
|
||||||
return reply
|
return reply
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,16 +60,14 @@ func (reply *StringReply) String() string {
|
|||||||
|
|
||||||
type NumericReply struct {
|
type NumericReply struct {
|
||||||
BaseReply
|
BaseReply
|
||||||
code Numeric
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewNumericReply(source Identifier, code Numeric, format string,
|
func NewNumericReply(source Identifier, code NumericCode, format string,
|
||||||
args ...interface{}) *NumericReply {
|
args ...interface{}) *NumericReply {
|
||||||
reply := &NumericReply{
|
reply := &NumericReply{}
|
||||||
code: code,
|
reply.code = code
|
||||||
}
|
|
||||||
reply.SetSource(source)
|
|
||||||
reply.message = fmt.Sprintf(format, args...)
|
reply.message = fmt.Sprintf(format, args...)
|
||||||
|
reply.SetSource(source)
|
||||||
return reply
|
return reply
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,7 +93,7 @@ func NewNamesReply(channel *Channel) Reply {
|
|||||||
reply := &NamesReply{
|
reply := &NamesReply{
|
||||||
channel: channel,
|
channel: channel,
|
||||||
}
|
}
|
||||||
reply.SetSource(channel)
|
reply.SetSource(channel.server)
|
||||||
return reply
|
return reply
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,14 +108,16 @@ func (reply *NamesReply) Format(client *Client) []string {
|
|||||||
nicks := reply.channel.Nicks()
|
nicks := reply.channel.Nicks()
|
||||||
for to < len(nicks) {
|
for to < len(nicks) {
|
||||||
if (from < (to - 1)) && tooLong(nicks[from:to]) {
|
if (from < (to - 1)) && tooLong(nicks[from:to]) {
|
||||||
lines = append(lines, RplNamReply(reply.channel, nicks[from:to-1]).Format(client)...)
|
lines = append(lines, RplNamReply(reply.channel,
|
||||||
|
nicks[from:to-1]).Format(client)...)
|
||||||
from, to = to-1, to
|
from, to = to-1, to
|
||||||
} else {
|
} else {
|
||||||
to += 1
|
to += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if from < len(nicks) {
|
if from < len(nicks) {
|
||||||
lines = append(lines, RplNamReply(reply.channel, nicks[from:]).Format(client)...)
|
lines = append(lines, RplNamReply(reply.channel,
|
||||||
|
nicks[from:]).Format(client)...)
|
||||||
}
|
}
|
||||||
lines = append(lines, RplEndOfNames(reply.channel).Format(client)...)
|
lines = append(lines, RplEndOfNames(reply.channel).Format(client)...)
|
||||||
return lines
|
return lines
|
||||||
@ -128,56 +131,56 @@ func (reply *NamesReply) String() string {
|
|||||||
// messaging replies
|
// messaging replies
|
||||||
|
|
||||||
func RplPrivMsg(source Identifier, target Identifier, message string) Reply {
|
func RplPrivMsg(source Identifier, target Identifier, message string) Reply {
|
||||||
return NewStringReply(source, "PRIVMSG", "%s :%s", target.Nick(), message)
|
return NewStringReply(source, PRIVMSG, "%s :%s", target.Nick(), message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RplNotice(source Identifier, target Identifier, message string) Reply {
|
func RplNotice(source Identifier, target Identifier, message string) Reply {
|
||||||
return NewStringReply(source, "NOTICE", "%s :%s", target.Nick(), message)
|
return NewStringReply(source, NOTICE, "%s :%s", target.Nick(), message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RplNick(source Identifier, newNick string) Reply {
|
func RplNick(source Identifier, newNick string) Reply {
|
||||||
return NewStringReply(source, "NICK", newNick)
|
return NewStringReply(source, NICK, newNick)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RplJoin(client *Client, channel *Channel) Reply {
|
func RplJoin(client *Client, channel *Channel) Reply {
|
||||||
return NewStringReply(client, "JOIN", channel.name)
|
return NewStringReply(client, JOIN, channel.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RplPart(client *Client, channel *Channel, message string) Reply {
|
func RplPart(client *Client, channel *Channel, message string) Reply {
|
||||||
return NewStringReply(client, "PART", "%s :%s", channel, message)
|
return NewStringReply(client, PART, "%s :%s", channel, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RplMode(client *Client, changes ModeChanges) Reply {
|
func RplMode(client *Client, changes ModeChanges) Reply {
|
||||||
return NewStringReply(client, "MODE", "%s :%s", client.Nick(), changes)
|
return NewStringReply(client, MODE, "%s :%s", client.Nick(), changes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RplChannelMode(client *Client, channel *Channel,
|
func RplChannelMode(client *Client, channel *Channel,
|
||||||
changes ChannelModeChanges) Reply {
|
changes ChannelModeChanges) Reply {
|
||||||
return NewStringReply(client, "MODE", "%s %s", channel, changes)
|
return NewStringReply(client, MODE, "%s %s", channel, changes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RplTopicMsg(source Identifier, channel *Channel) Reply {
|
func RplTopicMsg(source Identifier, channel *Channel) Reply {
|
||||||
return NewStringReply(source, "TOPIC", "%s :%s", channel, channel.topic)
|
return NewStringReply(source, TOPIC, "%s :%s", channel, channel.topic)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RplPing(server *Server, target Identifier) Reply {
|
func RplPing(server *Server, target Identifier) Reply {
|
||||||
return NewStringReply(server, "PING", target.Nick())
|
return NewStringReply(server, PING, target.Nick())
|
||||||
}
|
}
|
||||||
|
|
||||||
func RplPong(server *Server, client *Client) Reply {
|
func RplPong(server *Server, client *Client) Reply {
|
||||||
return NewStringReply(server, "PONG", client.Nick())
|
return NewStringReply(server, PONG, client.Nick())
|
||||||
}
|
}
|
||||||
|
|
||||||
func RplQuit(client *Client, message string) Reply {
|
func RplQuit(client *Client, message string) Reply {
|
||||||
return NewStringReply(client, "QUIT", ":%s", message)
|
return NewStringReply(client, QUIT, ":%s", message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RplError(server *Server, target Identifier) Reply {
|
func RplError(server *Server, target Identifier) Reply {
|
||||||
return NewStringReply(server, "ERROR", target.Nick())
|
return NewStringReply(server, ERROR, target.Nick())
|
||||||
}
|
}
|
||||||
|
|
||||||
func RplInviteMsg(channel *Channel, inviter *Client) Reply {
|
func RplInviteMsg(channel *Channel, inviter *Client) Reply {
|
||||||
return NewStringReply(inviter, "INVITE", channel.name)
|
return NewStringReply(inviter, INVITE, channel.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// numeric replies
|
// numeric replies
|
||||||
|
@ -9,20 +9,18 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
channels ChannelNameMap
|
channels ChannelNameMap
|
||||||
|
clients ClientNameMap
|
||||||
commands chan Command
|
commands chan Command
|
||||||
ctime time.Time
|
ctime time.Time
|
||||||
motdFile string
|
motdFile string
|
||||||
mutex *sync.Mutex
|
|
||||||
name string
|
name string
|
||||||
operators map[string]string
|
operators map[string]string
|
||||||
password string
|
password string
|
||||||
clients ClientNameMap
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServer(config *Config) *Server {
|
func NewServer(config *Config) *Server {
|
||||||
@ -32,7 +30,6 @@ func NewServer(config *Config) *Server {
|
|||||||
commands: make(chan Command),
|
commands: make(chan Command),
|
||||||
ctime: time.Now(),
|
ctime: time.Now(),
|
||||||
motdFile: config.MOTD,
|
motdFile: config.MOTD,
|
||||||
mutex: &sync.Mutex{},
|
|
||||||
name: config.Name,
|
name: config.Name,
|
||||||
operators: make(map[string]string),
|
operators: make(map[string]string),
|
||||||
password: config.Password,
|
password: config.Password,
|
||||||
@ -43,7 +40,6 @@ func NewServer(config *Config) *Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
go server.receiveCommands()
|
go server.receiveCommands()
|
||||||
|
|
||||||
for _, listenerConf := range config.Listeners {
|
for _, listenerConf := range config.Listeners {
|
||||||
go server.listen(listenerConf)
|
go server.listen(listenerConf)
|
||||||
}
|
}
|
||||||
@ -51,12 +47,6 @@ func NewServer(config *Config) *Server {
|
|||||||
return server
|
return server
|
||||||
}
|
}
|
||||||
|
|
||||||
func (server *Server) withMutex(f func()) {
|
|
||||||
server.mutex.Lock()
|
|
||||||
defer server.mutex.Unlock()
|
|
||||||
f()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (server *Server) receiveCommands() {
|
func (server *Server) receiveCommands() {
|
||||||
for command := range server.commands {
|
for command := range server.commands {
|
||||||
if DEBUG_SERVER {
|
if DEBUG_SERVER {
|
||||||
@ -145,9 +135,7 @@ func (s *Server) GetOrMakeChannel(name string) *Channel {
|
|||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
channel = NewChannel(s, name)
|
channel = NewChannel(s, name)
|
||||||
s.withMutex(func() {
|
|
||||||
s.channels[name] = channel
|
s.channels[name] = channel
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return channel
|
return channel
|
||||||
@ -316,17 +304,9 @@ func (m *NickCommand) HandleServer(s *Server) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make reply before changing nick.
|
|
||||||
reply := RplNick(c, m.nickname)
|
|
||||||
|
|
||||||
s.clients.Remove(c)
|
s.clients.Remove(c)
|
||||||
c.nick = m.nickname
|
c.commands <- m
|
||||||
s.clients.Add(c)
|
s.clients.Add(c)
|
||||||
|
|
||||||
iclients := c.InterestedClients()
|
|
||||||
for iclient := range iclients {
|
|
||||||
iclient.Reply(reply)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *UserMsgCommand) HandleServer(s *Server) {
|
func (m *UserMsgCommand) HandleServer(s *Server) {
|
||||||
@ -335,24 +315,10 @@ func (m *UserMsgCommand) HandleServer(s *Server) {
|
|||||||
|
|
||||||
func (m *QuitCommand) HandleServer(server *Server) {
|
func (m *QuitCommand) HandleServer(server *Server) {
|
||||||
client := m.Client()
|
client := m.Client()
|
||||||
iclients := client.InterestedClients()
|
|
||||||
iclients.Remove(client)
|
|
||||||
|
|
||||||
for channel := range client.channels {
|
server.clients.Remove(client)
|
||||||
channel.withMutex(func() {
|
|
||||||
channel.members.Remove(client)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
client.Reply(RplError(server, client))
|
client.commands <- m
|
||||||
client.Destroy()
|
|
||||||
|
|
||||||
if len(iclients) > 0 {
|
|
||||||
reply := RplQuit(client, m.message)
|
|
||||||
for iclient := range iclients {
|
|
||||||
iclient.Reply(reply)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *JoinCommand) HandleServer(s *Server) {
|
func (m *JoinCommand) HandleServer(s *Server) {
|
||||||
@ -374,9 +340,7 @@ func (m *JoinCommand) HandleServer(s *Server) {
|
|||||||
|
|
||||||
func (m *PartCommand) HandleServer(server *Server) {
|
func (m *PartCommand) HandleServer(server *Server) {
|
||||||
for _, chname := range m.channels {
|
for _, chname := range m.channels {
|
||||||
server.mutex.Lock()
|
|
||||||
channel := server.channels[chname]
|
channel := server.channels[chname]
|
||||||
server.mutex.Unlock()
|
|
||||||
|
|
||||||
if channel == nil {
|
if channel == nil {
|
||||||
m.Client().Reply(ErrNoSuchChannel(server, chname))
|
m.Client().Reply(ErrNoSuchChannel(server, chname))
|
||||||
@ -567,3 +531,11 @@ func (msg *NoticeCommand) HandleServer(server *Server) {
|
|||||||
}
|
}
|
||||||
target.Reply(RplPrivMsg(msg.Client(), target, msg.message))
|
target.Reply(RplPrivMsg(msg.Client(), target, msg.message))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (msg *DestroyChannel) HandleServer(server *Server) {
|
||||||
|
server.channels.Remove(msg.channel)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *DestroyClient) HandleServer(server *Server) {
|
||||||
|
server.clients.Remove(msg.client)
|
||||||
|
}
|
||||||
|
51
irc/types.go
51
irc/types.go
@ -28,9 +28,19 @@ func (mode UserMode) String() string {
|
|||||||
|
|
||||||
type Phase uint
|
type Phase uint
|
||||||
|
|
||||||
type Numeric uint
|
type ReplyCode interface {
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
|
||||||
func (code Numeric) String() string {
|
type StringCode string
|
||||||
|
|
||||||
|
func (code StringCode) String() string {
|
||||||
|
return string(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
type NumericCode uint
|
||||||
|
|
||||||
|
func (code NumericCode) String() string {
|
||||||
return fmt.Sprintf("%03d", code)
|
return fmt.Sprintf("%03d", code)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,29 +97,43 @@ func (clients ClientNameMap) Remove(client *Client) error {
|
|||||||
|
|
||||||
type ChannelModeSet map[ChannelMode]bool
|
type ChannelModeSet map[ChannelMode]bool
|
||||||
|
|
||||||
type ClientSet map[*Client]ChannelModeSet
|
type ClientSet map[*Client]bool
|
||||||
|
|
||||||
func (clients ClientSet) Add(client *Client) {
|
func (clients ClientSet) Add(client *Client) {
|
||||||
clients[client] = make(ChannelModeSet)
|
clients[client] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (clients ClientSet) Remove(client *Client) {
|
func (clients ClientSet) Remove(client *Client) {
|
||||||
delete(clients, client)
|
delete(clients, client)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (clients ClientSet) HasMode(client *Client, mode ChannelMode) bool {
|
func (clients ClientSet) Has(client *Client) bool {
|
||||||
modes, ok := clients[client]
|
return clients[client]
|
||||||
|
}
|
||||||
|
|
||||||
|
type MemberSet map[*Client]ChannelModeSet
|
||||||
|
|
||||||
|
func (members MemberSet) Add(member *Client) {
|
||||||
|
members[member] = make(ChannelModeSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (members MemberSet) Remove(member *Client) {
|
||||||
|
delete(members, member)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (members MemberSet) Has(member *Client) bool {
|
||||||
|
_, ok := members[member]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (members MemberSet) HasMode(member *Client, mode ChannelMode) bool {
|
||||||
|
modes, ok := members[member]
|
||||||
if !ok {
|
if !ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return modes[mode]
|
return modes[mode]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (clients ClientSet) Has(client *Client) bool {
|
|
||||||
_, ok := clients[client]
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
type ChannelSet map[*Channel]bool
|
type ChannelSet map[*Channel]bool
|
||||||
|
|
||||||
func (channels ChannelSet) Add(channel *Channel) {
|
func (channels ChannelSet) Add(channel *Channel) {
|
||||||
@ -141,6 +165,7 @@ type Replier interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Reply interface {
|
type Reply interface {
|
||||||
|
Code() ReplyCode
|
||||||
Format(*Client) []string
|
Format(*Client) []string
|
||||||
Source() Identifier
|
Source() Identifier
|
||||||
}
|
}
|
||||||
@ -172,6 +197,10 @@ type ChannelCommand interface {
|
|||||||
HandleChannel(channel *Channel)
|
HandleChannel(channel *Channel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ClientCommand interface {
|
||||||
|
HandleClient(client *Client)
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// structs
|
// structs
|
||||||
//
|
//
|
||||||
|
Loading…
Reference in New Issue
Block a user