Added proper parsing of channel modes.

Implemented parsing and storage of Channel Mode flags. Not all flags are currently being utilized.
This commit is contained in:
Justin 2015-08-07 17:37:41 -07:00
parent a6e09ed19f
commit 2ef0464d1d
6 changed files with 338 additions and 92 deletions

View File

@ -36,9 +36,9 @@ func (c *Channel) Join(client *Client, key string) {
if c.HasMember(client) { // client is already in this channel if c.HasMember(client) { // client is already in this channel
return return
} }
if len(c.Key) != 0 { //if key is required, verify that client provided matching key if c.HasMode(ChannelModeKey) { //if key is required, verify that client provided matching key
if c.Key != key { if c.Key != key {
m := irc.Message{Command: irc.ERR_BADCHANNELKEY} m := irc.Message{Prefix: c.Server.Prefix, Command: irc.ERR_BADCHANNELKEY, Params: []string{client.Nickname, c.Name}, Trailing: "Cannot join channel (+k)"}
err := client.Encode(&m) err := client.Encode(&m)
if err != nil { if err != nil {
println(err.Error()) println(err.Error())
@ -46,6 +46,11 @@ func (c *Channel) Join(client *Client, key string) {
return return
} }
} }
if c.HasMode(ChannelModeLimit) && c.GetMemberCount() >= c.GetLimit() { // Limit flag is set and limit is met
m := irc.Message{Prefix: c.Server.Prefix, Command: irc.ERR_CHANNELISFULL, Params: []string{client.Nickname, c.Name}, Trailing: "Cannot join channel (+l)"}
client.Encode(&m)
}
creator := c.GetMemberCount() == 0 creator := c.GetMemberCount() == 0
c.AddMember(client) c.AddMember(client)
@ -55,6 +60,10 @@ func (c *Channel) Join(client *Client, key string) {
// Creator should be a channel operator - Maybe check if it is a "safe" channel // Creator should be a channel operator - Maybe check if it is a "safe" channel
c.AddMemberMode(client, ChannelModeOperator) c.AddMemberMode(client, ChannelModeOperator)
if len(key) != 0 {
c.SetKey(key)
}
} }
var m irc.Message var m irc.Message
@ -66,6 +75,37 @@ func (c *Channel) Join(client *Client, key string) {
} }
//Notify existing members that new member is joining //Notify existing members that new member is joining
m = irc.Message{Prefix: client.Prefix, Command: irc.JOIN, Params: []string{c.Name}}
c.SendMessage(&m)
c.Names(client)
}
// SetKey sets the channel key
func (c *Channel) SetKey(key string) {
c.AddModeWithValue(ChannelModeKey, key)
}
// GetKey gets the key for the channel
func (c *Channel) GetKey() string {
val := c.GetMode(ChannelModeKey)
if val == nil {
return ""
}
return val.(string)
}
// Names responds to to IRC NAMES command for the channel
func (c *Channel) Names(client *Client) {
if c.HasMode(ChannelModeSecret) && !c.HasMember(client) { // If channel is secret and client isn't a member, don't reveal it
return
}
if c.HasMode(ChannelModePrivate) && !c.HasMember(client) { // If channel is private and client isn't a member, don't reply
return
}
allMembers := make([]string, len(c.members)) allMembers := make([]string, len(c.members))
i := 0 i := 0
@ -73,36 +113,46 @@ func (c *Channel) Join(client *Client, key string) {
allMembers[i] = member allMembers[i] = member
i++ i++
} }
// send list of users in channel // send list of users in channel
//channelPrefix := ""
// RFC 2812 defines other channel prefixes
channelPrefix := "="
if c.HasMode(ChannelModeSecret) {
channelPrefix = "@"
}
if c.HasMode(ChannelModePrivate) {
channelPrefix = "*"
}
for i := 0; i < (len(c.members)/20)+1; i++ { for i := 0; i < (len(c.members)/20)+1; i++ {
memberStr := "= " + c.Name + " :" memberStr := ""
end := (i + 1) * 20 end := (i + 1) * 20
if end > len(c.members) { if end > len(c.members) {
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] { for _, member := range allMembers[i*20 : end] {
mClient, _ := client.Server.GetClientByNick(member) mClient, _ := client.Server.GetClientByNick(member)
if mClient != nil { if mClient != nil {
if c.MemberHasMode(mClient, ChannelModeOperator) { if c.MemberHasMode(mClient, ChannelModeOperator) {
memberStr += "@" memberStr += "@"
} else if c.MemberHasMode(mClient, ChannelModeVoice) {
memberStr += "+"
} }
memberStr += mClient.Nickname + " " memberStr += mClient.Nickname + " "
mClient.Encode(&m)
} }
} }
m = irc.Message{Prefix: c.Server.Prefix, Command: irc.RPL_NAMREPLY, Params: []string{client.Nickname, memberStr}} m := irc.Message{Prefix: c.Server.Prefix, Command: irc.RPL_NAMREPLY, Params: []string{client.Nickname, channelPrefix, c.Name}, Trailing: memberStr}
client.Encode(&m) 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"} 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) client.Encode(&m)
} }
// Part handles when a client leaves a channel // Part handles when a client leaves a channel
@ -134,7 +184,7 @@ func (c *Channel) Quit(client *Client, message string) {
return return
} }
m := irc.Message{Prefix: client.Prefix, Command: irc.QUIT} m := irc.Message{Prefix: client.Prefix, Command: irc.QUIT, Params: []string{c.Name}}
if len(message) != 0 { if len(message) != 0 {
m.Trailing = message m.Trailing = message
} }
@ -241,6 +291,7 @@ func (c *Channel) RemoveMemberMode(client *Client, mode ChannelMode) {
} }
// GetMemberModes returns the Channel Modes active for a member
func (c *Channel) GetMemberModes(client *Client) *ChannelModeSet { func (c *Channel) GetMemberModes(client *Client) *ChannelModeSet {
c.membersMutex.RLock() c.membersMutex.RLock()
defer c.membersMutex.RUnlock() defer c.membersMutex.RUnlock()
@ -332,3 +383,69 @@ func (c *Channel) TopicCommand(client *Client, topic string) {
return return
} }
// AddBanMask sets the channel ban mask
func (c *Channel) AddBanMask(mask string) {
masks := c.GetBanMasks()
masks[mask] = nil
c.AddModeWithValue(ChannelModeBan, masks)
}
// GetBanMasks gets the ban masks for the channel
func (c *Channel) GetBanMasks() map[string]interface{} {
val := c.GetMode(ChannelModeBan)
if val == nil {
return make(map[string]interface{}, 0)
}
return val.(map[string]interface{})
}
// AddExceptionMask sets the channel exception mask
func (c *Channel) AddExceptionMask(mask string) {
masks := c.GetExceptionMasks()
masks[mask] = nil
c.AddModeWithValue(ChannelModeExceptionMask, masks)
}
// GetExceptionMasks gets the exception masks for the channel
func (c *Channel) GetExceptionMasks() map[string]interface{} {
val := c.GetMode(ChannelModeExceptionMask)
if val == nil {
return make(map[string]interface{}, 0)
}
return val.(map[string]interface{})
}
// AddInvitationMask sets the channel invitation mask
func (c *Channel) AddInvitationMask(mask string) {
masks := c.GetInvitationMasks()
masks[mask] = nil
c.AddModeWithValue(ChannelModeInvitationMask, masks)
}
// GetInvitationMasks gets the invitation masks for the channel
func (c *Channel) GetInvitationMasks() map[string]interface{} {
val := c.GetMode(ChannelModeInvitationMask)
if val == nil {
return make(map[string]interface{}, 0)
}
return val.(map[string]interface{})
}
// SetLimit sets the channel member limit
func (c *Channel) SetLimit(limit int) {
c.AddModeWithValue(ChannelModeLimit, limit)
}
// GetLimit gets the member limit for the channel
func (c *Channel) GetLimit() int {
val := c.GetMode(ChannelModeLimit)
if val == nil {
return 0
}
return val.(int)
}

View File

@ -126,7 +126,7 @@ func (c *Client) Welcome() {
c.Prefix = &irc.Prefix{Name: c.Nickname, User: c.Name, 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, m := irc.Message{Prefix: c.Server.Prefix, Command: irc.RPL_WELCOME,
Params: []string{c.Nickname, c.Server.Config.Welcome}} Params: []string{c.Nickname, "Welcome to the Internet Relay Network", c.Prefix.String()}}
err := c.Encode(&m) err := c.Encode(&m)
if err != nil { if err != nil {
@ -134,7 +134,7 @@ func (c *Client) Welcome() {
} }
m = irc.Message{Prefix: c.Server.Prefix, Command: irc.RPL_YOURHOST, 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)}} Params: []string{c.Nickname, fmt.Sprintf("Your host is %s, running version %s", c.Server.Config.Name, c.Server.Config.Version)}}
err = c.Encode(&m) err = c.Encode(&m)
if err != nil { if err != nil {
@ -157,10 +157,23 @@ func (c *Client) Welcome() {
return return
} }
m = irc.Message{Prefix: c.Server.Prefix, Command: irc.RPL_MOTDSTART, // Send MOTD
c.MOTD()
}
// MOTD returns the Message of the Day of the server to the client
func (c *Client) MOTD() {
if len(c.Server.Config.MOTD) == 0 {
m := irc.Message{Prefix: c.Server.Prefix, Command: irc.ERR_NOMOTD, Params: []string{c.Nickname}, Trailing: "MOTD File is missing"}
c.Encode(&m)
}
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)}} Params: []string{c.Nickname, fmt.Sprintf("%s - Message of the day", c.Server.Config.Name)}}
err = c.Encode(&m) err := c.Encode(&m)
if err != nil { if err != nil {
return return
} }

View File

@ -2,6 +2,7 @@ package irc
import ( import (
"fmt" "fmt"
"strconv"
"strings" "strings"
"github.com/sorcix/irc" "github.com/sorcix/irc"
@ -351,7 +352,7 @@ func whoLine(client *Client, channel *Channel, recipientClient string) string {
// Implemented according to RFC 1459 Section 4.2.4 and RFC 2812 Section 3.2.4 // Implemented according to RFC 1459 Section 4.2.4 and RFC 2812 Section 3.2.4
func TopicHandler(message *irc.Message, client *Client) { func TopicHandler(message *irc.Message, client *Client) {
if len(message.Params) == 0 { if len(message.Params) == 0 {
m := irc.Message{Prefix: client.Server.Prefix, Command: irc.ERR_NEEDMOREPARAMS, Trailing: "Not enough parameters"} m := irc.Message{Prefix: client.Server.Prefix, Command: irc.ERR_NEEDMOREPARAMS, Params: []string{client.Nickname}, Trailing: "Not enough parameters"}
client.Encode(&m) client.Encode(&m)
return return
} }
@ -359,7 +360,7 @@ func TopicHandler(message *irc.Message, client *Client) {
channelName := message.Params[0] channelName := message.Params[0]
channel, ok := client.Server.GetChannel(channelName) channel, ok := client.Server.GetChannel(channelName)
if !ok { if !ok {
m := irc.Message{Prefix: client.Server.Prefix, Command: irc.ERR_NOSUCHCHANNEL, Params: []string{channelName}, Trailing: "No such channel"} m := irc.Message{Prefix: client.Server.Prefix, Command: irc.ERR_NOSUCHCHANNEL, Params: []string{client.Nickname, channelName}, Trailing: "No such channel"}
client.Encode(&m) client.Encode(&m)
return return
} }
@ -380,7 +381,7 @@ func AwayHandler(message *irc.Message, client *Client) {
if len(message.Params) == 0 && len(message.Trailing) == 0 { if len(message.Params) == 0 && len(message.Trailing) == 0 {
client.AwayMessage = "" client.AwayMessage = ""
client.RemoveMode(UserModeAway) client.RemoveMode(UserModeAway)
m := irc.Message{Prefix: client.Server.Prefix, Command: irc.RPL_UNAWAY, Trailing: "You are no longer marked as being away"} m := irc.Message{Prefix: client.Server.Prefix, Command: irc.RPL_UNAWAY, Params: []string{client.Nickname}, Trailing: "You are no longer marked as being away"}
client.Encode(&m) client.Encode(&m)
return return
} }
@ -390,7 +391,7 @@ func AwayHandler(message *irc.Message, client *Client) {
client.AwayMessage = strings.Join(message.Params, " ") client.AwayMessage = strings.Join(message.Params, " ")
} }
client.AddMode(UserModeAway) client.AddMode(UserModeAway)
m := irc.Message{Prefix: client.Server.Prefix, Command: irc.RPL_NOWAWAY, Trailing: "You have been marked as being away"} m := irc.Message{Prefix: client.Server.Prefix, Command: irc.RPL_NOWAWAY, Params: []string{client.Nickname}, Trailing: "You have been marked as being away"}
client.Encode(&m) client.Encode(&m)
return return
@ -401,7 +402,7 @@ func AwayHandler(message *irc.Message, client *Client) {
// Implemented according to RFC 1459 Section 4.2.3 and RFC 2812 Section 3.1.5 and RFC 2811 // 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) { func ModeHandler(message *irc.Message, client *Client) {
if len(message.Params) == 0 { if len(message.Params) == 0 {
m := irc.Message{Prefix: client.Server.Prefix, Command: irc.ERR_NEEDMOREPARAMS, Trailing: "Not enough parameters"} m := irc.Message{Prefix: client.Server.Prefix, Command: irc.ERR_NEEDMOREPARAMS, Params: []string{client.Nickname}, Trailing: "Not enough parameters"}
client.Encode(&m) client.Encode(&m)
return return
} }
@ -420,21 +421,21 @@ func ModeHandler(message *irc.Message, client *Client) {
// Implemented according to RFC 1459 Section 4.2.3.2 and RFC 2812 Section 3.1.5 // Implemented according to RFC 1459 Section 4.2.3.2 and RFC 2812 Section 3.1.5
func UserModeHandler(message *irc.Message, client *Client) { func UserModeHandler(message *irc.Message, client *Client) {
if len(message.Params) == 0 { if len(message.Params) == 0 {
m := irc.Message{Prefix: client.Server.Prefix, Command: irc.ERR_NEEDMOREPARAMS, Trailing: "Not enough parameters"} m := irc.Message{Prefix: client.Server.Prefix, Command: irc.ERR_NEEDMOREPARAMS, Params: []string{client.Nickname}, Trailing: "Not enough parameters"}
client.Encode(&m) client.Encode(&m)
return return
} }
username := message.Params[0] username := message.Params[0]
if username != client.Nickname { if username != client.Nickname {
m := irc.Message{Prefix: client.Server.Prefix, Command: irc.ERR_USERSDONTMATCH, Trailing: "Cannot change mode for other users"} m := irc.Message{Prefix: client.Server.Prefix, Command: irc.ERR_USERSDONTMATCH, Params: []string{client.Nickname}, Trailing: "Cannot change mode for other users"}
client.Encode(&m) client.Encode(&m)
return return
} }
if len(message.Params) == 1 { // just nickname is provided if len(message.Params) == 1 { // just nickname is provided
// return current settings for this user // return current settings for this user
m := irc.Message{Prefix: client.Server.Prefix, Command: irc.RPL_UMODEIS, Params: []string{client.UserModeSet.String()}} m := irc.Message{Prefix: client.Server.Prefix, Command: irc.RPL_UMODEIS, Params: []string{client.Nickname, client.UserModeSet.String()}}
client.Encode(&m) client.Encode(&m)
return return
} }
@ -445,7 +446,7 @@ func UserModeHandler(message *irc.Message, client *Client) {
case ModeModifierAdd: case ModeModifierAdd:
case ModeModifierRemove: case ModeModifierRemove:
default: default:
m := irc.Message{Prefix: client.Server.Prefix, Command: irc.ERR_UMODEUNKNOWNFLAG, Trailing: "Unknown MODE flag"} m := irc.Message{Prefix: client.Server.Prefix, Command: irc.ERR_UMODEUNKNOWNFLAG, Params: []string{client.Nickname}, Trailing: "Unknown MODE flag"}
client.Encode(&m) client.Encode(&m)
return return
} }
@ -454,7 +455,7 @@ func UserModeHandler(message *irc.Message, client *Client) {
mode := UserMode(modeFlag) mode := UserMode(modeFlag)
_, ok := UserModes[mode] _, ok := UserModes[mode]
if !ok || mode == UserModeAway { // Away flag should only be set with AWAY command 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"} m := irc.Message{Prefix: client.Server.Prefix, Command: irc.ERR_UMODEUNKNOWNFLAG, Params: []string{client.Nickname}, Trailing: "Unknown MODE flag"}
client.Encode(&m) client.Encode(&m)
return return
} }
@ -501,90 +502,173 @@ func ChannelModeHandler(message *irc.Message, client *Client) {
} }
if len(message.Params) == 1 { // just channel name is provided if len(message.Params) == 1 { // just channel name is provided
// return current settings for this user // return current settings for this channel
m := irc.Message{Prefix: client.Server.Prefix, Command: irc.RPL_UMODEIS, Params: []string{channel.GetMemberModes(client).String()}} m := irc.Message{Prefix: client.Server.Prefix, Command: irc.RPL_CHANNELMODEIS, Params: []string{client.Nickname, channel.Name, channel.ChannelModeSet.String()}}
client.Encode(&m) client.Encode(&m)
return return
} }
if !channel.MemberHasMode(client, ChannelModeOperator) { // Only channel operators can make these changes if !channel.MemberHasMode(client, ChannelModeOperator) { // Only channel operators can make these changes
m := irc.Message{Prefix: client.Server.Prefix, Command: irc.ERR_CHANOPRIVSNEEDED, Params: []string{channel.Name}, Trailing: "You're not channel operator"} m := irc.Message{Prefix: client.Server.Prefix, Command: irc.ERR_CHANOPRIVSNEEDED, Params: []string{client.Nickname, channel.Name}, Trailing: "You're not channel operator"}
client.Encode(&m) client.Encode(&m)
return return
} }
setLimit := false type fullFlag struct {
getUsers := false ModeModifier
needMask := false ChannelMode
currentPlace := 1 Param string
for _, modeFlags := range message.Params[1:] {
currentPlace++
modifier := ModeModifier(modeFlags[0])
switch modifier {
case ModeModifierAdd:
case ModeModifierRemove:
default:
//modifier := ModeModifierQuery
/*
m := irc.Message{Prefix: client.Server.Prefix, Command: irc.ERR_UMODEUNKNOWNFLAG, Trailing: "Unknown MODE flag"}
client.Encode(&m)
return
*/
} }
for _, modeFlag := range modeFlags[1:] { needsArgs := []fullFlag{}
mode := ChannelMode(modeFlag) changes := []fullFlag{}
_, ok := ChannelModes[mode] argsCount := 0
if !ok { for _, param := range message.Params[1:] {
m := irc.Message{Prefix: client.Server.Prefix, Command: irc.ERR_UMODEUNKNOWNFLAG, Trailing: "Unknown MODE flag"} if len(needsArgs) != 0 { // This param will be an arg
client.Encode(&m) mode := needsArgs[0]
return needsArgs = needsArgs[1:]
argsCount++
if argsCount > 3 { //Only allow 3 argument based flags per mode command
break
} }
mode.Param = param
if modifier == ModeModifierAdd { switch mode.ChannelMode {
switch mode {
case ChannelModeCreator: // Can't make oneself a creator
case ChannelModeLimit:
setLimit = true
case ChannelModeOperator, ChannelModeVoice: case ChannelModeOperator, ChannelModeVoice:
getUsers = true n, ok := client.Server.GetClientByNick(param)
if ok {
found := channel.MemberHasMode(client, mode.ChannelMode)
if mode.ModeModifier == ModeModifierAdd {
channel.AddMemberMode(n, mode.ChannelMode)
if !found {
changes = append(changes, mode)
}
} else {
channel.RemoveMemberMode(n, mode.ChannelMode)
if found {
changes = append(changes, mode)
}
}
default: }
channel.AddMemberMode(client, mode) case ChannelModeLimit:
l, err := strconv.Atoi(param)
if err == nil {
channel.SetLimit(l)
changes = append(changes, mode)
}
case ChannelModeKey:
channel.SetKey(param)
changes = append(changes, mode)
case ChannelModeBan, ChannelModeExceptionMask, ChannelModeInvitationMask:
fillMask := func(mask string) string {
p := irc.ParsePrefix(param)
if len(p.Name) == 0 {
p.Name = "*"
}
if len(p.User) == 0 {
p.User = "*"
}
if len(p.Host) == 0 {
p.Host = "*"
}
return p.String()
}
mask := fillMask(param)
switch mode.ChannelMode {
case ChannelModeBan:
masks := channel.GetBanMasks()
_, ok := masks[mask]
if !ok {
channel.AddBanMask(mask)
changes = append(changes, mode)
}
case ChannelModeExceptionMask:
masks := channel.GetExceptionMasks()
_, ok := masks[mask]
if !ok {
channel.AddExceptionMask(mask)
changes = append(changes, mode)
}
case ChannelModeInvitationMask:
masks := channel.GetInvitationMasks()
_, ok := masks[mask]
if !ok {
channel.AddInvitationMask(mask)
changes = append(changes, mode)
}
} }
} else if modifier == ModeModifierRemove { }
switch mode { } else { // This should be a mode flag or series of mode flags
case ChannelModeCreator: // Can't remove oneself as creator modifier := ModeModifierAdd
//case ChannelModeBan: // Can't remove oneself from being restricted for _, char := range param {
default: mod := ModeModifier(char)
channel.RemoveMemberMode(client, mode) switch mod { // Set if flag is adding a removing a mode
case ModeModifierAdd:
modifier = ModeModifierAdd
case ModeModifierRemove:
modifier = ModeModifierRemove
} }
flag := ChannelMode(char)
switch flag {
case ChannelModeVoice, ChannelModeOperator, ChannelModeExceptionMask, ChannelModeInvitationMask, ChannelModeBan, ChannelModeLimit:
needsArgs = append(needsArgs, fullFlag{modifier, flag, ""})
case ChannelModeKey:
if modifier == ModeModifierAdd {
needsArgs = append(needsArgs, fullFlag{modifier, flag, ""})
}
}
}
} }
} }
if setLimit { // Get the number for setting the member limit cap changeString := ""
/* paramsChanged := []string{}
limit, err := strconv.Atoi(message.Params[currentPlace]) previousMode := ModeModifier(' ')
if err != nil { for _, change := range changes {
if len(change.Param) != 0 {
paramsChanged = append(paramsChanged, change.Param)
} }
*/ if change.ModeModifier != previousMode {
} changeString += string(change.ModeModifier)
if getUsers { // get users for either creating operators or adding voice
}
if needMask { // Set the ban mask
} }
changeString += string(change.ChannelMode)
previousMode = change.ModeModifier
} }
m := irc.Message{Prefix: client.Server.Prefix, Command: irc.RPL_UMODEIS, Params: []string{client.Nickname, client.UserModeSet.String()}} params := []string{channel.Name, changeString}
client.Encode(&m) params = append(params, paramsChanged...)
m := irc.Message{Prefix: client.Prefix, Command: irc.MODE, Params: params}
// Notify channel members of channel changes
channel.SendMessage(&m)
return return
} }
// NamesHandler is a specialized CommandHandler to respond to channel IRC NAMES commands from a client
// Implemented according to RFC 1459 Section 4.2.5 and RFC 2812 Section 3.2.5
func NamesHandler(message *irc.Message, client *Client) {
if len(message.Params) == 0 { // Send NAMES response for all channels
for _, ch := range client.Server.channels {
ch.Names(client)
}
} else {
channelNames := strings.Split(message.Params[0], ",")
for _, channelName := range channelNames {
ch, ok := client.Server.GetChannel(channelName)
if ok {
ch.Names(client)
}
}
}
}

View File

@ -1,6 +1,9 @@
package irc package irc
import "sync" import (
"fmt"
"sync"
)
// UserMode - RFC 1459 Section 4.2.3.2 and RFC 2812 Section 3.1.5 // UserMode - RFC 1459 Section 4.2.3.2 and RFC 2812 Section 3.1.5
type UserMode rune type UserMode rune
@ -153,6 +156,13 @@ func (c *ChannelModeSet) AddMode(mode ChannelMode) {
} }
// AddModeWithValue adds a ChannelMode as active with a value
func (c *Channel) AddModeWithValue(mode ChannelMode, value interface{}) {
c.mutex.Lock()
defer c.mutex.Unlock()
c.modes[mode] = value
}
// RemoveMode removes the given mode from the active set // RemoveMode removes the given mode from the active set
func (c *ChannelModeSet) RemoveMode(mode ChannelMode) { func (c *ChannelModeSet) RemoveMode(mode ChannelMode) {
c.mutex.Lock() c.mutex.Lock()
@ -170,14 +180,30 @@ func (c *ChannelModeSet) HasMode(mode ChannelMode) bool {
} }
// GetMode determines if the ChannelModeSet contains the given ChannelMode
func (c *ChannelModeSet) GetMode(mode ChannelMode) interface{} {
c.mutex.RLock()
defer c.mutex.RUnlock()
return c.modes[mode]
}
// String returns the ChannelModeSet formatted for the MODE queries // String returns the ChannelModeSet formatted for the MODE queries
func (c *ChannelModeSet) String() string { func (c *ChannelModeSet) String() string {
s := "" s := "+"
if len(c.modes) > 0 {
s = "+" params := []interface{}{}
for m, param := range c.modes {
switch m {
case ChannelModeKey, ChannelModeLimit, ChannelModeModerated, ChannelModeAnonymous, ChannelModeInviteOnly, ChannelModePrivate, ChannelModeSecret, ChannelModeTopic:
default:
continue
} }
for m := range c.modes {
s += string(m) s += string(m)
params = append(params, param)
}
for _, param := range params {
s += fmt.Sprintf(" %v", param)
} }
return s return s

2
mux.go
View File

@ -26,7 +26,7 @@ func (c *CommandsMux) HandleFunc(command string, handler CommandHandlerFunc) {
func (c *CommandsMux) ServeIRC(message *irc.Message, client *Client) { func (c *CommandsMux) ServeIRC(message *irc.Message, client *Client) {
h, ok := c.commands[message.Command] h, ok := c.commands[message.Command]
if !ok { if !ok {
m := irc.Message{Command: irc.ERR_UNKNOWNCOMMAND} m := irc.Message{Prefix: client.Server.Prefix, Command: irc.ERR_UNKNOWNCOMMAND, Params: []string{client.Nickname}}
client.Encode(&m) client.Encode(&m)
return return
} }

View File

@ -32,7 +32,7 @@ type Server struct {
type ServerConfig struct { type ServerConfig struct {
Name string Name string
MOTD string MOTD string
Welcome string Version string
TLSConfig *tls.Config TLSConfig *tls.Config
Addr string Addr string
@ -49,6 +49,12 @@ func NewServer(config ServerConfig) *Server {
s.clientsByNick = map[string]*Client{} s.clientsByNick = map[string]*Client{}
s.Prefix = &irc.Prefix{Name: config.Name} s.Prefix = &irc.Prefix{Name: config.Name}
s.channels = map[string]*Channel{} 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 return &s
} }