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
107
channel.go
107
channel.go
@ -9,12 +9,12 @@ import (
|
||||
|
||||
// Channel represents an IRC channel or room
|
||||
type Channel struct {
|
||||
Name string
|
||||
Modes ChannelModeSet
|
||||
Name string
|
||||
*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}}
|
||||
|
211
commands.go
211
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"}
|
||||
client.Encode(&m)
|
||||
|
||||
} 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…
Reference in New Issue
Block a user