irc/server.go

172 lines
4.0 KiB
Go

package irc
import (
"crypto/tls"
"fmt"
"net"
"sync"
"time"
"github.com/sorcix/irc"
)
// Server represents an IRC server
type Server struct {
Config ServerConfig
clients map[net.Addr]*Client
clientMutex sync.RWMutex
clientsByNick map[string]*Client
clientByNickMutex sync.RWMutex
Prefix *irc.Prefix
CommandsMux CommandsMux
created time.Time
channels map[string]*Channel
channelMutex sync.RWMutex
OperAuthMethod
}
// ServerConfig contains configuration data for seeding a server
type ServerConfig struct {
Name string
MOTD string
Version string
TLSConfig *tls.Config
Addr string
Password string
}
// 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.CommandsMux = NewCommandsMux()
s.created = time.Now()
s.clientsByNick = map[string]*Client{}
s.Prefix = &irc.Prefix{Name: config.Name}
s.channels = map[string]*Channel{}
if len(s.Config.Name) == 0 {
s.Config.Name = "localhost"
}
if len(s.Config.Version) == 0 {
s.Config.Version = "1.0"
}
return &s
}
// AddClient adds a new Client
func (s *Server) AddClient(client *Client) {
s.clientMutex.Lock()
defer s.clientMutex.Unlock()
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())
}
// 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]
}
// 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
}
// 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)
}
// 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
}
// GetClientByNick returns a client with the corresponding nickname
func (s *Server) GetClientByNick(nick string) (*Client, bool) {
s.clientByNickMutex.RLock()
defer s.clientByNickMutex.RUnlock()
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)
} else {
listener, err = net.Listen("tcp", s.Config.Addr)
}
if err != nil {
fmt.Println("Error starting listner", err.Error())
return
}
for {
conn, err := listener.Accept()
if err != nil {
fmt.Println("Error accepting connection", err.Error())
//return
continue
}
ircConn := irc.NewConn(conn)
client := s.newClient(ircConn, conn)
defer client.Close()
go func() {
fmt.Println("Incoming connection from:", conn.RemoteAddr())
client.handleIncoming()
fmt.Println("Disconnected with:", conn.RemoteAddr())
}()
}
}
// 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
}