Started adding support for Modes.
Added support for the following commands: * TOPIC * AWAY * MODE (just usermode support for now)
This commit is contained in:
		
							parent
							
								
									db91c5e08b
								
							
						
					
					
						commit
						b354ba7187
					
				
							
								
								
									
										105
									
								
								channel.go
									
									
									
									
									
								
							
							
						
						
									
										105
									
								
								channel.go
									
									
									
									
									
								
							@ -10,11 +10,11 @@ import (
 | 
			
		||||
// Channel represents an IRC channel or room
 | 
			
		||||
type Channel struct {
 | 
			
		||||
	Name string
 | 
			
		||||
	Modes ChannelModeSet
 | 
			
		||||
	*ChannelModeSet
 | 
			
		||||
	Topic string
 | 
			
		||||
	Key   string
 | 
			
		||||
 | 
			
		||||
	members      map[string]ChannelModeSet
 | 
			
		||||
	members      map[string]*ChannelModeSet
 | 
			
		||||
	membersMutex sync.RWMutex
 | 
			
		||||
 | 
			
		||||
	Server *Server
 | 
			
		||||
@ -23,17 +23,17 @@ type Channel struct {
 | 
			
		||||
// NewChannel creates and returns a new Channel
 | 
			
		||||
func NewChannel(s *Server, creator *Client) *Channel {
 | 
			
		||||
	c := &Channel{}
 | 
			
		||||
	c.members = map[string]ChannelModeSet{}
 | 
			
		||||
	c.members = map[string]*ChannelModeSet{}
 | 
			
		||||
	c.Server = s
 | 
			
		||||
	c.Modes = NewChannelModeSet()
 | 
			
		||||
	c.ChannelModeSet = 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
 | 
			
		||||
 | 
			
		||||
	if c.HasMember(client) { // client is already in this channel
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if len(c.Key) != 0 { //if key is required, verify that client provided matching key
 | 
			
		||||
@ -46,13 +46,14 @@ func (c *Channel) Join(client *Client, key string) {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	operator := len(c.members) == 0
 | 
			
		||||
	creator := c.GetMemberCount() == 0
 | 
			
		||||
 | 
			
		||||
	c.AddMember(client)
 | 
			
		||||
	client.AddChannel(c)
 | 
			
		||||
 | 
			
		||||
	if operator { // Client is creating channel
 | 
			
		||||
	if creator { // Client is creating channel
 | 
			
		||||
		// Creator should be a channel operator - Maybe check if it is a "safe" channel
 | 
			
		||||
 | 
			
		||||
		c.AddMemberMode(client, ChannelModeOperator)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -85,6 +86,9 @@ func (c *Channel) Join(client *Client, key string) {
 | 
			
		||||
			mClient, _ := client.Server.GetClientByNick(member)
 | 
			
		||||
 | 
			
		||||
			if mClient != nil {
 | 
			
		||||
				if c.MemberHasMode(mClient, ChannelModeOperator) {
 | 
			
		||||
					memberStr += "@"
 | 
			
		||||
				}
 | 
			
		||||
				memberStr += mClient.Nickname + " "
 | 
			
		||||
 | 
			
		||||
				mClient.Encode(&m)
 | 
			
		||||
@ -104,8 +108,7 @@ func (c *Channel) Join(client *Client, key string) {
 | 
			
		||||
// 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
 | 
			
		||||
	if !c.HasMember(client) { // 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
 | 
			
		||||
@ -125,8 +128,7 @@ func (c *Channel) Part(client *Client, message string) {
 | 
			
		||||
// 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
 | 
			
		||||
	if !c.HasMember(client) { // 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
 | 
			
		||||
@ -201,6 +203,21 @@ func (c *Channel) RemoveMember(client *Client) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HasMember returns if a client is an existing member of this channel
 | 
			
		||||
func (c *Channel) HasMember(client *Client) bool {
 | 
			
		||||
	c.membersMutex.RLock()
 | 
			
		||||
	defer c.membersMutex.RUnlock()
 | 
			
		||||
	_, found := c.members[client.Nickname]
 | 
			
		||||
	return found
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetMemberCount returns how many users are currently on the channel
 | 
			
		||||
func (c *Channel) GetMemberCount() int {
 | 
			
		||||
	c.membersMutex.RLock()
 | 
			
		||||
	defer c.membersMutex.RUnlock()
 | 
			
		||||
	return len(c.members)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddMemberMode adds a mode for the member of the channel
 | 
			
		||||
func (c *Channel) AddMemberMode(client *Client, mode ChannelMode) {
 | 
			
		||||
	c.membersMutex.Lock()
 | 
			
		||||
@ -224,6 +241,23 @@ func (c *Channel) RemoveMemberMode(client *Client, mode ChannelMode) {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Channel) GetMemberModes(client *Client) *ChannelModeSet {
 | 
			
		||||
	c.membersMutex.RLock()
 | 
			
		||||
	defer c.membersMutex.RUnlock()
 | 
			
		||||
	return c.members[client.Nickname]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MemberHasMode returns whether the given client has the requested mode
 | 
			
		||||
func (c *Channel) MemberHasMode(client *Client, mode ChannelMode) bool {
 | 
			
		||||
	c.membersMutex.RLock()
 | 
			
		||||
	defer c.membersMutex.RUnlock()
 | 
			
		||||
	member, ok := c.members[client.Nickname]
 | 
			
		||||
	if !ok { //client is not a member in channel
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return member.HasMode(mode)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Channel) delete() {
 | 
			
		||||
	if len(c.members) != 0 {
 | 
			
		||||
		return
 | 
			
		||||
@ -234,7 +268,7 @@ func (c *Channel) delete() {
 | 
			
		||||
 | 
			
		||||
var channelStarters = map[uint8]interface{}{'&': nil, '#': nil, '+': nil, '!': nil}
 | 
			
		||||
 | 
			
		||||
// validName checks if it meets parameters found in rfc2812 1.3
 | 
			
		||||
// validName checks if it meets parameters found in RFC 2812 Section 1.3
 | 
			
		||||
func (c *Channel) validName() bool {
 | 
			
		||||
	_, ok := channelStarters[c.Name[0]]
 | 
			
		||||
	if !ok {
 | 
			
		||||
@ -253,3 +287,48 @@ func (c *Channel) validName() bool {
 | 
			
		||||
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TopicCommand handles querying or modifying the channels topic
 | 
			
		||||
func (c *Channel) TopicCommand(client *Client, topic string) {
 | 
			
		||||
 | 
			
		||||
	if !c.HasMember(client) { // Client isn't on 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
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	//Client is trying to get topic
 | 
			
		||||
	if len(topic) == 0 { //Get channels topic
 | 
			
		||||
		if len(c.Topic) == 0 { // No topic is not set
 | 
			
		||||
			m := irc.Message{Prefix: client.Server.Prefix, Command: irc.RPL_NOTOPIC, Params: []string{c.Name}, Trailing: "No topic is set"}
 | 
			
		||||
			client.Encode(&m)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Return Channel topic
 | 
			
		||||
		m := irc.Message{Prefix: client.Server.Prefix, Command: irc.RPL_TOPIC, Params: []string{c.Name}, Trailing: c.Topic}
 | 
			
		||||
		client.Encode(&m)
 | 
			
		||||
		return
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Client is trying to set topic
 | 
			
		||||
	// If client is the operator, he can set the topic always
 | 
			
		||||
	isOp := c.MemberHasMode(client, ChannelModeOperator)
 | 
			
		||||
	tMode := c.HasMode(ChannelModeTopic)
 | 
			
		||||
 | 
			
		||||
	if isOp || !tMode { // Has permissions - operator or channel does not have +t mode
 | 
			
		||||
		c.Topic = topic
 | 
			
		||||
		//Notify channel members of new topic
 | 
			
		||||
		m := irc.Message{Prefix: client.Prefix, Command: irc.TOPIC, Params: []string{c.Name}, Trailing: c.Topic}
 | 
			
		||||
		c.SendMessage(&m)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Don't have permissions to set topic
 | 
			
		||||
 | 
			
		||||
	m := irc.Message{Prefix: client.Server.Prefix, Command: irc.ERR_CHANOPRIVSNEEDED, Params: []string{c.Name}, Trailing: "You're not channel operator"}
 | 
			
		||||
	client.Encode(&m)
 | 
			
		||||
	return
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										11
									
								
								client.go
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								client.go
									
									
									
									
									
								
							@ -33,6 +33,8 @@ type Client struct {
 | 
			
		||||
 | 
			
		||||
	channels     map[string]*Channel
 | 
			
		||||
	channelMutex sync.Mutex
 | 
			
		||||
 | 
			
		||||
	*UserModeSet
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Server) newClient(ircConn *irc.Conn, conn net.Conn) *Client {
 | 
			
		||||
@ -40,6 +42,7 @@ func (s *Server) newClient(ircConn *irc.Conn, conn net.Conn) *Client {
 | 
			
		||||
	client.authorized = len(s.Config.Password) == 0
 | 
			
		||||
	client.idleTimer = time.AfterFunc(time.Minute, client.idle)
 | 
			
		||||
	client.channels = map[string]*Channel{}
 | 
			
		||||
	client.UserModeSet = NewUserModeSet()
 | 
			
		||||
	return client
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -53,13 +56,13 @@ func (c *Client) Close() error {
 | 
			
		||||
 | 
			
		||||
// Ping sends an IRC PING command to a client
 | 
			
		||||
func (c *Client) Ping() {
 | 
			
		||||
	m := irc.Message{Command: irc.PING, Params: []string{"JuddBot"}, Trailing: "JuddBot"}
 | 
			
		||||
	m := irc.Message{Command: irc.PING, Trailing: c.Server.Config.Name}
 | 
			
		||||
	c.Encode(&m)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Pong sends an IRC PONG command to the client
 | 
			
		||||
func (c *Client) Pong() {
 | 
			
		||||
	m := irc.Message{Command: irc.PONG, Params: []string{"JuddBot"}, Trailing: "JuddBot"}
 | 
			
		||||
	m := irc.Message{Command: irc.PONG, Trailing: c.Server.Config.Name}
 | 
			
		||||
	c.Encode(&m)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -101,7 +104,7 @@ func (c *Client) idle() {
 | 
			
		||||
func (c *Client) quit() {
 | 
			
		||||
	// Have client leave/part each channel
 | 
			
		||||
	for _, channel := range c.GetChannels() {
 | 
			
		||||
		channel.Quit(c, "")
 | 
			
		||||
		channel.Quit(c, "Disconnected")
 | 
			
		||||
	}
 | 
			
		||||
	c.Quit()
 | 
			
		||||
}
 | 
			
		||||
@ -120,7 +123,7 @@ func (c *Client) Quit() {
 | 
			
		||||
func (c *Client) Welcome() {
 | 
			
		||||
 | 
			
		||||
	// 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.Name, Host: c.Host}
 | 
			
		||||
 | 
			
		||||
	m := irc.Message{Prefix: c.Server.Prefix, Command: irc.RPL_WELCOME,
 | 
			
		||||
		Params: []string{c.Nickname, c.Server.Config.Welcome}}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										209
									
								
								commands.go
									
									
									
									
									
								
							
							
						
						
									
										209
									
								
								commands.go
									
									
									
									
									
								
							@ -21,26 +21,30 @@ func (f CommandHandlerFunc) ServeIRC(message *irc.Message, client *Client) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PingHandler is a CommandHandler to respond to IRC PING commands from a client
 | 
			
		||||
// Implemented according to RFC 1459 4.6.2 and RFC 2812 3.7.2
 | 
			
		||||
// Implemented according to RFC 1459 Section 4.6.2 and RFC 2812 Section 3.7.2
 | 
			
		||||
func PingHandler(message *irc.Message, client *Client) {
 | 
			
		||||
	client.Pong()
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PongHandler is a CommandHandler to respond to IRC PONG commands from a client
 | 
			
		||||
// Implemented according to RFC 1459 4.6.3 and RFC 2812 3.7.3
 | 
			
		||||
// Implemented according to RFC 1459 Section 4.6.3 and RFC 2812 Section 3.7.3
 | 
			
		||||
func PongHandler(message *irc.Message, client *Client) {
 | 
			
		||||
	//client.Ping()
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// QuitHandler is a CommandHandler to respond to IRC QUIT commands from a client
 | 
			
		||||
// Implemented according to RFC 1459 4.1.6 and RFC 2812 3.1.7
 | 
			
		||||
// Implemented according to RFC 1459 Section 4.1.6 and RFC 2812 Section 3.1.7
 | 
			
		||||
func QuitHandler(message *irc.Message, client *Client) {
 | 
			
		||||
 | 
			
		||||
	var leavingMessage string
 | 
			
		||||
	if len(message.Params) != 0 {
 | 
			
		||||
		leavingMessage = message.Params[0]
 | 
			
		||||
	} else if len(message.Trailing) != 0 {
 | 
			
		||||
		leavingMessage = message.Trailing
 | 
			
		||||
	} else {
 | 
			
		||||
		leavingMessage = client.Nickname
 | 
			
		||||
	}
 | 
			
		||||
	for _, channel := range client.GetChannels() {
 | 
			
		||||
		channel.Quit(client, leavingMessage)
 | 
			
		||||
@ -53,7 +57,7 @@ func QuitHandler(message *irc.Message, client *Client) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NickHandler is a CommandHandler to respond to IRC NICK commands from a client
 | 
			
		||||
// Implemented according to RFC 1459 4.1.2 and RFC 2812 3.1.2
 | 
			
		||||
// Implemented according to RFC 1459 Section 4.1.2 and RFC 2812 Section 3.1.2
 | 
			
		||||
func NickHandler(message *irc.Message, client *Client) {
 | 
			
		||||
 | 
			
		||||
	var m irc.Message
 | 
			
		||||
@ -97,7 +101,7 @@ func NickHandler(message *irc.Message, client *Client) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UserHandler is a CommandHandler to respond to IRC USER commands from a client
 | 
			
		||||
// Implemented according to RFC 1459 4.1.3 and RFC 2812 3.1.3
 | 
			
		||||
// Implemented according to RFC 1459 Section 4.1.3 and RFC 2812 Section 3.1.3
 | 
			
		||||
func UserHandler(message *irc.Message, client *Client) {
 | 
			
		||||
	var m irc.Message
 | 
			
		||||
	serverName := client.Server.Config.Name
 | 
			
		||||
@ -136,7 +140,7 @@ 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
 | 
			
		||||
// Implemented according to RFC 1459 Section 4.2.1 and RFC 2812 Section 3.2.1
 | 
			
		||||
func JoinHandler(message *irc.Message, client *Client) {
 | 
			
		||||
	channelNames := message.Params[0]
 | 
			
		||||
	if channelNames == "0" { // Leave all channels
 | 
			
		||||
@ -177,7 +181,7 @@ func JoinHandler(message *irc.Message, client *Client) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
// Implemented according to RFC 1459 Section 4.2.2 and RFC 2812 Section 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"}
 | 
			
		||||
@ -202,7 +206,7 @@ func PartHandler(message *irc.Message, client *Client) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
// Implemented according to RFC 1459 Section 4.4.1 and RFC 2812 Section 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)"}
 | 
			
		||||
@ -232,6 +236,12 @@ func PrivMsgHandler(message *irc.Message, client *Client) {
 | 
			
		||||
	if ok {
 | 
			
		||||
		m := irc.Message{Prefix: client.Prefix, Command: irc.PRIVMSG, Params: []string{cl.Nickname}, Trailing: message.Trailing}
 | 
			
		||||
		cl.Encode(&m)
 | 
			
		||||
 | 
			
		||||
		if cl.HasMode(UserModeAway) {
 | 
			
		||||
			m := irc.Message{Prefix: cl.Server.Prefix, Command: irc.RPL_AWAY, Params: []string{client.Nickname, cl.Nickname}, Trailing: cl.AwayMessage}
 | 
			
		||||
			client.Encode(&m)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -241,7 +251,7 @@ func PrivMsgHandler(message *irc.Message, client *Client) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
// Implemented according to RFC 1459 Section 4.4.2 and RFC 2812 Section 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)"}
 | 
			
		||||
@ -280,7 +290,7 @@ func NoticeHandler(message *irc.Message, client *Client) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
// Implemented according to RFC 1459 Section 4.5.1 and RFC 2812 Section 3.6.1
 | 
			
		||||
func WhoHandler(message *irc.Message, client *Client) {
 | 
			
		||||
	if len(message.Params) == 0 {
 | 
			
		||||
		//return listing of all users
 | 
			
		||||
@ -292,13 +302,188 @@ func WhoHandler(message *irc.Message, client *Client) {
 | 
			
		||||
			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)
 | 
			
		||||
				msg := whoLine(cl, ch, client.Nickname)
 | 
			
		||||
				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"}
 | 
			
		||||
 | 
			
		||||
	} else {
 | 
			
		||||
		// Not a channel, maybe a user
 | 
			
		||||
		cl, ok := client.Server.GetClientByNick(message.Params[0])
 | 
			
		||||
		if ok {
 | 
			
		||||
			msg := whoLine(cl, nil, client.Nickname)
 | 
			
		||||
			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, message.Params[0]}, Trailing: "End of WHO list"}
 | 
			
		||||
	client.Encode(&m)
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func whoLine(client *Client, channel *Channel, recipientClient string) string {
 | 
			
		||||
	channelName := "*"
 | 
			
		||||
 | 
			
		||||
	here := "H"
 | 
			
		||||
	if client.UserModeSet.HasMode(UserModeAway) {
 | 
			
		||||
		here = "G"
 | 
			
		||||
	}
 | 
			
		||||
	opStatus := ""
 | 
			
		||||
	if client.HasMode(UserModeOperator) || client.HasMode(UserModeLocalOperator) {
 | 
			
		||||
		opStatus += "*"
 | 
			
		||||
	}
 | 
			
		||||
	if channel != nil {
 | 
			
		||||
		channelName = channel.Name
 | 
			
		||||
		if channel.MemberHasMode(client, ChannelModeOperator) {
 | 
			
		||||
			opStatus += "@"
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	hopCount := 0 //For now only local clients allowed - no federation
 | 
			
		||||
 | 
			
		||||
	return fmt.Sprintf("%s %s %s %s %s %s %s%s :%d %s", recipientClient, channelName, client.Name, client.Host, client.Server.Config.Name, client.Nickname, here, opStatus, hopCount, client.RealName)
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TopicHandler is a CommandHandler to respond to IRC TOPIC commands from a client
 | 
			
		||||
// Implemented according to RFC 1459 Section 4.2.4 and RFC 2812 Section 3.2.4
 | 
			
		||||
func TopicHandler(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
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	channelName := message.Params[0]
 | 
			
		||||
	channel, ok := client.Server.GetChannel(channelName)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		m := irc.Message{Prefix: client.Server.Prefix, Command: irc.ERR_NOSUCHCHANNEL, Params: []string{channelName}, Trailing: "No such channel"}
 | 
			
		||||
		client.Encode(&m)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	m := ""
 | 
			
		||||
	if len(message.Params) > 1 {
 | 
			
		||||
		m = message.Params[1]
 | 
			
		||||
	} else {
 | 
			
		||||
		m = message.Trailing
 | 
			
		||||
	}
 | 
			
		||||
	channel.TopicCommand(client, m)
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AwayHandler is a CommandHandler to respond to IRC AWAY commands from a client
 | 
			
		||||
// Implemented according to RFC 1459 Section 5.1 and RFC 2812 Section 4.1
 | 
			
		||||
func AwayHandler(message *irc.Message, client *Client) {
 | 
			
		||||
	if len(message.Params) == 0 && len(message.Trailing) == 0 {
 | 
			
		||||
		client.AwayMessage = ""
 | 
			
		||||
		client.RemoveMode(UserModeAway)
 | 
			
		||||
		m := irc.Message{Prefix: client.Server.Prefix, Command: irc.RPL_UNAWAY, Trailing: "You are no longer marked as being away"}
 | 
			
		||||
		client.Encode(&m)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if len(message.Trailing) > 0 {
 | 
			
		||||
		client.AwayMessage = message.Trailing
 | 
			
		||||
	} else {
 | 
			
		||||
		client.AwayMessage = strings.Join(message.Params, " ")
 | 
			
		||||
	}
 | 
			
		||||
	client.AddMode(UserModeAway)
 | 
			
		||||
	m := irc.Message{Prefix: client.Server.Prefix, Command: irc.RPL_NOWAWAY, Trailing: "You have been marked as being away"}
 | 
			
		||||
 | 
			
		||||
	client.Encode(&m)
 | 
			
		||||
	return
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ModeHandler is a CommandHandler to respond to IRC MODE commands from a client
 | 
			
		||||
// Implemented according to RFC 1459 Section 4.2.3 and RFC 2812 Section 3.1.5 and RFC 2811
 | 
			
		||||
func ModeHandler(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
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	id := message.Params[0]
 | 
			
		||||
	_, ok := client.Server.GetChannel(id)
 | 
			
		||||
	if ok {
 | 
			
		||||
		ChannelModeHandler(message, client)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	UserModeHandler(message, client)
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UserModeHandler is a specialized CommandHandler to respond to global or user IRC MODE commands from a client
 | 
			
		||||
// Implemented according to RFC 1459 Section 4.2.3.2 and RFC 2812 Section 3.1.5
 | 
			
		||||
func UserModeHandler(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
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	username := message.Params[0]
 | 
			
		||||
	if username != client.Nickname {
 | 
			
		||||
		m := irc.Message{Prefix: client.Server.Prefix, Command: irc.ERR_USERSDONTMATCH, Trailing: "Cannot change mode for other users"}
 | 
			
		||||
		client.Encode(&m)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(message.Params) == 1 { // just nickname is provided
 | 
			
		||||
		// return current settings for this user
 | 
			
		||||
		m := irc.Message{Prefix: client.Server.Prefix, Command: irc.RPL_UMODEIS, Params: []string{client.UserModeSet.String()}}
 | 
			
		||||
		client.Encode(&m)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, modeFlags := range message.Params[1:] {
 | 
			
		||||
		modifier := ModeModifier(modeFlags[0])
 | 
			
		||||
		switch modifier {
 | 
			
		||||
		case ModeModifierAdd:
 | 
			
		||||
		case ModeModifierRemove:
 | 
			
		||||
		default:
 | 
			
		||||
			m := irc.Message{Prefix: client.Server.Prefix, Command: irc.ERR_UMODEUNKNOWNFLAG, Trailing: "Unknown MODE flag"}
 | 
			
		||||
			client.Encode(&m)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, modeFlag := range modeFlags[1:] {
 | 
			
		||||
			mode := UserMode(modeFlag)
 | 
			
		||||
			_, ok := UserModes[mode]
 | 
			
		||||
			if !ok || mode == UserModeAway { // Away flag should only be set with AWAY command
 | 
			
		||||
				m := irc.Message{Prefix: client.Server.Prefix, Command: irc.ERR_UMODEUNKNOWNFLAG, Trailing: "Unknown MODE flag"}
 | 
			
		||||
				client.Encode(&m)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			if modifier == ModeModifierAdd {
 | 
			
		||||
				switch mode {
 | 
			
		||||
				case UserModeOperator, UserModeLocalOperator: // Can't make oneself an operator
 | 
			
		||||
				default:
 | 
			
		||||
					client.AddMode(mode)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			} else if modifier == ModeModifierRemove {
 | 
			
		||||
				switch mode {
 | 
			
		||||
				case UserModeRestricted: // Can't remove oneself from being restricted
 | 
			
		||||
				default:
 | 
			
		||||
					client.RemoveMode(mode)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	m := irc.Message{Prefix: client.Server.Prefix, Command: irc.RPL_UMODEIS, Params: []string{client.Nickname, client.UserModeSet.String()}}
 | 
			
		||||
	client.Encode(&m)
 | 
			
		||||
	return
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ChannelModeHandler is a specialized CommandHandler to respond to channel IRC MODE commands from a client
 | 
			
		||||
// Implemented according to RFC 1459 Section 4.2.3.1 and RFC 2811
 | 
			
		||||
func ChannelModeHandler(message *irc.Message, client *Client) {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										93
									
								
								modes.go
									
									
									
									
									
								
							
							
						
						
									
										93
									
								
								modes.go
									
									
									
									
									
								
							@ -2,7 +2,7 @@ package irc
 | 
			
		||||
 | 
			
		||||
import "sync"
 | 
			
		||||
 | 
			
		||||
// UserMode  - RFC 1459 4.2.3.2 and RFC 2812 3.1.5
 | 
			
		||||
// UserMode  - RFC 1459 Section 4.2.3.2 and RFC 2812 Section 3.1.5
 | 
			
		||||
type UserMode rune
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
@ -15,6 +15,7 @@ const (
 | 
			
		||||
	UserModeServerNotice  UserMode = 's' //obsolete
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// UserModes contains the supported User UserMode types
 | 
			
		||||
var UserModes = map[UserMode]interface{}{
 | 
			
		||||
	UserModeAway:          nil,
 | 
			
		||||
	UserModeInvisible:     nil,
 | 
			
		||||
@ -25,17 +26,20 @@ var UserModes = map[UserMode]interface{}{
 | 
			
		||||
	UserModeServerNotice:  nil,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UserModeSet provides means for storing and checking UserModes
 | 
			
		||||
type UserModeSet struct {
 | 
			
		||||
	userModes map[UserMode]interface{}
 | 
			
		||||
	mutex     sync.Mutex
 | 
			
		||||
	mutex     sync.RWMutex
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewUserModeSet() UserModeSet {
 | 
			
		||||
// NewUserModeSet creates and returns a new UserModeSet
 | 
			
		||||
func NewUserModeSet() *UserModeSet {
 | 
			
		||||
	u := UserModeSet{}
 | 
			
		||||
	u.userModes = map[UserMode]interface{}{}
 | 
			
		||||
	return u
 | 
			
		||||
	return &u
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddMode adds a mode to the UserModeSet
 | 
			
		||||
func (u *UserModeSet) AddMode(mode UserMode) {
 | 
			
		||||
	u.mutex.Lock()
 | 
			
		||||
	defer u.mutex.Unlock()
 | 
			
		||||
@ -43,6 +47,7 @@ func (u *UserModeSet) AddMode(mode UserMode) {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RemoveMode removes a mode from the UserModeSet
 | 
			
		||||
func (u *UserModeSet) RemoveMode(mode UserMode) {
 | 
			
		||||
	u.mutex.Lock()
 | 
			
		||||
	defer u.mutex.Unlock()
 | 
			
		||||
@ -50,16 +55,28 @@ func (u *UserModeSet) RemoveMode(mode UserMode) {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HasMode detects if a UserMode is contained in the UserModeSet
 | 
			
		||||
func (u *UserModeSet) HasMode(mode UserMode) bool {
 | 
			
		||||
	u.mutex.RLock()
 | 
			
		||||
	defer u.mutex.RUnlock()
 | 
			
		||||
	_, found := u.userModes[mode]
 | 
			
		||||
	return found
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// String formats the UserModeString to be returned for MODE queries
 | 
			
		||||
func (u *UserModeSet) String() string {
 | 
			
		||||
	s := ""
 | 
			
		||||
	for m, _ := range u.userModes {
 | 
			
		||||
	if len(u.userModes) > 0 {
 | 
			
		||||
		s = "+"
 | 
			
		||||
	}
 | 
			
		||||
	for m := range u.userModes {
 | 
			
		||||
		s += string(m)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ModeModifier - RFC 1459 4.2.3 and RFC 2812 3.1.5
 | 
			
		||||
// ModeModifier - RFC 1459 Section 4.2.3 and RFC 2812 Section 3.1.5
 | 
			
		||||
type ModeModifier rune
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
@ -67,7 +84,7 @@ const (
 | 
			
		||||
	ModeModifierRemove ModeModifier = '-'
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ChannelMode -  RFC 1459 4.2.3.1 and RFC 2812 3.2.3 and RFC 2811 4
 | 
			
		||||
// ChannelMode -  RFC 1459 Section 4.2.3.1 and RFC 2812 Section 3.2.3 and RFC 2811 Section 4
 | 
			
		||||
type ChannelMode rune
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
@ -93,27 +110,75 @@ const (
 | 
			
		||||
	ChannelModeInvitationMask ChannelMode = 'I'
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ChannelModes contains the supported channel modes
 | 
			
		||||
// Currently supported modes are just those in RFC 1459
 | 
			
		||||
var ChannelModes = map[ChannelMode]interface{}{
 | 
			
		||||
	//ChannelModeCreator:           nil,
 | 
			
		||||
	ChannelModeOperator: nil,
 | 
			
		||||
	ChannelModeVoice:    nil,
 | 
			
		||||
	//ChannelModeAnonymous:         nil,
 | 
			
		||||
	ChannelModeInviteOnly:        nil,
 | 
			
		||||
	ChannelModeModerated:         nil,
 | 
			
		||||
	ChannelModeNoOutsideMessages: nil,
 | 
			
		||||
	//ChannelModeQuiet:             nil,
 | 
			
		||||
	ChannelModePrivate: nil,
 | 
			
		||||
	ChannelModeSecret:  nil,
 | 
			
		||||
	//ChannelModeReOp:              nil,
 | 
			
		||||
	ChannelModeTopic: nil,
 | 
			
		||||
	ChannelModeKey:   nil,
 | 
			
		||||
	ChannelModeLimit: nil,
 | 
			
		||||
	ChannelModeBan:   nil,
 | 
			
		||||
	//ChannelModeExceptionMask:     nil,
 | 
			
		||||
	//ChannelModeInvitationMask:    nil,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ChannelModeSet represents a set of active ChannelModes
 | 
			
		||||
type ChannelModeSet struct {
 | 
			
		||||
	Modes map[ChannelMode]interface{}
 | 
			
		||||
	mutex sync.Mutex
 | 
			
		||||
	modes map[ChannelMode]interface{}
 | 
			
		||||
	mutex sync.RWMutex
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewChannelModeSet() ChannelModeSet {
 | 
			
		||||
// NewChannelModeSet creates and returns a new ChannelModeSet
 | 
			
		||||
func NewChannelModeSet() *ChannelModeSet {
 | 
			
		||||
	c := ChannelModeSet{}
 | 
			
		||||
	c.Modes = map[ChannelMode]interface{}{}
 | 
			
		||||
	return c
 | 
			
		||||
	c.modes = map[ChannelMode]interface{}{}
 | 
			
		||||
	return &c
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddMode adds a ChannelMode as active
 | 
			
		||||
func (c *ChannelModeSet) AddMode(mode ChannelMode) {
 | 
			
		||||
	c.mutex.Lock()
 | 
			
		||||
	defer c.mutex.Unlock()
 | 
			
		||||
	c.Modes[mode] = nil
 | 
			
		||||
	c.modes[mode] = nil
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RemoveMode removes the given mode from the active set
 | 
			
		||||
func (c *ChannelModeSet) RemoveMode(mode ChannelMode) {
 | 
			
		||||
	c.mutex.Lock()
 | 
			
		||||
	defer c.mutex.Unlock()
 | 
			
		||||
	delete(c.Modes, mode)
 | 
			
		||||
	delete(c.modes, mode)
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HasMode determines if the ChannelModeSet contains the given ChannelMode
 | 
			
		||||
func (c *ChannelModeSet) HasMode(mode ChannelMode) bool {
 | 
			
		||||
	c.mutex.RLock()
 | 
			
		||||
	defer c.mutex.RUnlock()
 | 
			
		||||
	_, found := c.modes[mode]
 | 
			
		||||
	return found
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// String returns the ChannelModeSet formatted for the MODE queries
 | 
			
		||||
func (c *ChannelModeSet) String() string {
 | 
			
		||||
	s := ""
 | 
			
		||||
	if len(c.modes) > 0 {
 | 
			
		||||
		s = "+"
 | 
			
		||||
	}
 | 
			
		||||
	for m := range c.modes {
 | 
			
		||||
		s += string(m)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user