ergo/irc/modes.go

307 lines
8.0 KiB
Go
Raw Normal View History

// Copyright (c) 2012-2014 Jeremy Latt
// Copyright (c) 2014-2015 Edmund Huber
2017-03-12 23:08:18 +01:00
// Copyright (c) 2016-2017 Daniel Oaks <daniel@danieloaks.net>
// released under the MIT license
package irc
import (
"strconv"
"strings"
"github.com/oragono/oragono/irc/modes"
2017-06-14 20:00:53 +02:00
"github.com/oragono/oragono/irc/sno"
)
var (
// DefaultChannelModes are enabled on brand new channels when they're created.
// this can be overridden in the `channels` config, with the `default-modes` key
DefaultChannelModes = modes.Modes{
modes.NoOutside, modes.OpOnlyTopic,
}
)
2017-03-24 03:23:21 +01:00
// applyUserModeChanges applies the given changes, and returns the applied changes.
func (client *Client) applyUserModeChanges(force bool, changes modes.ModeChanges) modes.ModeChanges {
applied := make(modes.ModeChanges, 0)
2017-03-12 23:08:18 +01:00
for _, change := range changes {
switch change.Mode {
case modes.Bot, modes.Invisible, modes.WallOps, modes.UserRoleplaying, modes.Operator, modes.LocalOperator, modes.RegisteredOnly:
switch change.Op {
case modes.Add:
if !force && (change.Mode == modes.Operator || change.Mode == modes.LocalOperator) {
2017-05-08 01:15:16 +02:00
continue
}
if client.flags[change.Mode] {
2017-03-12 23:08:18 +01:00
continue
}
client.flags[change.Mode] = true
2017-03-12 23:08:18 +01:00
applied = append(applied, change)
case modes.Remove:
if !client.flags[change.Mode] {
2017-03-12 23:08:18 +01:00
continue
}
delete(client.flags, change.Mode)
2017-03-12 23:08:18 +01:00
applied = append(applied, change)
}
case modes.ServerNotice:
if !client.flags[modes.Operator] {
2017-05-08 01:15:16 +02:00
continue
}
var masks []sno.Mask
if change.Op == modes.Add || change.Op == modes.Remove {
2018-04-16 05:20:37 +02:00
var newArg string
for _, char := range change.Arg {
2018-04-16 05:20:37 +02:00
mask := sno.Mask(char)
if sno.ValidMasks[mask] {
masks = append(masks, mask)
newArg += string(char)
}
2017-03-12 23:08:18 +01:00
}
2018-04-16 05:20:37 +02:00
change.Arg = newArg
2017-05-08 01:15:16 +02:00
}
if change.Op == modes.Add {
2017-05-08 01:15:16 +02:00
client.server.snomasks.AddMasks(client, masks...)
applied = append(applied, change)
} else if change.Op == modes.Remove {
2017-05-08 01:15:16 +02:00
client.server.snomasks.RemoveMasks(client, masks...)
2017-03-12 23:08:18 +01:00
applied = append(applied, change)
}
}
// can't do anything to TLS mode
}
// return the changes we could actually apply
return applied
}
// ParseDefaultChannelModes parses the `default-modes` line of the config
func ParseDefaultChannelModes(config *Config) modes.Modes {
if config.Channels.DefaultModes == nil {
// not present in config, fall back to compile-time default
return DefaultChannelModes
}
modeChangeStrings := strings.Split(strings.TrimSpace(*config.Channels.DefaultModes), " ")
modeChanges, _ := ParseChannelModeChanges(modeChangeStrings...)
defaultChannelModes := make(modes.Modes, 0)
for _, modeChange := range modeChanges {
if modeChange.Op == modes.Add {
defaultChannelModes = append(defaultChannelModes, modeChange.Mode)
}
}
return defaultChannelModes
}
2017-03-24 03:23:21 +01:00
// ParseChannelModeChanges returns the valid changes, and the list of unknown chars.
func ParseChannelModeChanges(params ...string) (modes.ModeChanges, map[rune]bool) {
changes := make(modes.ModeChanges, 0)
2017-03-24 03:23:21 +01:00
unknown := make(map[rune]bool)
2016-06-22 13:35:26 +02:00
op := modes.List
if 0 < len(params) {
2017-03-24 03:23:21 +01:00
modeArg := params[0]
skipArgs := 1
2016-06-22 13:35:26 +02:00
for _, mode := range modeArg {
if mode == '-' || mode == '+' {
op = modes.ModeOp(mode)
2016-06-22 13:35:26 +02:00
continue
}
change := modes.ModeChange{
Mode: modes.Mode(mode),
Op: op,
2016-06-22 13:35:26 +02:00
}
// put arg into modechange if needed
switch modes.Mode(mode) {
case modes.BanMask, modes.ExceptMask, modes.InviteMask:
2017-03-24 03:23:21 +01:00
if len(params) > skipArgs {
change.Arg = params[skipArgs]
2016-09-14 11:48:47 +02:00
skipArgs++
2016-06-22 13:35:26 +02:00
} else {
change.Op = modes.List
2016-06-22 13:35:26 +02:00
}
case modes.ChannelFounder, modes.ChannelAdmin, modes.ChannelOperator, modes.Halfop, modes.Voice:
2017-03-24 03:23:21 +01:00
if len(params) > skipArgs {
change.Arg = params[skipArgs]
2016-09-14 11:48:47 +02:00
skipArgs++
2016-06-22 13:35:26 +02:00
} else {
continue
}
case modes.Key, modes.UserLimit:
2016-09-14 11:48:47 +02:00
// don't require value when removing
if change.Op == modes.Add {
2017-03-24 03:23:21 +01:00
if len(params) > skipArgs {
change.Arg = params[skipArgs]
2016-09-14 11:48:47 +02:00
skipArgs++
} else {
continue
}
}
}
var isKnown bool
for _, supportedMode := range modes.SupportedChannelModes {
if rune(supportedMode) == mode {
isKnown = true
break
}
}
for _, supportedMode := range modes.ChannelPrivModes {
if rune(supportedMode) == mode {
isKnown = true
break
}
}
if mode == rune(modes.Voice) {
2017-06-29 17:14:38 +02:00
isKnown = true
}
if !isKnown {
2017-03-24 03:23:21 +01:00
unknown[mode] = true
2017-05-08 01:15:16 +02:00
continue
2016-06-22 13:35:26 +02:00
}
2017-03-24 03:23:21 +01:00
changes = append(changes, change)
2016-06-22 13:35:26 +02:00
}
2017-03-24 03:23:21 +01:00
}
2016-06-22 13:35:26 +02:00
2017-03-24 03:23:21 +01:00
return changes, unknown
}
2016-10-23 16:50:18 +02:00
2017-03-24 03:23:21 +01:00
// ApplyChannelModeChanges applies a given set of mode changes.
2018-02-05 15:21:08 +01:00
func (channel *Channel) ApplyChannelModeChanges(client *Client, isSamode bool, changes modes.ModeChanges, rb *ResponseBuffer) modes.ModeChanges {
2017-03-24 03:23:21 +01:00
// so we only output one warning for each list type when full
listFullWarned := make(map[modes.Mode]bool)
clientIsOp := channel.ClientIsAtLeast(client, modes.ChannelOperator)
2017-03-24 03:23:21 +01:00
var alreadySentPrivError bool
applied := make(modes.ModeChanges, 0)
2017-03-24 03:23:21 +01:00
isListOp := func(change modes.ModeChange) bool {
return (change.Op == modes.List) || (change.Arg == "")
2017-11-13 08:55:26 +01:00
}
hasPrivs := func(change modes.ModeChange) bool {
2017-11-13 08:55:26 +01:00
if isSamode {
return true
}
switch change.Mode {
case modes.ChannelFounder, modes.ChannelAdmin, modes.ChannelOperator, modes.Halfop, modes.Voice:
2017-11-13 08:55:26 +01:00
// Admins can't give other people Admin or remove it from others
if change.Mode == modes.ChannelAdmin {
2017-11-13 08:55:26 +01:00
return false
}
if change.Op == modes.List {
2017-11-13 08:55:26 +01:00
return true
}
cfarg, _ := CasefoldName(change.Arg)
if change.Op == modes.Remove && cfarg == client.nickCasefolded {
2017-11-13 08:55:26 +01:00
// "There is no restriction, however, on anyone `deopping' themselves"
// <https://tools.ietf.org/html/rfc2812#section-3.1.5>
return true
}
return channel.ClientIsAtLeast(client, change.Mode)
case modes.BanMask:
2017-11-13 08:55:26 +01:00
// #163: allow unprivileged users to list ban masks
return clientIsOp || isListOp(change)
default:
return clientIsOp
}
}
2017-03-24 03:23:21 +01:00
for _, change := range changes {
2017-11-13 08:55:26 +01:00
if !hasPrivs(change) {
2017-03-24 03:23:21 +01:00
if !alreadySentPrivError {
alreadySentPrivError = true
2018-01-28 01:51:46 +01:00
rb.Add(nil, client.server.name, ERR_CHANOPRIVSNEEDED, channel.name, client.t("You're not a channel operator"))
}
2017-03-24 03:23:21 +01:00
continue
}
switch change.Mode {
case modes.BanMask, modes.ExceptMask, modes.InviteMask:
2017-11-13 08:55:26 +01:00
if isListOp(change) {
2018-02-05 15:21:08 +01:00
channel.ShowMaskList(client, change.Mode, rb)
2017-03-24 03:23:21 +01:00
continue
}
// confirm mask looks valid
mask, err := Casefold(change.Arg)
2017-03-24 03:23:21 +01:00
if err != nil {
continue
}
2016-06-22 13:35:26 +02:00
switch change.Op {
case modes.Add:
if channel.lists[change.Mode].Length() >= client.server.Limits().ChanListModes {
if !listFullWarned[change.Mode] {
2018-02-05 15:21:08 +01:00
rb.Add(nil, client.server.name, ERR_BANLISTFULL, client.Nick(), channel.Name(), change.Mode.String(), client.t("Channel list is full"))
listFullWarned[change.Mode] = true
2017-03-24 03:23:21 +01:00
}
continue
}
channel.lists[change.Mode].Add(mask)
2017-03-24 03:23:21 +01:00
applied = append(applied, change)
2016-10-23 16:50:18 +02:00
case modes.Remove:
channel.lists[change.Mode].Remove(mask)
2017-03-24 03:23:21 +01:00
applied = append(applied, change)
}
2016-06-22 13:35:26 +02:00
case modes.UserLimit:
switch change.Op {
case modes.Add:
val, err := strconv.ParseUint(change.Arg, 10, 64)
2017-03-24 03:23:21 +01:00
if err == nil {
2017-10-23 01:50:16 +02:00
channel.setUserLimit(val)
2016-06-22 13:35:26 +02:00
applied = append(applied, change)
}
case modes.Remove:
2017-10-23 01:50:16 +02:00
channel.setUserLimit(0)
2017-03-24 03:23:21 +01:00
applied = append(applied, change)
}
2016-09-14 11:48:47 +02:00
case modes.Key:
switch change.Op {
case modes.Add:
channel.setKey(change.Arg)
2016-09-14 11:48:47 +02:00
case modes.Remove:
2017-10-23 01:50:16 +02:00
channel.setKey("")
2017-03-24 03:23:21 +01:00
}
applied = append(applied, change)
2016-09-14 11:48:47 +02:00
case modes.InviteOnly, modes.Moderated, modes.NoOutside, modes.OpOnlyTopic, modes.RegisteredOnly, modes.Secret, modes.ChanRoleplaying:
if change.Op == modes.List {
2017-10-23 01:50:16 +02:00
continue
}
2016-09-14 11:48:47 +02:00
already := channel.setMode(change.Mode, change.Op == modes.Add)
2017-10-23 01:50:16 +02:00
if !already {
2017-03-24 03:23:21 +01:00
applied = append(applied, change)
}
2016-06-22 13:35:26 +02:00
case modes.ChannelFounder, modes.ChannelAdmin, modes.ChannelOperator, modes.Halfop, modes.Voice:
if change.Op == modes.List {
2017-10-23 01:50:16 +02:00
continue
}
2016-06-22 13:35:26 +02:00
2018-02-05 15:21:08 +01:00
change := channel.applyModeMemberNoMutex(client, change.Mode, change.Op, change.Arg, rb)
2017-03-24 03:23:21 +01:00
if change != nil {
applied = append(applied, *change)
2016-06-22 13:35:26 +02:00
}
}
}
2017-03-24 03:23:21 +01:00
return applied
}