Added channels. Closes #1.
Support for the following Commands added. JOIN, PART, PRIVMSG, NOTICE, and WHO
This commit is contained in:
parent
acfc3f2c60
commit
c9d9191a5a
254
channel.go
Normal file
254
channel.go
Normal file
@ -0,0 +1,254 @@
|
|||||||
|
package irc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/sorcix/irc"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Channel represents an IRC channel or room
|
||||||
|
type Channel struct {
|
||||||
|
Name string
|
||||||
|
Modes ChannelModeSet
|
||||||
|
Topic string
|
||||||
|
Key string
|
||||||
|
|
||||||
|
members map[string]ChannelModeSet
|
||||||
|
membersMutex sync.RWMutex
|
||||||
|
|
||||||
|
Server *Server
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewChannel creates and returns a new Channel
|
||||||
|
func NewChannel(s *Server, creator *Client) *Channel {
|
||||||
|
c := &Channel{}
|
||||||
|
c.members = map[string]ChannelModeSet{}
|
||||||
|
c.Server = s
|
||||||
|
c.Modes = NewChannelModeSet()
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Join handles a client joining the channel and notifies other channel members
|
||||||
|
func (c *Channel) Join(client *Client, key string) {
|
||||||
|
_, ok := c.members[client.Nickname]
|
||||||
|
if ok { // client is already in this channel
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(c.Key) != 0 { //if key is required, verify that client provided matching key
|
||||||
|
if c.Key != key {
|
||||||
|
m := irc.Message{Command: irc.ERR_BADCHANNELKEY}
|
||||||
|
err := client.Encode(&m)
|
||||||
|
if err != nil {
|
||||||
|
println(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
operator := len(c.members) == 0
|
||||||
|
|
||||||
|
c.AddMember(client)
|
||||||
|
client.AddChannel(c)
|
||||||
|
|
||||||
|
if operator { // Client is creating channel
|
||||||
|
// Creator should be a channel operator - Maybe check if it is a "safe" channel
|
||||||
|
c.AddMemberMode(client, ChannelModeOperator)
|
||||||
|
}
|
||||||
|
|
||||||
|
var m irc.Message
|
||||||
|
|
||||||
|
// Send topic if it exists
|
||||||
|
if len(c.Topic) != 0 {
|
||||||
|
m = irc.Message{Prefix: c.Server.Prefix, Command: irc.RPL_TOPIC, Params: []string{c.Name}, Trailing: c.Topic}
|
||||||
|
client.Encode(&m)
|
||||||
|
}
|
||||||
|
|
||||||
|
//Notify existing members that new member is joining
|
||||||
|
|
||||||
|
allMembers := make([]string, len(c.members))
|
||||||
|
i := 0
|
||||||
|
for member := range c.members {
|
||||||
|
allMembers[i] = member
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
// send list of users in channel
|
||||||
|
for i := 0; i < (len(c.members)/20)+1; i++ {
|
||||||
|
memberStr := "= " + c.Name + " :"
|
||||||
|
end := (i + 1) * 20
|
||||||
|
if end > len(c.members) {
|
||||||
|
end = len(c.members)
|
||||||
|
}
|
||||||
|
m := irc.Message{Prefix: client.Prefix, Command: irc.JOIN, Params: []string{c.Name}}
|
||||||
|
for _, member := range allMembers[i*20 : end] {
|
||||||
|
mClient, _ := client.Server.GetClientByNick(member)
|
||||||
|
|
||||||
|
if mClient != nil {
|
||||||
|
memberStr += mClient.Nickname + " "
|
||||||
|
|
||||||
|
mClient.Encode(&m)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
m = irc.Message{Prefix: c.Server.Prefix, Command: irc.RPL_NAMREPLY, Params: []string{client.Nickname, memberStr}}
|
||||||
|
client.Encode(&m)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
m = irc.Message{Prefix: c.Server.Prefix, Command: irc.RPL_ENDOFNAMES, Params: []string{client.Nickname, c.Name}, Trailing: "End of NAMES list"}
|
||||||
|
client.Encode(&m)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Part handles when a client leaves a channel
|
||||||
|
func (c *Channel) Part(client *Client, message string) {
|
||||||
|
|
||||||
|
_, ok := c.members[client.Nickname]
|
||||||
|
if !ok { // client is not in this channel
|
||||||
|
m := irc.Message{Prefix: client.Server.Prefix, Command: irc.ERR_NOTONCHANNEL, Params: []string{c.Name}, Trailing: "You're not on that channel"}
|
||||||
|
client.Encode(&m)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
m := irc.Message{Prefix: client.Prefix, Command: irc.PART, Params: []string{c.Name}}
|
||||||
|
if len(message) != 0 {
|
||||||
|
m.Trailing = message
|
||||||
|
}
|
||||||
|
|
||||||
|
c.SendMessage(&m)
|
||||||
|
|
||||||
|
c.RemoveMember(client)
|
||||||
|
client.RemoveChannel(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quit is when a client quits the server - at channel level, similar to part
|
||||||
|
func (c *Channel) Quit(client *Client, message string) {
|
||||||
|
|
||||||
|
_, ok := c.members[client.Nickname]
|
||||||
|
if !ok { // client is not in this channel
|
||||||
|
//m := irc.Message{Prefix: &irc.Prefix{Name: client.Server.config.Name}, Command: irc.ERR_NOTONCHANNEL, Params: []string{c.Name}, Trailing: "You're not on that channel"}
|
||||||
|
//client.Encode(&m)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
m := irc.Message{Prefix: client.Prefix, Command: irc.QUIT}
|
||||||
|
if len(message) != 0 {
|
||||||
|
m.Trailing = message
|
||||||
|
}
|
||||||
|
c.SendMessage(&m)
|
||||||
|
|
||||||
|
c.RemoveMember(client)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Message is when a Private Message is directed for this channel - forward the message to each member
|
||||||
|
func (c *Channel) Message(client *Client, message string) {
|
||||||
|
m := irc.Message{Prefix: client.Prefix, Command: irc.PRIVMSG, Params: []string{c.Name}, Trailing: message}
|
||||||
|
|
||||||
|
c.SendMessageToOthers(&m, client)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notice is when a Notice is directed for this channel - forward the notice to each member
|
||||||
|
func (c *Channel) Notice(client *Client, message string) {
|
||||||
|
m := irc.Message{Prefix: client.Prefix, Command: irc.NOTICE, Params: []string{c.Name}, Trailing: message}
|
||||||
|
|
||||||
|
c.SendMessageToOthers(&m, client)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendMessage allows sending an IRC message to all channel members
|
||||||
|
func (c *Channel) SendMessage(m *irc.Message) {
|
||||||
|
for member := range c.members {
|
||||||
|
mClient, _ := c.Server.GetClientByNick(member)
|
||||||
|
if mClient != nil {
|
||||||
|
mClient.Encode(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendMessageToOthers allows sending an IRC message to all other channel members
|
||||||
|
func (c *Channel) SendMessageToOthers(m *irc.Message, client *Client) {
|
||||||
|
for member := range c.members {
|
||||||
|
if member == client.Nickname {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
mClient, _ := c.Server.GetClientByNick(member)
|
||||||
|
|
||||||
|
if mClient != nil {
|
||||||
|
mClient.Encode(m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddMember adds a member to the channel
|
||||||
|
func (c *Channel) AddMember(client *Client) {
|
||||||
|
c.membersMutex.Lock()
|
||||||
|
defer c.membersMutex.Unlock()
|
||||||
|
_, ok := c.members[client.Nickname]
|
||||||
|
if ok { // client is already a member
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.members[client.Nickname] = NewChannelModeSet()
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveMember removes a member from the channel
|
||||||
|
func (c *Channel) RemoveMember(client *Client) {
|
||||||
|
c.membersMutex.Lock()
|
||||||
|
defer c.membersMutex.Unlock()
|
||||||
|
delete(c.members, client.Nickname)
|
||||||
|
if len(c.members) == 0 { // NO more members
|
||||||
|
c.delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddMemberMode adds a mode for the member of the channel
|
||||||
|
func (c *Channel) AddMemberMode(client *Client, mode ChannelMode) {
|
||||||
|
c.membersMutex.Lock()
|
||||||
|
defer c.membersMutex.Unlock()
|
||||||
|
m, ok := c.members[client.Nickname]
|
||||||
|
if ok {
|
||||||
|
m.AddMode(mode)
|
||||||
|
c.members[client.Nickname] = m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveMemberMode removes a mode from the member of the channel
|
||||||
|
func (c *Channel) RemoveMemberMode(client *Client, mode ChannelMode) {
|
||||||
|
c.membersMutex.Lock()
|
||||||
|
defer c.membersMutex.Unlock()
|
||||||
|
m, ok := c.members[client.Nickname]
|
||||||
|
if ok {
|
||||||
|
m.RemoveMode(mode)
|
||||||
|
c.members[client.Nickname] = m
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Channel) delete() {
|
||||||
|
if len(c.members) != 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Server.RemoveChannel(c)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var channelStarters = map[uint8]interface{}{'&': nil, '#': nil, '+': nil, '!': nil}
|
||||||
|
|
||||||
|
// validName checks if it meets parameters found in rfc2812 1.3
|
||||||
|
func (c *Channel) validName() bool {
|
||||||
|
_, ok := channelStarters[c.Name[0]]
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(c.Name, " ") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.ContainsRune(c.Name, '\a') { // \a is the same as ^G
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Name = strings.TrimSuffix(c.Name, ",") //trim comma off of the end
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
72
client.go
72
client.go
@ -5,6 +5,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sorcix/irc"
|
"github.com/sorcix/irc"
|
||||||
@ -22,26 +23,30 @@ type Client struct {
|
|||||||
|
|
||||||
Prefix *irc.Prefix
|
Prefix *irc.Prefix
|
||||||
|
|
||||||
server *Server
|
Server *Server
|
||||||
authorized bool
|
authorized bool
|
||||||
|
|
||||||
idleTimer *time.Timer
|
idleTimer *time.Timer
|
||||||
quitTimer *time.Timer
|
quitTimer *time.Timer
|
||||||
|
|
||||||
awayMessage string
|
AwayMessage string
|
||||||
|
|
||||||
|
channels map[string]*Channel
|
||||||
|
channelMutex sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) newClient(ircConn *irc.Conn, conn net.Conn) *Client {
|
func (s *Server) newClient(ircConn *irc.Conn, conn net.Conn) *Client {
|
||||||
client := &Client{Conn: ircConn, conn: conn, server: s}
|
client := &Client{Conn: ircConn, conn: conn, Server: s}
|
||||||
client.authorized = len(s.config.Password) == 0
|
client.authorized = len(s.Config.Password) == 0
|
||||||
client.idleTimer = time.AfterFunc(time.Minute, client.idle)
|
client.idleTimer = time.AfterFunc(time.Minute, client.idle)
|
||||||
|
client.channels = map[string]*Channel{}
|
||||||
return client
|
return client
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close cleans up the IRC client and closes the connection
|
// Close cleans up the IRC client and closes the connection
|
||||||
func (c *Client) Close() error {
|
func (c *Client) Close() error {
|
||||||
c.server.RemoveClient(c)
|
c.Server.RemoveClient(c)
|
||||||
c.server.RemoveClientNick(c)
|
c.Server.RemoveClientNick(c)
|
||||||
|
|
||||||
return c.Conn.Close()
|
return c.Conn.Close()
|
||||||
}
|
}
|
||||||
@ -59,7 +64,7 @@ func (c *Client) Pong() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) handleIncoming() {
|
func (c *Client) handleIncoming() {
|
||||||
c.server.AddClient(c)
|
c.Server.AddClient(c)
|
||||||
for {
|
for {
|
||||||
message, err := c.Decode()
|
message, err := c.Decode()
|
||||||
if err != nil || message == nil {
|
if err != nil || message == nil {
|
||||||
@ -81,7 +86,7 @@ func (c *Client) handleIncoming() {
|
|||||||
c.quitTimer = nil
|
c.quitTimer = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
c.server.CommandsMux.ServeIRC(message, c)
|
c.Server.CommandsMux.ServeIRC(message, c)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,12 +98,16 @@ func (c *Client) idle() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) quit() {
|
func (c *Client) quit() {
|
||||||
|
// Have client leave/part each channel
|
||||||
|
for _, channel := range c.GetChannels() {
|
||||||
|
channel.Quit(c, "")
|
||||||
|
}
|
||||||
c.Quit()
|
c.Quit()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Quit sends the IRC Quit command and closes the connection
|
// Quit sends the IRC Quit command and closes the connection
|
||||||
func (c *Client) Quit() {
|
func (c *Client) Quit() {
|
||||||
m := irc.Message{Prefix: &irc.Prefix{Name: c.server.config.Name}, Command: irc.QUIT,
|
m := irc.Message{Prefix: &irc.Prefix{Name: c.Server.Config.Name}, Command: irc.QUIT,
|
||||||
Params: []string{c.Nickname}}
|
Params: []string{c.Nickname}}
|
||||||
|
|
||||||
c.Encode(&m)
|
c.Encode(&m)
|
||||||
@ -112,55 +121,55 @@ func (c *Client) Welcome() {
|
|||||||
// Have all client info now
|
// Have all client info now
|
||||||
c.Prefix = &irc.Prefix{Name: c.Nickname, User: c.Username, Host: c.Host}
|
c.Prefix = &irc.Prefix{Name: c.Nickname, User: c.Username, Host: c.Host}
|
||||||
|
|
||||||
m := irc.Message{Prefix: c.server.Prefix, Command: irc.RPL_WELCOME,
|
m := irc.Message{Prefix: c.Server.Prefix, Command: irc.RPL_WELCOME,
|
||||||
Params: []string{c.Nickname, c.server.config.Welcome}}
|
Params: []string{c.Nickname, c.Server.Config.Welcome}}
|
||||||
|
|
||||||
err := c.Encode(&m)
|
err := c.Encode(&m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
m = irc.Message{Prefix: c.server.Prefix, Command: irc.RPL_YOURHOST,
|
m = irc.Message{Prefix: c.Server.Prefix, Command: irc.RPL_YOURHOST,
|
||||||
Params: []string{c.Nickname, fmt.Sprintf("Your host is %s", c.server.config.Name)}}
|
Params: []string{c.Nickname, fmt.Sprintf("Your host is %s", c.Server.Config.Name)}}
|
||||||
|
|
||||||
err = c.Encode(&m)
|
err = c.Encode(&m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
m = irc.Message{Prefix: c.server.Prefix, Command: irc.RPL_CREATED,
|
m = irc.Message{Prefix: c.Server.Prefix, Command: irc.RPL_CREATED,
|
||||||
Params: []string{c.Nickname, fmt.Sprintf("This server was created %s", c.server.created)}}
|
Params: []string{c.Nickname, fmt.Sprintf("This server was created %s", c.Server.created)}}
|
||||||
|
|
||||||
err = c.Encode(&m)
|
err = c.Encode(&m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
m = irc.Message{Prefix: c.server.Prefix, Command: irc.RPL_MYINFO,
|
m = irc.Message{Prefix: c.Server.Prefix, Command: irc.RPL_MYINFO,
|
||||||
Params: []string{c.Nickname, fmt.Sprintf("%s - Golang IRC server", c.server.config.Name)}}
|
Params: []string{c.Nickname, fmt.Sprintf("%s - Golang IRC server", c.Server.Config.Name)}}
|
||||||
|
|
||||||
err = c.Encode(&m)
|
err = c.Encode(&m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
m = irc.Message{Prefix: c.server.Prefix, Command: irc.RPL_MOTDSTART,
|
m = irc.Message{Prefix: c.Server.Prefix, Command: irc.RPL_MOTDSTART,
|
||||||
Params: []string{c.Nickname, fmt.Sprintf("%s - Message of the day", c.server.config.Name)}}
|
Params: []string{c.Nickname, fmt.Sprintf("%s - Message of the day", c.Server.Config.Name)}}
|
||||||
|
|
||||||
err = c.Encode(&m)
|
err = c.Encode(&m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
m = irc.Message{Prefix: c.server.Prefix, Command: irc.RPL_MOTD,
|
m = irc.Message{Prefix: c.Server.Prefix, Command: irc.RPL_MOTD,
|
||||||
Params: []string{c.Nickname, c.server.config.MOTD}}
|
Params: []string{c.Nickname, c.Server.Config.MOTD}}
|
||||||
|
|
||||||
err = c.Encode(&m)
|
err = c.Encode(&m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
m = irc.Message{Prefix: c.server.Prefix, Command: irc.RPL_ENDOFMOTD,
|
m = irc.Message{Prefix: c.Server.Prefix, Command: irc.RPL_ENDOFMOTD,
|
||||||
Params: []string{c.Nickname, "End of MOTD"}}
|
Params: []string{c.Nickname, "End of MOTD"}}
|
||||||
|
|
||||||
err = c.Encode(&m)
|
err = c.Encode(&m)
|
||||||
@ -168,3 +177,22 @@ func (c *Client) Welcome() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddChannel adds a channel to the client's active list
|
||||||
|
func (c *Client) AddChannel(channel *Channel) {
|
||||||
|
c.channelMutex.Lock()
|
||||||
|
defer c.channelMutex.Unlock()
|
||||||
|
c.channels[channel.Name] = channel
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveChannel removes a channel to the client's active list
|
||||||
|
func (c *Client) RemoveChannel(channel *Channel) {
|
||||||
|
c.channelMutex.Lock()
|
||||||
|
defer c.channelMutex.Unlock()
|
||||||
|
delete(c.channels, channel.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetChannels gets a list of channels this client is joined to
|
||||||
|
func (c *Client) GetChannels() map[string]*Channel {
|
||||||
|
return c.channels
|
||||||
|
}
|
||||||
|
189
commands.go
189
commands.go
@ -2,6 +2,7 @@ package irc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/sorcix/irc"
|
"github.com/sorcix/irc"
|
||||||
)
|
)
|
||||||
@ -37,7 +38,15 @@ func PongHandler(message *irc.Message, client *Client) {
|
|||||||
// Implemented according to RFC 1459 4.1.6 and RFC 2812 3.1.7
|
// Implemented according to RFC 1459 4.1.6 and RFC 2812 3.1.7
|
||||||
func QuitHandler(message *irc.Message, client *Client) {
|
func QuitHandler(message *irc.Message, client *Client) {
|
||||||
|
|
||||||
m := irc.Message{Prefix: client.server.Prefix, Command: irc.ERROR, Trailing: "quit"}
|
var leavingMessage string
|
||||||
|
if len(message.Params) != 0 {
|
||||||
|
leavingMessage = message.Params[0]
|
||||||
|
}
|
||||||
|
for _, channel := range client.GetChannels() {
|
||||||
|
channel.Quit(client, leavingMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
m := irc.Message{Prefix: client.Server.Prefix, Command: irc.ERROR, Trailing: "quit"}
|
||||||
|
|
||||||
client.Encode(&m)
|
client.Encode(&m)
|
||||||
client.Close()
|
client.Close()
|
||||||
@ -48,7 +57,7 @@ func QuitHandler(message *irc.Message, client *Client) {
|
|||||||
func NickHandler(message *irc.Message, client *Client) {
|
func NickHandler(message *irc.Message, client *Client) {
|
||||||
|
|
||||||
var m irc.Message
|
var m irc.Message
|
||||||
name := client.server.config.Name
|
name := client.Server.Config.Name
|
||||||
nickname := client.Nickname
|
nickname := client.Nickname
|
||||||
|
|
||||||
if len(message.Params) == 0 {
|
if len(message.Params) == 0 {
|
||||||
@ -59,7 +68,7 @@ func NickHandler(message *irc.Message, client *Client) {
|
|||||||
|
|
||||||
newNickname := message.Params[0]
|
newNickname := message.Params[0]
|
||||||
|
|
||||||
_, found := client.server.ClientsByNick[newNickname]
|
_, found := client.Server.GetClientByNick(newNickname)
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case !client.authorized:
|
case !client.authorized:
|
||||||
@ -72,11 +81,11 @@ func NickHandler(message *irc.Message, client *Client) {
|
|||||||
default:
|
default:
|
||||||
if len(client.Nickname) == 0 && len(client.Username) != 0 { // Client is connected now, show MOTD ...
|
if len(client.Nickname) == 0 && len(client.Username) != 0 { // Client is connected now, show MOTD ...
|
||||||
client.Nickname = newNickname
|
client.Nickname = newNickname
|
||||||
client.server.AddClientNick(client)
|
client.Server.AddClientNick(client)
|
||||||
client.Welcome()
|
client.Welcome()
|
||||||
} else { //change client name
|
} else { //change client name
|
||||||
client.Nickname = newNickname
|
client.Nickname = newNickname
|
||||||
client.server.UpdateClientNick(client, nickname)
|
client.Server.UpdateClientNick(client, nickname)
|
||||||
//fmt.Println("Updating client name")
|
//fmt.Println("Updating client name")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -91,7 +100,7 @@ func NickHandler(message *irc.Message, client *Client) {
|
|||||||
// Implemented according to RFC 1459 4.1.3 and RFC 2812 3.1.3
|
// Implemented according to RFC 1459 4.1.3 and RFC 2812 3.1.3
|
||||||
func UserHandler(message *irc.Message, client *Client) {
|
func UserHandler(message *irc.Message, client *Client) {
|
||||||
var m irc.Message
|
var m irc.Message
|
||||||
serverName := client.server.config.Name
|
serverName := client.Server.Config.Name
|
||||||
//nickname := client.Nickname
|
//nickname := client.Nickname
|
||||||
|
|
||||||
if len(client.Username) != 0 { // Already registered
|
if len(client.Username) != 0 { // Already registered
|
||||||
@ -125,3 +134,171 @@ func UserHandler(message *irc.Message, client *Client) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// JoinHandler is a CommandHandler to respond to IRC JOIN commands from a client
|
||||||
|
// Implemented according to RFC 1459 4.2.1 and RFC 2812 3.2.1
|
||||||
|
func JoinHandler(message *irc.Message, client *Client) {
|
||||||
|
channelNames := message.Params[0]
|
||||||
|
if channelNames == "0" { // Leave all channels
|
||||||
|
for _, channel := range client.GetChannels() {
|
||||||
|
channel.Part(client, "")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
channelList := strings.Split(channelNames, ",")
|
||||||
|
keys := ""
|
||||||
|
if len(message.Params) >= 2 {
|
||||||
|
keys = message.Params[1]
|
||||||
|
}
|
||||||
|
keyList := strings.Split(keys, ",")
|
||||||
|
|
||||||
|
for i, cName := range channelList {
|
||||||
|
var key string
|
||||||
|
if len(keyList) > i {
|
||||||
|
key = keyList[i]
|
||||||
|
}
|
||||||
|
channel, ok := client.Server.GetChannel(cName)
|
||||||
|
if !ok { // Channel doesn't exist yet
|
||||||
|
channel = NewChannel(client.Server, client)
|
||||||
|
channel.Name = cName
|
||||||
|
|
||||||
|
//channel.Members[client.Nickname] = client.Nickname
|
||||||
|
|
||||||
|
channel.Key = key
|
||||||
|
|
||||||
|
client.Server.AddChannel(channel)
|
||||||
|
|
||||||
|
} else { //Channel already exists
|
||||||
|
}
|
||||||
|
//Notify channel members of new member
|
||||||
|
channel.Join(client, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// PartHandler is a CommandHandler to respond to IRC PART commands from a client
|
||||||
|
// Implemented according to RFC 1459 4.2.2 and RFC 2812 3.2.2
|
||||||
|
func PartHandler(message *irc.Message, client *Client) {
|
||||||
|
if len(message.Params) == 0 {
|
||||||
|
m := irc.Message{Prefix: client.Server.Prefix, Command: irc.ERR_NEEDMOREPARAMS, Trailing: "Not enough parameters"}
|
||||||
|
client.Encode(&m)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, cName := range message.Params {
|
||||||
|
channel, ok := client.Server.GetChannel(cName)
|
||||||
|
if !ok { // Channel doesn't exist yet
|
||||||
|
|
||||||
|
m := irc.Message{Prefix: client.Server.Prefix, Command: irc.ERR_NOSUCHCHANNEL, Trailing: "You're not on that channel"}
|
||||||
|
client.Encode(&m)
|
||||||
|
continue
|
||||||
|
|
||||||
|
} else { //Channel already exists
|
||||||
|
}
|
||||||
|
|
||||||
|
channel.Part(client, message.Trailing)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrivMsgHandler is a CommandHandler to respond to IRC PRIVMSG commands from a client
|
||||||
|
// Implemented according to RFC 1459 4.4.1 and RFC 2812 3.3.1
|
||||||
|
func PrivMsgHandler(message *irc.Message, client *Client) {
|
||||||
|
if len(message.Params) == 0 {
|
||||||
|
m := irc.Message{Prefix: client.Server.Prefix, Command: irc.ERR_NORECIPIENT, Params: []string{client.Nickname}, Trailing: "No recipient given (PRIVMSG)"}
|
||||||
|
client.Encode(&m)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(message.Params) > 1 {
|
||||||
|
m := irc.Message{Prefix: client.Server.Prefix, Command: irc.ERR_TOOMANYTARGETS, Params: []string{client.Nickname}}
|
||||||
|
client.Encode(&m)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(message.Trailing) == 0 {
|
||||||
|
m := irc.Message{Prefix: client.Server.Prefix, Command: irc.ERR_NOTEXTTOSEND, Params: []string{client.Nickname}, Trailing: "No text to send"}
|
||||||
|
client.Encode(&m)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
to := message.Params[0]
|
||||||
|
ch, ok := client.Server.GetChannel(to)
|
||||||
|
if ok { // message is to a channel
|
||||||
|
ch.Message(client, message.Trailing)
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
// message to a user?
|
||||||
|
cl, ok := client.Server.GetClientByNick(to)
|
||||||
|
if ok {
|
||||||
|
m := irc.Message{Prefix: client.Prefix, Command: irc.PRIVMSG, Params: []string{cl.Nickname}, Trailing: message.Trailing}
|
||||||
|
cl.Encode(&m)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
m := irc.Message{Prefix: client.Server.Prefix, Command: irc.ERR_NOSUCHNICK, Params: []string{client.Nickname}, Trailing: "No recipient given (PRIVMSG)"}
|
||||||
|
client.Encode(&m)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// NoticeHandler is a CommandHandler to respond to IRC NOTICE commands from a client
|
||||||
|
// Implemented according to RFC 1459 4.4.2 and RFC 2812 3.3.2
|
||||||
|
func NoticeHandler(message *irc.Message, client *Client) {
|
||||||
|
if len(message.Params) == 0 {
|
||||||
|
m := irc.Message{Prefix: client.Server.Prefix, Command: irc.ERR_NORECIPIENT, Params: []string{client.Nickname}, Trailing: "No recipient given (PRIVMSG)"}
|
||||||
|
client.Encode(&m)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(message.Params) > 1 {
|
||||||
|
m := irc.Message{Prefix: client.Server.Prefix, Command: irc.ERR_TOOMANYTARGETS, Params: []string{client.Nickname}}
|
||||||
|
client.Encode(&m)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(message.Trailing) == 0 {
|
||||||
|
m := irc.Message{Prefix: client.Server.Prefix, Command: irc.ERR_NOTEXTTOSEND, Params: []string{client.Nickname}, Trailing: "No text to send"}
|
||||||
|
client.Encode(&m)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
to := message.Params[0]
|
||||||
|
ch, ok := client.Server.GetChannel(to)
|
||||||
|
if ok { // message is to a channel
|
||||||
|
ch.Notice(client, message.Trailing)
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
// message to a user?
|
||||||
|
cl, ok := client.Server.GetClientByNick(to)
|
||||||
|
if ok {
|
||||||
|
m := irc.Message{Prefix: client.Prefix, Command: irc.NOTICE, Params: []string{cl.Nickname}, Trailing: message.Trailing}
|
||||||
|
cl.Encode(&m)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
m := irc.Message{Prefix: client.Server.Prefix, Command: irc.ERR_NOSUCHNICK, Params: []string{client.Nickname}, Trailing: "No recipient given (PRIVMSG)"}
|
||||||
|
client.Encode(&m)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// WhoHandler is a CommandHandler to respond to IRC WHO commands from a client
|
||||||
|
// Implemented according to RFC 1459 4.5.1 and RFC 2812 3.6.1
|
||||||
|
func WhoHandler(message *irc.Message, client *Client) {
|
||||||
|
if len(message.Params) == 0 {
|
||||||
|
//return listing of all users
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ch, ok := client.Server.GetChannel(message.Params[0])
|
||||||
|
if ok { //Channel exists
|
||||||
|
for clientName := range ch.members {
|
||||||
|
cl, found := client.Server.GetClientByNick(clientName)
|
||||||
|
|
||||||
|
if found {
|
||||||
|
msg := fmt.Sprintf("%s %s %s %s %s %s %s%s :%d %s", client.Nickname, ch.Name, cl.Name, cl.Host, client.Server.Config.Name, cl.Nickname, "H", "", 0, cl.RealName)
|
||||||
|
m := irc.Message{Prefix: client.Server.Prefix, Command: irc.RPL_WHOREPLY, Params: strings.Fields(msg)}
|
||||||
|
client.Encode(&m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m := irc.Message{Prefix: client.Server.Prefix, Command: irc.RPL_ENDOFWHO, Params: []string{client.Nickname, ch.Name}, Trailing: "End of WHO list"}
|
||||||
|
client.Encode(&m)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
119
modes.go
Normal file
119
modes.go
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
package irc
|
||||||
|
|
||||||
|
import "sync"
|
||||||
|
|
||||||
|
// UserMode - RFC 1459 4.2.3.2 and RFC 2812 3.1.5
|
||||||
|
type UserMode rune
|
||||||
|
|
||||||
|
const (
|
||||||
|
UserModeAway UserMode = 'a'
|
||||||
|
UserModeInvisible UserMode = 'i'
|
||||||
|
UserModeWallOps UserMode = 'w'
|
||||||
|
UserModeRestricted UserMode = 'r'
|
||||||
|
UserModeOperator UserMode = 'o'
|
||||||
|
UserModeLocalOperator UserMode = 'O'
|
||||||
|
UserModeServerNotice UserMode = 's' //obsolete
|
||||||
|
)
|
||||||
|
|
||||||
|
var UserModes = map[UserMode]interface{}{
|
||||||
|
UserModeAway: nil,
|
||||||
|
UserModeInvisible: nil,
|
||||||
|
UserModeWallOps: nil,
|
||||||
|
UserModeRestricted: nil,
|
||||||
|
UserModeOperator: nil,
|
||||||
|
UserModeLocalOperator: nil,
|
||||||
|
UserModeServerNotice: nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserModeSet struct {
|
||||||
|
userModes map[UserMode]interface{}
|
||||||
|
mutex sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUserModeSet() UserModeSet {
|
||||||
|
u := UserModeSet{}
|
||||||
|
u.userModes = map[UserMode]interface{}{}
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *UserModeSet) AddMode(mode UserMode) {
|
||||||
|
u.mutex.Lock()
|
||||||
|
defer u.mutex.Unlock()
|
||||||
|
u.userModes[mode] = nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *UserModeSet) RemoveMode(mode UserMode) {
|
||||||
|
u.mutex.Lock()
|
||||||
|
defer u.mutex.Unlock()
|
||||||
|
delete(u.userModes, mode)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *UserModeSet) String() string {
|
||||||
|
s := ""
|
||||||
|
for m, _ := range u.userModes {
|
||||||
|
s += string(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// ModeModifier - RFC 1459 4.2.3 and RFC 2812 3.1.5
|
||||||
|
type ModeModifier rune
|
||||||
|
|
||||||
|
const (
|
||||||
|
ModeModifierAdd ModeModifier = '+'
|
||||||
|
ModeModifierRemove ModeModifier = '-'
|
||||||
|
)
|
||||||
|
|
||||||
|
// ChannelMode - RFC 1459 4.2.3.1 and RFC 2812 3.2.3 and RFC 2811 4
|
||||||
|
type ChannelMode rune
|
||||||
|
|
||||||
|
const (
|
||||||
|
ChannelModeCreator ChannelMode = 'O'
|
||||||
|
ChannelModeOperator ChannelMode = 'o'
|
||||||
|
ChannelModeVoice ChannelMode = 'v'
|
||||||
|
|
||||||
|
ChannelModeAnonymous ChannelMode = 'a'
|
||||||
|
ChannelModeInviteOnly ChannelMode = 'i'
|
||||||
|
ChannelModeModerated ChannelMode = 'm'
|
||||||
|
ChannelModeNoOutsideMessages ChannelMode = 'n'
|
||||||
|
ChannelModeQuiet ChannelMode = 'q'
|
||||||
|
ChannelModePrivate ChannelMode = 'p'
|
||||||
|
ChannelModeSecret ChannelMode = 's'
|
||||||
|
ChannelModeReOp ChannelMode = 'r'
|
||||||
|
ChannelModeTopic ChannelMode = 't'
|
||||||
|
|
||||||
|
ChannelModeKey ChannelMode = 'k'
|
||||||
|
ChannelModeLimit ChannelMode = 'l'
|
||||||
|
|
||||||
|
ChannelModeBan ChannelMode = 'b'
|
||||||
|
ChannelModeExceptionMask ChannelMode = 'e'
|
||||||
|
ChannelModeInvitationMask ChannelMode = 'I'
|
||||||
|
)
|
||||||
|
|
||||||
|
type ChannelModeSet struct {
|
||||||
|
Modes map[ChannelMode]interface{}
|
||||||
|
mutex sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewChannelModeSet() ChannelModeSet {
|
||||||
|
c := ChannelModeSet{}
|
||||||
|
c.Modes = map[ChannelMode]interface{}{}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ChannelModeSet) AddMode(mode ChannelMode) {
|
||||||
|
c.mutex.Lock()
|
||||||
|
defer c.mutex.Unlock()
|
||||||
|
c.Modes[mode] = nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ChannelModeSet) RemoveMode(mode ChannelMode) {
|
||||||
|
c.mutex.Lock()
|
||||||
|
defer c.mutex.Unlock()
|
||||||
|
delete(c.Modes, mode)
|
||||||
|
|
||||||
|
}
|
64
server.go
64
server.go
@ -12,17 +12,20 @@ import (
|
|||||||
|
|
||||||
// Server represents an IRC server
|
// Server represents an IRC server
|
||||||
type Server struct {
|
type Server struct {
|
||||||
config ServerConfig
|
Config ServerConfig
|
||||||
|
|
||||||
Clients map[net.Addr]*Client
|
clients map[net.Addr]*Client
|
||||||
clientMutex sync.RWMutex
|
clientMutex sync.RWMutex
|
||||||
|
|
||||||
ClientsByNick map[string]*Client
|
clientsByNick map[string]*Client
|
||||||
clientByNickMutex sync.RWMutex
|
clientByNickMutex sync.RWMutex
|
||||||
|
|
||||||
Prefix *irc.Prefix
|
Prefix *irc.Prefix
|
||||||
CommandsMux CommandsMux
|
CommandsMux CommandsMux
|
||||||
created time.Time
|
created time.Time
|
||||||
|
|
||||||
|
channels map[string]*Channel
|
||||||
|
channelMutex sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServerConfig contains configuration data for seeding a server
|
// ServerConfig contains configuration data for seeding a server
|
||||||
@ -39,13 +42,13 @@ type ServerConfig struct {
|
|||||||
// NewServer creates and returns a new Server based on the provided config
|
// NewServer creates and returns a new Server based on the provided config
|
||||||
func NewServer(config ServerConfig) *Server {
|
func NewServer(config ServerConfig) *Server {
|
||||||
s := Server{}
|
s := Server{}
|
||||||
s.config = config
|
s.Config = config
|
||||||
s.Clients = map[net.Addr]*Client{}
|
s.clients = map[net.Addr]*Client{}
|
||||||
s.CommandsMux = NewCommandsMux()
|
s.CommandsMux = NewCommandsMux()
|
||||||
s.created = time.Now()
|
s.created = time.Now()
|
||||||
s.ClientsByNick = map[string]*Client{}
|
s.clientsByNick = map[string]*Client{}
|
||||||
s.Prefix = &irc.Prefix{Name: config.Name}
|
s.Prefix = &irc.Prefix{Name: config.Name}
|
||||||
|
s.channels = map[string]*Channel{}
|
||||||
return &s
|
return &s
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,60 +56,61 @@ func NewServer(config ServerConfig) *Server {
|
|||||||
func (s *Server) AddClient(client *Client) {
|
func (s *Server) AddClient(client *Client) {
|
||||||
s.clientMutex.Lock()
|
s.clientMutex.Lock()
|
||||||
defer s.clientMutex.Unlock()
|
defer s.clientMutex.Unlock()
|
||||||
s.Clients[client.conn.RemoteAddr()] = client
|
s.clients[client.conn.RemoteAddr()] = client
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveClient removes a client
|
// RemoveClient removes a client
|
||||||
func (s *Server) RemoveClient(client *Client) {
|
func (s *Server) RemoveClient(client *Client) {
|
||||||
s.clientMutex.Lock()
|
s.clientMutex.Lock()
|
||||||
defer s.clientMutex.Unlock()
|
defer s.clientMutex.Unlock()
|
||||||
delete(s.Clients, client.conn.RemoteAddr())
|
delete(s.clients, client.conn.RemoteAddr())
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetClient finds a client by its address and returns it
|
// GetClient finds a client by its address and returns it
|
||||||
func (s *Server) GetClient(addr net.Addr) *Client {
|
func (s *Server) GetClient(addr net.Addr) *Client {
|
||||||
s.clientMutex.RLock()
|
s.clientMutex.RLock()
|
||||||
defer s.clientMutex.RUnlock()
|
defer s.clientMutex.RUnlock()
|
||||||
return s.Clients[addr]
|
return s.clients[addr]
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddClientNick adds a client based on its nickname
|
// AddClientNick adds a client based on its nickname
|
||||||
func (s *Server) AddClientNick(client *Client) {
|
func (s *Server) AddClientNick(client *Client) {
|
||||||
s.clientByNickMutex.Lock()
|
s.clientByNickMutex.Lock()
|
||||||
defer s.clientByNickMutex.Unlock()
|
defer s.clientByNickMutex.Unlock()
|
||||||
s.ClientsByNick[client.Nickname] = client
|
s.clientsByNick[client.Nickname] = client
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveClientNick removes a client based on its nickname
|
// RemoveClientNick removes a client based on its nickname
|
||||||
func (s *Server) RemoveClientNick(client *Client) {
|
func (s *Server) RemoveClientNick(client *Client) {
|
||||||
s.clientByNickMutex.Lock()
|
s.clientByNickMutex.Lock()
|
||||||
defer s.clientByNickMutex.Unlock()
|
defer s.clientByNickMutex.Unlock()
|
||||||
delete(s.ClientsByNick, client.Nickname)
|
delete(s.clientsByNick, client.Nickname)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateClientNick updates the nickname of a client as it is stored by the server
|
// UpdateClientNick updates the nickname of a client as it is stored by the server
|
||||||
func (s *Server) UpdateClientNick(client *Client, oldNick string) {
|
func (s *Server) UpdateClientNick(client *Client, oldNick string) {
|
||||||
s.clientByNickMutex.Lock()
|
s.clientByNickMutex.Lock()
|
||||||
defer s.clientByNickMutex.Unlock()
|
defer s.clientByNickMutex.Unlock()
|
||||||
delete(s.ClientsByNick, oldNick)
|
delete(s.clientsByNick, oldNick)
|
||||||
s.ClientsByNick[client.Nickname] = client
|
s.clientsByNick[client.Nickname] = client
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetClientByNick returns a client with the corresponding nickname
|
// GetClientByNick returns a client with the corresponding nickname
|
||||||
func (s *Server) GetClientByNick(nick string) *Client {
|
func (s *Server) GetClientByNick(nick string) (*Client, bool) {
|
||||||
s.clientByNickMutex.RLock()
|
s.clientByNickMutex.RLock()
|
||||||
defer s.clientByNickMutex.RUnlock()
|
defer s.clientByNickMutex.RUnlock()
|
||||||
return s.ClientsByNick[nick]
|
c, ok := s.clientsByNick[nick]
|
||||||
|
return c, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the server listening on the configured port
|
// Start the server listening on the configured port
|
||||||
func (s *Server) Start() {
|
func (s *Server) Start() {
|
||||||
var listener net.Listener
|
var listener net.Listener
|
||||||
var err error
|
var err error
|
||||||
if s.config.TLSConfig != nil {
|
if s.Config.TLSConfig != nil {
|
||||||
listener, err = tls.Listen("tcp", s.config.Addr, s.config.TLSConfig)
|
listener, err = tls.Listen("tcp", s.Config.Addr, s.Config.TLSConfig)
|
||||||
} else {
|
} else {
|
||||||
listener, err = net.Listen("tcp", s.config.Addr)
|
listener, err = net.Listen("tcp", s.Config.Addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -134,3 +138,25 @@ func (s *Server) Start() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddChannel adds an active channel
|
||||||
|
func (s *Server) AddChannel(channel *Channel) {
|
||||||
|
s.channelMutex.Lock()
|
||||||
|
defer s.channelMutex.Unlock()
|
||||||
|
s.channels[channel.Name] = channel
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveChannel removes a channel from the active listing
|
||||||
|
func (s *Server) RemoveChannel(channel *Channel) {
|
||||||
|
s.channelMutex.Lock()
|
||||||
|
defer s.channelMutex.Unlock()
|
||||||
|
delete(s.channels, channel.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetChannel finds and returns an active channel with a matching name if it exists
|
||||||
|
func (s *Server) GetChannel(channelName string) (*Channel, bool) {
|
||||||
|
s.channelMutex.RLock()
|
||||||
|
defer s.channelMutex.RUnlock()
|
||||||
|
c, ok := s.channels[channelName]
|
||||||
|
return c, ok
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user