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"
 | 
			
		||||
	"net"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/sorcix/irc"
 | 
			
		||||
@ -22,26 +23,30 @@ type Client struct {
 | 
			
		||||
 | 
			
		||||
	Prefix *irc.Prefix
 | 
			
		||||
 | 
			
		||||
	server     *Server
 | 
			
		||||
	Server     *Server
 | 
			
		||||
	authorized bool
 | 
			
		||||
 | 
			
		||||
	idleTimer *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 {
 | 
			
		||||
	client := &Client{Conn: ircConn, conn: conn, server: s}
 | 
			
		||||
	client.authorized = len(s.config.Password) == 0
 | 
			
		||||
	client := &Client{Conn: ircConn, conn: conn, Server: s}
 | 
			
		||||
	client.authorized = len(s.Config.Password) == 0
 | 
			
		||||
	client.idleTimer = time.AfterFunc(time.Minute, client.idle)
 | 
			
		||||
	client.channels = map[string]*Channel{}
 | 
			
		||||
	return client
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Close cleans up the IRC client and closes the connection
 | 
			
		||||
func (c *Client) Close() error {
 | 
			
		||||
	c.server.RemoveClient(c)
 | 
			
		||||
	c.server.RemoveClientNick(c)
 | 
			
		||||
	c.Server.RemoveClient(c)
 | 
			
		||||
	c.Server.RemoveClientNick(c)
 | 
			
		||||
 | 
			
		||||
	return c.Conn.Close()
 | 
			
		||||
}
 | 
			
		||||
@ -59,7 +64,7 @@ func (c *Client) Pong() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Client) handleIncoming() {
 | 
			
		||||
	c.server.AddClient(c)
 | 
			
		||||
	c.Server.AddClient(c)
 | 
			
		||||
	for {
 | 
			
		||||
		message, err := c.Decode()
 | 
			
		||||
		if err != nil || message == nil {
 | 
			
		||||
@ -81,7 +86,7 @@ func (c *Client) handleIncoming() {
 | 
			
		||||
			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() {
 | 
			
		||||
	// Have client leave/part each channel
 | 
			
		||||
	for _, channel := range c.GetChannels() {
 | 
			
		||||
		channel.Quit(c, "")
 | 
			
		||||
	}
 | 
			
		||||
	c.Quit()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Quit sends the IRC Quit command and closes the connection
 | 
			
		||||
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}}
 | 
			
		||||
 | 
			
		||||
	c.Encode(&m)
 | 
			
		||||
@ -112,55 +121,55 @@ func (c *Client) Welcome() {
 | 
			
		||||
	// Have all client info now
 | 
			
		||||
	c.Prefix = &irc.Prefix{Name: c.Nickname, User: c.Username, Host: c.Host}
 | 
			
		||||
 | 
			
		||||
	m := irc.Message{Prefix: c.server.Prefix, Command: irc.RPL_WELCOME,
 | 
			
		||||
		Params: []string{c.Nickname, c.server.config.Welcome}}
 | 
			
		||||
	m := irc.Message{Prefix: c.Server.Prefix, Command: irc.RPL_WELCOME,
 | 
			
		||||
		Params: []string{c.Nickname, c.Server.Config.Welcome}}
 | 
			
		||||
 | 
			
		||||
	err := c.Encode(&m)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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)}}
 | 
			
		||||
	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)}}
 | 
			
		||||
 | 
			
		||||
	err = c.Encode(&m)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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)}}
 | 
			
		||||
	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)}}
 | 
			
		||||
 | 
			
		||||
	err = c.Encode(&m)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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)}}
 | 
			
		||||
	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)}}
 | 
			
		||||
 | 
			
		||||
	err = c.Encode(&m)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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)}}
 | 
			
		||||
	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)}}
 | 
			
		||||
 | 
			
		||||
	err = c.Encode(&m)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	m = irc.Message{Prefix: c.server.Prefix, Command: irc.RPL_MOTD,
 | 
			
		||||
		Params: []string{c.Nickname, c.server.config.MOTD}}
 | 
			
		||||
	m = irc.Message{Prefix: c.Server.Prefix, Command: irc.RPL_MOTD,
 | 
			
		||||
		Params: []string{c.Nickname, c.Server.Config.MOTD}}
 | 
			
		||||
 | 
			
		||||
	err = c.Encode(&m)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		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"}}
 | 
			
		||||
 | 
			
		||||
	err = c.Encode(&m)
 | 
			
		||||
@ -168,3 +177,22 @@ func (c *Client) Welcome() {
 | 
			
		||||
		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 (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"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
 | 
			
		||||
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.Close()
 | 
			
		||||
@ -48,7 +57,7 @@ func QuitHandler(message *irc.Message, client *Client) {
 | 
			
		||||
func NickHandler(message *irc.Message, client *Client) {
 | 
			
		||||
 | 
			
		||||
	var m irc.Message
 | 
			
		||||
	name := client.server.config.Name
 | 
			
		||||
	name := client.Server.Config.Name
 | 
			
		||||
	nickname := client.Nickname
 | 
			
		||||
 | 
			
		||||
	if len(message.Params) == 0 {
 | 
			
		||||
@ -59,7 +68,7 @@ func NickHandler(message *irc.Message, client *Client) {
 | 
			
		||||
 | 
			
		||||
	newNickname := message.Params[0]
 | 
			
		||||
 | 
			
		||||
	_, found := client.server.ClientsByNick[newNickname]
 | 
			
		||||
	_, found := client.Server.GetClientByNick(newNickname)
 | 
			
		||||
 | 
			
		||||
	switch {
 | 
			
		||||
	case !client.authorized:
 | 
			
		||||
@ -72,11 +81,11 @@ func NickHandler(message *irc.Message, client *Client) {
 | 
			
		||||
	default:
 | 
			
		||||
		if len(client.Nickname) == 0 && len(client.Username) != 0 { // Client is connected now, show MOTD ...
 | 
			
		||||
			client.Nickname = newNickname
 | 
			
		||||
			client.server.AddClientNick(client)
 | 
			
		||||
			client.Server.AddClientNick(client)
 | 
			
		||||
			client.Welcome()
 | 
			
		||||
		} else { //change client name
 | 
			
		||||
			client.Nickname = newNickname
 | 
			
		||||
			client.server.UpdateClientNick(client, nickname)
 | 
			
		||||
			client.Server.UpdateClientNick(client, nickname)
 | 
			
		||||
			//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
 | 
			
		||||
func UserHandler(message *irc.Message, client *Client) {
 | 
			
		||||
	var m irc.Message
 | 
			
		||||
	serverName := client.server.config.Name
 | 
			
		||||
	serverName := client.Server.Config.Name
 | 
			
		||||
	//nickname := client.Nickname
 | 
			
		||||
 | 
			
		||||
	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
 | 
			
		||||
type Server struct {
 | 
			
		||||
	config ServerConfig
 | 
			
		||||
	Config ServerConfig
 | 
			
		||||
 | 
			
		||||
	Clients     map[net.Addr]*Client
 | 
			
		||||
	clients     map[net.Addr]*Client
 | 
			
		||||
	clientMutex sync.RWMutex
 | 
			
		||||
 | 
			
		||||
	ClientsByNick     map[string]*Client
 | 
			
		||||
	clientsByNick     map[string]*Client
 | 
			
		||||
	clientByNickMutex sync.RWMutex
 | 
			
		||||
 | 
			
		||||
	Prefix      *irc.Prefix
 | 
			
		||||
	CommandsMux CommandsMux
 | 
			
		||||
	created     time.Time
 | 
			
		||||
 | 
			
		||||
	channels     map[string]*Channel
 | 
			
		||||
	channelMutex sync.RWMutex
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
func NewServer(config ServerConfig) *Server {
 | 
			
		||||
	s := Server{}
 | 
			
		||||
	s.config = config
 | 
			
		||||
	s.Clients = map[net.Addr]*Client{}
 | 
			
		||||
	s.Config = config
 | 
			
		||||
	s.clients = map[net.Addr]*Client{}
 | 
			
		||||
	s.CommandsMux = NewCommandsMux()
 | 
			
		||||
	s.created = time.Now()
 | 
			
		||||
	s.ClientsByNick = map[string]*Client{}
 | 
			
		||||
	s.clientsByNick = map[string]*Client{}
 | 
			
		||||
	s.Prefix = &irc.Prefix{Name: config.Name}
 | 
			
		||||
 | 
			
		||||
	s.channels = map[string]*Channel{}
 | 
			
		||||
	return &s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -53,60 +56,61 @@ func NewServer(config ServerConfig) *Server {
 | 
			
		||||
func (s *Server) AddClient(client *Client) {
 | 
			
		||||
	s.clientMutex.Lock()
 | 
			
		||||
	defer s.clientMutex.Unlock()
 | 
			
		||||
	s.Clients[client.conn.RemoteAddr()] = client
 | 
			
		||||
	s.clients[client.conn.RemoteAddr()] = client
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RemoveClient removes a client
 | 
			
		||||
func (s *Server) RemoveClient(client *Client) {
 | 
			
		||||
	s.clientMutex.Lock()
 | 
			
		||||
	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
 | 
			
		||||
func (s *Server) GetClient(addr net.Addr) *Client {
 | 
			
		||||
	s.clientMutex.RLock()
 | 
			
		||||
	defer s.clientMutex.RUnlock()
 | 
			
		||||
	return s.Clients[addr]
 | 
			
		||||
	return s.clients[addr]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddClientNick adds a client based on its nickname
 | 
			
		||||
func (s *Server) AddClientNick(client *Client) {
 | 
			
		||||
	s.clientByNickMutex.Lock()
 | 
			
		||||
	defer s.clientByNickMutex.Unlock()
 | 
			
		||||
	s.ClientsByNick[client.Nickname] = client
 | 
			
		||||
	s.clientsByNick[client.Nickname] = client
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RemoveClientNick removes a client based on its nickname
 | 
			
		||||
func (s *Server) RemoveClientNick(client *Client) {
 | 
			
		||||
	s.clientByNickMutex.Lock()
 | 
			
		||||
	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
 | 
			
		||||
func (s *Server) UpdateClientNick(client *Client, oldNick string) {
 | 
			
		||||
	s.clientByNickMutex.Lock()
 | 
			
		||||
	defer s.clientByNickMutex.Unlock()
 | 
			
		||||
	delete(s.ClientsByNick, oldNick)
 | 
			
		||||
	s.ClientsByNick[client.Nickname] = client
 | 
			
		||||
	delete(s.clientsByNick, oldNick)
 | 
			
		||||
	s.clientsByNick[client.Nickname] = client
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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()
 | 
			
		||||
	defer s.clientByNickMutex.RUnlock()
 | 
			
		||||
	return s.ClientsByNick[nick]
 | 
			
		||||
	c, ok := s.clientsByNick[nick]
 | 
			
		||||
	return c, ok
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Start the server listening on the configured port
 | 
			
		||||
func (s *Server) Start() {
 | 
			
		||||
	var listener net.Listener
 | 
			
		||||
	var err error
 | 
			
		||||
	if s.config.TLSConfig != nil {
 | 
			
		||||
		listener, err = tls.Listen("tcp", s.config.Addr, s.config.TLSConfig)
 | 
			
		||||
	if s.Config.TLSConfig != nil {
 | 
			
		||||
		listener, err = tls.Listen("tcp", s.Config.Addr, s.Config.TLSConfig)
 | 
			
		||||
	} else {
 | 
			
		||||
		listener, err = net.Listen("tcp", s.config.Addr)
 | 
			
		||||
		listener, err = net.Listen("tcp", s.Config.Addr)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user