modes: Fix modes, re-add channel modes

This commit is contained in:
Daniel Oaks 2016-06-22 21:35:26 +10:00
parent 04442ddef1
commit e19c1527a4
3 changed files with 185 additions and 160 deletions

View File

@ -6,7 +6,6 @@
package irc package irc
import ( import (
"fmt"
"log" "log"
"strconv" "strconv"
) )
@ -66,7 +65,7 @@ func (channel *Channel) Names(client *Client) {
} }
if len(buffer)+1+len(nick) > maxNamLen { if len(buffer)+1+len(nick) > maxNamLen {
client.Send(nil, client.server.nameString, RPL_NAMREPLY, "=", channel.nameString, buffer) client.Send(nil, client.server.nameString, RPL_NAMREPLY, client.nickString, "=", channel.nameString, buffer)
buffer = nick buffer = nick
continue continue
} }
@ -75,8 +74,8 @@ func (channel *Channel) Names(client *Client) {
buffer += nick buffer += nick
} }
client.Send(nil, client.server.nameString, RPL_NAMREPLY, "=", channel.nameString, buffer) client.Send(nil, client.server.nameString, RPL_NAMREPLY, client.nickString, "=", channel.nameString, buffer)
client.Send(nil, client.server.nameString, RPL_ENDOFNAMES, channel.nameString, "End of NAMES list") client.Send(nil, client.server.nameString, RPL_ENDOFNAMES, client.nickString, channel.nameString, "End of NAMES list")
} }
func (channel *Channel) ClientIsOperator(client *Client) bool { func (channel *Channel) ClientIsOperator(client *Client) bool {
@ -158,7 +157,7 @@ func (channel *Channel) ModeString(client *Client) (str string) {
str += " " + strconv.FormatUint(channel.userLimit, 10) str += " " + strconv.FormatUint(channel.userLimit, 10)
} }
return return str
} }
func (channel *Channel) IsFull() bool { func (channel *Channel) IsFull() bool {
@ -199,6 +198,10 @@ func (channel *Channel) Join(client *Client, key string) {
return return
} }
for member := range channel.members {
member.Send(nil, client.nickMaskString, "JOIN", channel.nameString)
}
client.channels.Add(channel) client.channels.Add(channel)
channel.members.Add(client) channel.members.Add(client)
if !channel.flags[Persistent] && (len(channel.members) == 1) { if !channel.flags[Persistent] && (len(channel.members) == 1) {
@ -207,16 +210,8 @@ func (channel *Channel) Join(client *Client, key string) {
} }
client.Send(nil, client.nickMaskString, "JOIN", channel.nameString) client.Send(nil, client.nickMaskString, "JOIN", channel.nameString)
return
//TODO(dan): should we be continuing here????
// return was above this originally, is it required?
/*
for member := range channel.members {
member.Reply(reply)
}
channel.GetTopic(client) channel.GetTopic(client)
channel.Names(client) channel.Names(client)
*/
} }
func (channel *Channel) Part(client *Client, message string) { func (channel *Channel) Part(client *Client, message string) {
@ -233,17 +228,17 @@ func (channel *Channel) Part(client *Client, message string) {
func (channel *Channel) GetTopic(client *Client) { func (channel *Channel) GetTopic(client *Client) {
if !channel.members.Has(client) { if !channel.members.Has(client) {
client.Send(nil, client.server.nameString, ERR_NOTONCHANNEL, channel.nameString, "You're not on that channel") client.Send(nil, client.server.nameString, ERR_NOTONCHANNEL, client.nickString, channel.nameString, "You're not on that channel")
return return
} }
if channel.topic == "" { if channel.topic == "" {
// clients appear not to expect this client.Send(nil, client.server.nameString, RPL_NOTOPIC, client.nickString, channel.nameString, "No topic is set")
//replier.Reply(RplNoTopic(channel))
return return
} }
client.Send(nil, client.server.nameString, RPL_TOPIC, channel.nameString, channel.topic) client.Send(nil, client.server.nameString, RPL_TOPIC, client.nickString, channel.nameString, channel.topic)
//TODO(dan): show topic time and setter here too
} }
func (channel *Channel) SetTopic(client *Client, topic string) { func (channel *Channel) SetTopic(client *Client, topic string) {
@ -322,16 +317,11 @@ func (channel *Channel) applyModeFlag(client *Client, mode ChannelMode,
} }
func (channel *Channel) applyModeMember(client *Client, mode ChannelMode, func (channel *Channel) applyModeMember(client *Client, mode ChannelMode,
op ModeOp, nick string) bool { op ModeOp, nick string) *ChannelModeChange {
if !channel.ClientIsOperator(client) {
client.Send(nil, client.server.nameString, ERR_CHANOPRIVSNEEDED, channel.nameString, "You're not a channel operator")
return false
}
if nick == "" { if nick == "" {
//TODO(dan): shouldn't this be handled before it reaches this function? //TODO(dan): shouldn't this be handled before it reaches this function?
client.Send(nil, client.server.nameString, ERR_NEEDMOREPARAMS, "MODE", "Not enough parameters") client.Send(nil, client.server.nameString, ERR_NEEDMOREPARAMS, "MODE", "Not enough parameters")
return false return nil
} }
target := channel.server.clients.Get(Name(nick)) target := channel.server.clients.Get(Name(nick))
@ -339,30 +329,38 @@ func (channel *Channel) applyModeMember(client *Client, mode ChannelMode,
//TODO(dan): investigate using NOSUCHNICK and NOSUCHCHANNEL specifically as that other IRCd (insp?) does, //TODO(dan): investigate using NOSUCHNICK and NOSUCHCHANNEL specifically as that other IRCd (insp?) does,
// since I think that would make sense // since I think that would make sense
client.Send(nil, client.server.nameString, ERR_NOSUCHNICK, nick, "No such nick") client.Send(nil, client.server.nameString, ERR_NOSUCHNICK, nick, "No such nick")
return false return nil
} }
if !channel.members.Has(target) { if !channel.members.Has(target) {
client.Send(nil, client.server.nameString, ERR_USERNOTINCHANNEL, client.nickString, channel.nameString, "They aren't on that channel") client.Send(nil, client.server.nameString, ERR_USERNOTINCHANNEL, client.nickString, channel.nameString, "They aren't on that channel")
return false return nil
} }
switch op { switch op {
case Add: case Add:
if channel.members[target][mode] { if channel.members[target][mode] {
return false return nil
} }
channel.members[target][mode] = true channel.members[target][mode] = true
return true return &ChannelModeChange{
op: Add,
mode: mode,
arg: nick,
}
case Remove: case Remove:
if !channel.members[target][mode] { if !channel.members[target][mode] {
return false return nil
} }
channel.members[target][mode] = false channel.members[target][mode] = false
return true return &ChannelModeChange{
op: Remove,
mode: mode,
arg: nick,
} }
return false }
return nil
} }
func (channel *Channel) ShowMaskList(client *Client, mode ChannelMode) { func (channel *Channel) ShowMaskList(client *Client, mode ChannelMode) {
@ -404,117 +402,6 @@ func (channel *Channel) applyModeMask(client *Client, mode ChannelMode, op ModeO
return false return false
} }
func (channel *Channel) applyMode(client *Client, change *ChannelModeChange) bool {
switch change.mode {
case BanMask, ExceptMask, InviteMask:
return channel.applyModeMask(client, change.mode, change.op,
NewName(change.arg))
case InviteOnly, Moderated, NoOutside, OpOnlyTopic, Persistent, Secret:
return channel.applyModeFlag(client, change.mode, change.op)
case Key:
if !channel.ClientIsOperator(client) {
client.Send(nil, client.server.nameString, ERR_CHANOPRIVSNEEDED, channel.nameString, "You're not a channel operator")
return false
}
switch change.op {
case Add:
if change.arg == "" {
client.Send(nil, client.server.nameString, ERR_NEEDMOREPARAMS, "MODE", "Not enough parameters")
return false
}
key := change.arg
if key == channel.key {
return false
}
channel.key = key
return true
case Remove:
channel.key = ""
return true
}
case UserLimit:
limit, err := strconv.ParseUint(change.arg, 10, 64)
if err != nil {
client.Send(nil, client.server.nameString, ERR_NEEDMOREPARAMS, "MODE", "Not enough parameters")
return false
}
if (limit == 0) || (limit == channel.userLimit) {
return false
}
channel.userLimit = limit
return true
case ChannelFounder, ChannelAdmin, ChannelOperator, Halfop, Voice:
var hasPrivs bool
// make sure client has privs to edit the given prefix
for _, mode := range ChannelPrivModes {
if channel.members[client][mode] {
hasPrivs = true
// Admins can't give other people Admin or remove it from others,
// standard for that channel mode, we worry about this later
if mode == ChannelAdmin && change.mode == ChannelAdmin {
hasPrivs = false
}
break
} else if mode == change.mode {
break
}
}
name := NewName(change.arg)
if !hasPrivs {
if change.op == Remove && name.ToLower() == client.nick.ToLower() {
// success!
} else {
client.Send(nil, client.server.nameString, ERR_CHANOPRIVSNEEDED, channel.nameString, "You're not a channel operator")
return false
}
}
return channel.applyModeMember(client, change.mode, change.op, name.String())
default:
client.Send(nil, client.server.nameString, ERR_UNKNOWNMODE, change.mode.String(), fmt.Sprintf(":is an unknown mode char to me for %s", channel))
}
return false
}
func (channel *Channel) Mode(client *Client, changes ChannelModeChanges) {
if len(changes) == 0 {
client.Send(nil, client.server.nameString, RPL_CHANNELMODEIS, channel.nameString, channel.ModeString(client))
return
}
applied := make(ChannelModeChanges, 0)
for _, change := range changes {
if channel.applyMode(client, change) {
applied = append(applied, change)
}
}
if len(applied) > 0 {
appliedString := applied.String()
for member := range channel.members {
member.Send(nil, client.nickMaskString, "MODE", channel.nameString, appliedString)
}
if err := channel.Persist(); err != nil {
log.Println("Channel.Persist:", channel, err)
}
}
}
func (channel *Channel) Persist() (err error) { func (channel *Channel) Persist() (err error) {
if channel.flags[Persistent] { if channel.flags[Persistent] {
_, err = channel.server.db.Exec(` _, err = channel.server.db.Exec(`

View File

@ -132,9 +132,7 @@ var (
func modeHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool { func modeHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
name := NewName(msg.Params[0]) name := NewName(msg.Params[0])
if name.IsChannel() { if name.IsChannel() {
// return cmodeHandler(server, client, msg) return cmodeHandler(server, client, msg)
client.Notice("CMODEs are not yet supported!")
return false
} else { } else {
return umodeHandler(server, client, msg) return umodeHandler(server, client, msg)
} }
@ -164,14 +162,15 @@ func umodeHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
// assemble changes // assemble changes
changes := make(ModeChanges, 0) changes := make(ModeChanges, 0)
applied := make(ModeChanges, 0)
if len(msg.Params) > 1 { if len(msg.Params) > 1 {
modeArg := msg.Params[0] modeArg := msg.Params[1]
op := ModeOp(modeArg[0]) op := ModeOp(modeArg[0])
if (op == Add) || (op == Remove) { if (op == Add) || (op == Remove) {
modeArg = modeArg[1:] modeArg = modeArg[1:]
} else { } else {
client.Send(nil, server.nameString, ERR_UNKNOWNERROR, client.nickString, "MODE", "Mode string could not be parsed correctly") client.Send(nil, server.nameString, ERR_UNKNOWNMODE, client.nickString, string(modeArg[1]), "is an unknown mode character to me")
return false return false
} }
@ -195,14 +194,14 @@ func umodeHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
continue continue
} }
target.flags[change.mode] = true target.flags[change.mode] = true
changes = append(changes, change) applied = append(applied, change)
case Remove: case Remove:
if !target.flags[change.mode] { if !target.flags[change.mode] {
continue continue
} }
delete(target.flags, change.mode) delete(target.flags, change.mode)
changes = append(changes, change) applied = append(applied, change)
} }
case Operator, LocalOperator: case Operator, LocalOperator:
@ -211,7 +210,7 @@ func umodeHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
continue continue
} }
delete(target.flags, change.mode) delete(target.flags, change.mode)
changes = append(changes, change) applied = append(applied, change)
} }
} }
} }
@ -225,15 +224,153 @@ func umodeHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
return false return false
} }
/* // MODE <target> [<modestring> [<mode arguments>...]]
func (msg *ChannelModeCommand) HandleServer(server *Server) { func cmodeHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
client := msg.Client() channelName := NewName(msg.Params[0])
channel := server.channels.Get(msg.channel) channel := server.channels.Get(channelName)
if channel == nil { if channel == nil {
client.ErrNoSuchChannel(msg.channel) client.Send(nil, server.nameString, ERR_NOSUCHCHANNEL, client.nickString, msg.Params[0], "No such channel")
return return false
} }
channel.Mode(client, msg.changes) // assemble changes
//TODO(dan): split out assembling changes into func that returns changes, err
changes := make(ChannelModeChanges, 0)
applied := make(ChannelModeChanges, 0)
if len(msg.Params) > 1 {
modeArg := msg.Params[1]
op := ModeOp(modeArg[0])
if (op == Add) || (op == Remove) {
modeArg = modeArg[1:]
} else {
client.Send(nil, server.nameString, ERR_UNKNOWNMODE, client.nickString, string(modeArg[1]), "is an unknown mode character to me")
return false
}
skipArgs := 2
for _, mode := range modeArg {
if mode == '-' || mode == '+' {
op = ModeOp(mode)
continue
}
change := ChannelModeChange{
mode: ChannelMode(mode),
op: op,
}
// put arg into modechange if needed
switch ChannelMode(mode) {
case BanMask, ExceptMask, InviteMask:
if len(msg.Params) > skipArgs {
change.arg = msg.Params[skipArgs]
skipArgs += 1
} else {
change.op = List
}
case Key, UserLimit, ChannelFounder, ChannelAdmin, ChannelOperator, Halfop, Voice:
if len(msg.Params) > skipArgs {
change.arg = msg.Params[skipArgs]
skipArgs += 1
} else {
continue
}
}
applied = append(applied, &change)
}
for _, change := range changes {
switch change.mode {
case BanMask, ExceptMask, InviteMask:
mask := change.arg
list := channel.lists[change.mode]
if list == nil {
// This should never happen, but better safe than panicky.
client.Send(nil, server.nameString, ERR_UNKNOWNERROR, client.nickString, "MODE", "Could not complete MODE command")
return false
}
if (change.op == List) || (mask == "") {
channel.ShowMaskList(client, change.mode)
continue
}
switch change.op {
case Add:
list.Add(Name(mask))
applied = append(applied, change)
case Remove:
list.Remove(Name(mask))
applied = append(applied, change)
}
case InviteOnly, Moderated, NoOutside, OpOnlyTopic, Persistent, Secret:
switch change.op {
case Add:
if channel.flags[change.mode] {
continue
}
channel.flags[change.mode] = true
applied = append(applied, change)
case Remove:
if !channel.flags[change.mode] {
continue
}
delete(channel.flags, change.mode)
applied = append(applied, change)
}
case ChannelFounder, ChannelAdmin, ChannelOperator, Halfop, Voice:
// make sure client has privs to edit the given prefix
var hasPrivs bool
for _, mode := range ChannelPrivModes {
if channel.members[client][mode] {
hasPrivs = true
// Admins can't give other people Admin or remove it from others,
// standard for that channel mode, we worry about this later
if mode == ChannelAdmin && change.mode == ChannelAdmin {
hasPrivs = false
}
break
} else if mode == change.mode {
break
}
}
name := NewName(change.arg)
if !hasPrivs {
if change.op == Remove && name.ToLower() == client.nick.ToLower() {
// success!
} else {
client.Send(nil, client.server.nameString, ERR_CHANOPRIVSNEEDED, channel.nameString, "You're not a channel operator")
continue
}
}
change := channel.applyModeMember(client, change.mode, change.op, change.arg)
if change != nil {
applied = append(changes, change)
}
}
}
}
if len(applied) > 0 {
//TODO(dan): we should change the name of String and make it return a slice here
args := append([]string{channel.nameString}, strings.Split(applied.String(), " ")...)
client.Send(nil, client.nickMaskString, "MODE", args...)
} else {
//TODO(dan): we should just make ModeString return a slice here
args := append([]string{client.nickString, channel.nameString}, strings.Split(channel.ModeString(client), " ")...)
client.Send(nil, client.nickMaskString, RPL_CHANNELMODEIS, args...)
}
return false
} }
*/

View File

@ -60,6 +60,7 @@ const (
RPL_LISTEND = "323" RPL_LISTEND = "323"
RPL_CHANNELMODEIS = "324" RPL_CHANNELMODEIS = "324"
RPL_UNIQOPIS = "325" RPL_UNIQOPIS = "325"
RPL_CREATIONTIME = "329"
RPL_NOTOPIC = "331" RPL_NOTOPIC = "331"
RPL_TOPIC = "332" RPL_TOPIC = "332"
RPL_INVITING = "341" RPL_INVITING = "341"