Commit 5a64ce6b authored by Daniel Kertesz's avatar Daniel Kertesz

more names refactoring

parent 7419c895
......@@ -15,7 +15,7 @@ type Channel struct {
TopicTime time.Time
Password string
BanList []Hostmask
Members map[client.GUID]*client.Client
Members map[client.GUID]*client.User
Privileges map[client.GUID]utils.Flags
Modes utils.Flags
}
......@@ -25,7 +25,7 @@ func NewChannel(name string) *Channel {
Name: name,
Modes: utils.NewChannelFlags(),
Created: time.Now(),
Members: make(map[client.GUID]*client.Client),
Members: make(map[client.GUID]*client.User),
Privileges: make(map[client.GUID]utils.Flags),
}
......@@ -49,7 +49,7 @@ func (ch *Channel) SerializeChanModes(includePassword bool) string {
return rv
}
func (ch *Channel) addMember(cli *client.Client) {
func (ch *Channel) addMember(cli *client.User) {
var isFounder bool = false
if len(ch.Members) == 0 {
......@@ -65,7 +65,7 @@ func (ch *Channel) addMember(cli *client.Client) {
cli.AddChannel(ch.Name)
}
func (ch *Channel) removeMember(cli *client.Client) {
func (ch *Channel) removeMember(cli *client.User) {
if _, ok := ch.Members[cli.GUID()]; ok {
delete(ch.Members, cli.GUID())
}
......@@ -76,12 +76,12 @@ func (ch *Channel) removeMember(cli *client.Client) {
cli.RemoveChannel(ch.Name)
}
func (ch *Channel) hasClient(cli *client.Client) bool {
func (ch *Channel) hasClient(cli *client.User) bool {
_, ok := ch.Members[cli.GUID()]
return ok
}
func (ch *Channel) isVoice(cli *client.Client) bool {
func (ch *Channel) isVoice(cli *client.User) bool {
if flags, ok := ch.Privileges[cli.GUID()]; ok {
return flags.Has(utils.CHAN_VOICE)
}
......@@ -90,7 +90,7 @@ func (ch *Channel) isVoice(cli *client.Client) bool {
return false
}
func (ch *Channel) isOp(cli *client.Client) bool {
func (ch *Channel) isOp(cli *client.User) bool {
if flags, ok := ch.Privileges[cli.GUID()]; ok {
return flags.Has(utils.CHAN_OP)
}
......
......@@ -17,10 +17,6 @@ const (
// maximum number of lines in a client queue before... we do something.
ClientWriteQueue = 256
// ClientEvent event types
CONNECTED = iota
DISCONNECTED
// Connection statuses
PRE_REGISTRATION = iota
CAP_NEGOTIATION
......@@ -30,18 +26,15 @@ const (
type GUID string
type ClientMessage struct {
C *Client
M *message.Message
}
type ClientEvent struct {
C *Client
Type int
type UserEvent struct {
User *User
Connected bool
Disconnected bool
Message *message.Message
}
// Client is any IRC client connection
type Client struct {
// User is any IRC client connection
type User struct {
conn net.Conn
// this must be buffered!
......@@ -67,11 +60,11 @@ type Client struct {
log *log.Entry
}
func (c *Client) String() string {
func (c *User) String() string {
return fmt.Sprintf("<Client(nick=%q, ident=%q, host=%q, realname=%q)>", c.Nick, c.Ident, c.Host, c.Realname)
}
func New(conn net.Conn) (*Client, error) {
func New(conn net.Conn) (*User, error) {
id := uuid.NewV4()
now := time.Now()
......@@ -93,7 +86,7 @@ func New(conn net.Conn) (*Client, error) {
remoteHost = names[0]
}
c := &Client{
c := &User{
// It's common practice to set the nickname as "*" before the client registers
Nick: "*",
LoginTime: now,
......@@ -111,39 +104,39 @@ func New(conn net.Conn) (*Client, error) {
}
// Hostmask returns the hostmask for a user, in the format "ident!user@hostname"
func (c *Client) Hostmask() string {
func (c *User) Hostmask() string {
return fmt.Sprintf("%s!%s@%s", c.Nick, c.Ident, c.Host)
}
func (c *Client) IsRegistered() bool {
func (c *User) IsRegistered() bool {
return c.State >= REGISTERED
}
func (c *Client) IsAway() bool {
func (c *User) IsAway() bool {
return c.Flags.Has(utils.USER_AWAY)
}
func (c *Client) SetAway(message string) {
func (c *User) SetAway(message string) {
c.awayReason = message
c.Flags.Set(utils.USER_AWAY)
}
func (c *Client) ClearAway() {
func (c *User) ClearAway() {
c.awayReason = ""
c.Flags.Unset(utils.USER_AWAY)
}
func (c *Client) AwayReason() string {
func (c *User) AwayReason() string {
return c.awayReason
}
func (c *Client) UpdateLastMessage() {
func (c *User) UpdateLastMessage() {
c.LastMessage = time.Now()
}
// Reader reads from the client socket, parse each line as "IRC message" and write the message to
// the processing channel (inChan).
func (c *Client) Reader(inChan chan<- ClientMessage, cliChan chan<- ClientEvent) {
func (c *User) Reader(userEvents chan<- UserEvent) {
scanner := bufio.NewScanner(c.conn)
for scanner.Scan() {
......@@ -154,7 +147,7 @@ func (c *Client) Reader(inChan chan<- ClientMessage, cliChan chan<- ClientEvent)
if err != nil {
panic(err)
}
inChan <- ClientMessage{c, msg}
userEvents <- UserEvent{User: c, Message: msg}
}
if err := scanner.Err(); err != nil {
......@@ -162,14 +155,14 @@ func (c *Client) Reader(inChan chan<- ClientMessage, cliChan chan<- ClientEvent)
}
// see http://www.tapirgames.com/blog/golang-channel-closing
cliChan <- ClientEvent{C: c, Type: DISCONNECTED}
userEvents <- UserEvent{User: c, Disconnected: true}
c.log.Debug("Exiting Reader")
c.exitWriter()
}
// TODO: find a way to send writes to a client and then close the connection; this will be used to
// send error messages and then terminate the client session.
func (c *Client) Writer() {
func (c *User) Writer() {
for line := range c.Out {
c.log.Debugf(">> %s", line)
payload := []byte(line + "\r\n")
......@@ -189,24 +182,24 @@ func (c *Client) Writer() {
}
// exitWriter closes the output channel, shutting down the goroutine that runs WriteResponse()
func (c *Client) exitWriter() {
func (c *User) exitWriter() {
close(c.Out)
}
func (c *Client) CloseConnection() error {
func (c *User) CloseConnection() error {
c.log.Info("Server closing connection for client")
return c.conn.Close()
}
func (c *Client) GUID() GUID {
func (c *User) GUID() GUID {
return GUID(c.Id.String())
}
func (c *Client) AddChannel(name string) {
func (c *User) AddChannel(name string) {
c.Channels = append(c.Channels, name)
}
func (c *Client) RemoveChannel(name string) {
func (c *User) RemoveChannel(name string) {
rv := make([]string, 0, len(c.Channels))
for _, c := range c.Channels {
if c != name {
......
......@@ -39,6 +39,8 @@ type ListenConfig struct {
Key string `yaml:"key"`
// Proxy enable HA-PROXY support.
Proxy bool `yaml:"proxy"`
// Server enable connections from other servers
Server bool `yaml:"server"`
}
type AdminConfig struct {
......
This diff is collapsed.
......@@ -8,27 +8,27 @@ import (
)
// 461 ERR_NEEDMOREPARAMS
func (s *Server) replyNeedMoreParams(cli *client.Client, command string) {
func (s *Server) replyNeedMoreParams(cli *client.User, command string) {
s.send(cli, message.NewMessage("461", []string{cli.Nick, command}, "Not enough parameters"))
}
// 403 ERR_NOSUCHCHANNEL
func (s *Server) replyNoSuchChannel(cli *client.Client, name string) {
func (s *Server) replyNoSuchChannel(cli *client.User, name string) {
s.send(cli, message.NewMessage("403", []string{cli.Nick, name}, "No such channel"))
}
//411 ERR_NORECIPIENT
func (s *Server) replyNoRecipient(cli *client.Client, command string) {
func (s *Server) replyNoRecipient(cli *client.User, command string) {
s.send(cli, message.NewMessage("411", []string{cli.Nick}, fmt.Sprintf("No recipient given (%s)", command)))
}
// 401 ERR_NOSUCHNICK
func (s *Server) replyNoSuchNick(cli *client.Client, nick string) {
func (s *Server) replyNoSuchNick(cli *client.User, nick string) {
s.send(cli, message.NewMessage("401", []string{cli.Nick, nick}, "No such nick/channel"))
}
// 315 RPL_ENDOFWHO
func (srv *Server) replyWhoEnd(cli *client.Client, name string) {
func (srv *Server) replyWhoEnd(cli *client.User, name string) {
resp := message.NewMessage("315", []string{cli.Nick, name}, "End of /WHO list")
srv.send(cli, resp)
}
......@@ -59,8 +59,8 @@ type Server struct {
handlers map[string]ircCmdHandler
channels map[string]*Channel
clients map[uuid.UUID]*client.Client // ID -> client
nicknames map[string]uuid.UUID // nickname -> ID
clients map[uuid.UUID]*client.User // ID -> client
nicknames map[string]uuid.UUID // nickname -> ID
ExitChan chan bool
config *Config
......@@ -84,7 +84,7 @@ func NewServer(config *Config) *Server {
startTime: time.Now(),
handlers: make(map[string]ircCmdHandler),
channels: make(map[string]*Channel),
clients: make(map[uuid.UUID]*client.Client),
clients: make(map[uuid.UUID]*client.User),
nicknames: make(map[string]uuid.UUID),
ExitChan: make(chan bool, 1),
......@@ -134,20 +134,17 @@ func (srv *Server) StartServer() {
log.Debug("Starting main loop")
// inChan is used to fan-in messages from all the connected clients
inChan := make(chan client.ClientMessage)
// cliChan is used to process client related events (new connections and lost connections)
cliChan := make(chan client.ClientEvent)
userEvents := make(chan client.UserEvent)
for _, lconf := range srv.config.Listen {
log.Infof("Starting IRC server on: %s (TLS: %v, Proxy: %v)", lconf.Address, lconf.TLS, lconf.Proxy)
go srv.listen(lconf, inChan, cliChan)
go srv.listen(lconf, userEvents)
}
go srv.mainLoop(inChan, cliChan)
go srv.mainLoop(userEvents)
}
func (srv *Server) listen(lconf *ListenConfig, inChan chan<- client.ClientMessage, cliChan chan<- client.ClientEvent) {
func (srv *Server) listen(lconf *ListenConfig, userEvents chan<- client.UserEvent) {
listener, err := net.Listen("tcp", lconf.Address)
if err != nil {
panic(err)
......@@ -175,21 +172,21 @@ func (srv *Server) listen(lconf *ListenConfig, inChan chan<- client.ClientMessag
}).Info("New client connection")
// Each new client is handled in its own goroutine to allow DNS resolution without blocking the listening goroutine (this one)
go srv.newClientConnection(conn, inChan, cliChan)
go srv.newClientConnection(conn, userEvents)
}
}
// newClientConnection handles new client connections; it needs to be a goroutine because it does DNS resolution.
// TODO: start a goroutine that will kill this connection if it doesn't transition to the "registered" state in X seconds.
func (srv *Server) newClientConnection(conn net.Conn, inChan chan<- client.ClientMessage, cliChan chan<- client.ClientEvent) {
func (srv *Server) newClientConnection(conn net.Conn, userEvents chan<- client.UserEvent) {
cli, err := client.New(conn)
if err != nil {
log.Errorf("Error creating client object: %s", err.Error())
conn.Close()
return
}
cliChan <- client.ClientEvent{C: cli, Type: client.CONNECTED}
go cli.Reader(inChan, cliChan)
userEvents <- client.UserEvent{User: cli, Connected: true}
go cli.Reader(userEvents)
go cli.Writer()
}
......@@ -197,37 +194,33 @@ func (srv *Server) newClientConnection(conn net.Conn, inChan chan<- client.Clien
// incoming commands from 'inChan'. It also handles new IRC (network) connections.
// NOTE:
// the server handles incoming messages sequentially.
func (srv *Server) mainLoop(inChan <-chan client.ClientMessage, cliChan <-chan client.ClientEvent) {
func (srv *Server) mainLoop(userEvents <-chan client.UserEvent) {
for {
select {
case climsg := <-inChan:
if !climsg.C.IsRegistered() || climsg.C.Nick == "" || climsg.C.Ident == "" {
if !isPreRegCommand(climsg.M.Command) {
log.Warnf("Client %q attempted a command before being registered", climsg.C)
resp := message.NewMessage("451", []string{}, "You have not registered")
srv.send(climsg.C, resp)
continue
case event := <-userEvents:
if event.Connected {
srv.addNewClient(event.User)
} else if event.Disconnected {
srv.removeClient(event.User)
} else {
if !event.User.IsRegistered() || event.User.Nick == "" || event.User.Ident == "" {
if !isPreRegCommand(event.Message.Command) {
log.Warnf("Client %q attempted a command before being registered", event.User)
resp := message.NewMessage("451", []string{}, "You have not registered")
srv.send(event.User, resp)
continue
}
}
}
if handler, ok := srv.handlers[climsg.M.Command]; ok {
if climsg.M.Command != "PING" {
log.Infof("Handling command '%s' with args: %q, %q", climsg.M.Command, climsg.M.Args, climsg.M.Trailing)
if handler, ok := srv.handlers[event.Message.Command]; ok {
if event.Message.Command != "PING" {
log.Infof("Handling command '%s' with args: %q, %q", event.Message.Command, event.Message.Args, event.Message.Trailing)
}
handler(event.User, event.Message)
} else {
log.Warnf("handler not found for command %s (%q)", event.Message.Command, event.Message.Args)
}
handler(climsg.C, climsg.M)
} else {
log.Warnf("handler not found for command %s (%q)", climsg.M.Command, climsg.M.Args)
}
case event := <-cliChan:
if event.Type == client.CONNECTED {
srv.addNewClient(event.C)
} else if event.Type == client.DISCONNECTED {
srv.removeClient(event.C)
} else {
log.Errorf("Unknown event type: %q", event)
}
case <-srv.ExitChan:
log.Info("Quitting server (ExitChan triggered)")
return
......@@ -236,7 +229,7 @@ func (srv *Server) mainLoop(inChan <-chan client.ClientMessage, cliChan <-chan c
}
// send send a Response to a client. It inserts the Source prefix when needed.
func (s *Server) send(cli *client.Client, msg *message.Message) {
func (s *Server) send(cli *client.User, msg *message.Message) {
t := []string{}
if msg.Source != "" {
t = append(t, ":"+msg.Source)
......@@ -265,14 +258,14 @@ func (s *Server) send(cli *client.Client, msg *message.Message) {
}
// addNewClient is called when a new client connects
func (srv *Server) addNewClient(cli *client.Client) {
func (srv *Server) addNewClient(cli *client.User) {
log.Debugf("Adding client: %v", cli.Id.String())
srv.clients[cli.Id] = cli
metrics.ClientsConnected.Inc()
}
// removeClient is called when a client is no longer connected
func (srv *Server) removeClient(cli *client.Client) {
func (srv *Server) removeClient(cli *client.User) {
log.Debugf("Removing client: %v", cli.Id.String())
for _, channel := range srv.channels {
......@@ -306,7 +299,7 @@ on them automatically upon joining the network, the server SHOULD send the clien
the client sent it the MOTD command, i.e. it must send either the successful Message of the Day
numerics or the ERR_NOMOTD numeric. */
func (srv *Server) registerClient(cli *client.Client) {
func (srv *Server) registerClient(cli *client.User) {
if cli.Ident == "" || cli.Nick == "" || cli.IsRegistered() || cli.State == client.CAP_NEGOTIATION {
return
}
......@@ -388,7 +381,7 @@ func (srv *Server) getChannel(name string) (*Channel, error) {
return nil, fmt.Errorf("Channel %s does not exists", name)
}
func (srv *Server) createChannel(name string, founder *client.Client) (*Channel, error) {
func (srv *Server) createChannel(name string, founder *client.User) (*Channel, error) {
ch, err := srv.getChannel(name)
if err == nil {
return nil, fmt.Errorf("Channel %q already exists", name)
......@@ -403,7 +396,7 @@ func (srv *Server) createChannel(name string, founder *client.Client) (*Channel,
return ch, nil
}
func (srv *Server) getClient(nickname string) (*client.Client, error) {
func (srv *Server) getClient(nickname string) (*client.User, error) {
lNickname := strings.ToLower(nickname)
id, ok := srv.nicknames[lNickname]
if !ok {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment