浏览代码

fix #1074

Make snomask add/remove behavior match other ircds
tags/v2.6.0-rc1
Shivaram Lingamneni 3 年前
父节点
当前提交
91cfdb963d
共有 8 个文件被更改,包括 214 次插入44 次删除
  1. 1
    0
      Makefile
  2. 16
    18
      irc/modes.go
  3. 1
    3
      irc/modes/modes.go
  4. 32
    0
      irc/modes/modes_test.go
  5. 15
    13
      irc/sno/constants.go
  6. 87
    0
      irc/sno/utils.go
  7. 53
    0
      irc/sno/utils_test.go
  8. 9
    10
      irc/snomanager.go

+ 1
- 0
Makefile 查看文件

@@ -32,6 +32,7 @@ test:
32 32
 	cd irc/modes && go test . && go vet .
33 33
 	cd irc/mysql && go test . && go vet .
34 34
 	cd irc/passwd && go test . && go vet .
35
+	cd irc/sno && go test . && go vet .
35 36
 	cd irc/utils && go test . && go vet .
36 37
 	./.check-gofmt.sh
37 38
 

+ 16
- 18
irc/modes.go 查看文件

@@ -75,30 +75,28 @@ func ApplyUserModeChanges(client *Client, changes modes.ModeChanges, force bool,
75 75
 			}
76 76
 		} else {
77 77
 			// server notices are weird
78
-			if !client.HasMode(modes.Operator) {
78
+			if !client.HasMode(modes.Operator) || change.Op == modes.List {
79 79
 				continue
80 80
 			}
81
-			var masks []sno.Mask
82
-			if change.Op == modes.Add || change.Op == modes.Remove {
83
-				var newArg string
84
-				for _, char := range change.Arg {
85
-					mask := sno.Mask(char)
86
-					if sno.ValidMasks[mask] {
87
-						masks = append(masks, mask)
88
-						newArg += string(char)
89
-					}
90
-				}
91
-				change.Arg = newArg
92
-			}
93
-			if change.Op == modes.Add {
81
+
82
+			currentMasks := client.server.snomasks.MasksEnabled(client)
83
+			addMasks, removeMasks, newArg := sno.EvaluateSnomaskChanges(change.Op == modes.Add, change.Arg, currentMasks)
84
+
85
+			success := false
86
+			if len(addMasks) != 0 {
94 87
 				oper := client.Oper()
95 88
 				// #1176: require special operator privileges to subscribe to snomasks
96 89
 				if oper.HasRoleCapab("snomasks") || oper.HasRoleCapab("ban") {
97
-					client.server.snomasks.AddMasks(client, masks...)
98
-					applied = append(applied, change)
90
+					success = true
91
+					client.server.snomasks.AddMasks(client, addMasks...)
99 92
 				}
100
-			} else if change.Op == modes.Remove {
101
-				client.server.snomasks.RemoveMasks(client, masks...)
93
+			}
94
+			if len(removeMasks) != 0 {
95
+				success = true
96
+				client.server.snomasks.RemoveMasks(client, removeMasks...)
97
+			}
98
+			if success {
99
+				change.Arg = newArg
102 100
 				applied = append(applied, change)
103 101
 			}
104 102
 		}

+ 1
- 3
irc/modes/modes.go 查看文件

@@ -212,12 +212,10 @@ func ParseUserModeChanges(params ...string) (ModeChanges, map[rune]bool) {
212 212
 			// put arg into modechange if needed
213 213
 			switch Mode(mode) {
214 214
 			case ServerNotice:
215
-				// always require arg
215
+				// arg is optional for ServerNotice (we accept bare `-s`)
216 216
 				if len(params) > skipArgs {
217 217
 					change.Arg = params[skipArgs]
218 218
 					skipArgs++
219
-				} else {
220
-					continue
221 219
 				}
222 220
 			}
223 221
 

+ 32
- 0
irc/modes/modes_test.go 查看文件

@@ -15,6 +15,38 @@ func assertEqual(supplied, expected interface{}, t *testing.T) {
15 15
 	}
16 16
 }
17 17
 
18
+func TestParseUserModeChanges(t *testing.T) {
19
+	emptyUnknown := make(map[rune]bool)
20
+	changes, unknown := ParseUserModeChanges("+i")
21
+	assertEqual(unknown, emptyUnknown, t)
22
+	assertEqual(changes, ModeChanges{ModeChange{Op: Add, Mode: Invisible}}, t)
23
+
24
+	// no-op change to sno
25
+	changes, unknown = ParseUserModeChanges("+is")
26
+	assertEqual(unknown, emptyUnknown, t)
27
+	assertEqual(changes, ModeChanges{ModeChange{Op: Add, Mode: Invisible}, ModeChange{Op: Add, Mode: ServerNotice}}, t)
28
+
29
+	// add snomasks
30
+	changes, unknown = ParseUserModeChanges("+is", "ac")
31
+	assertEqual(unknown, emptyUnknown, t)
32
+	assertEqual(changes, ModeChanges{ModeChange{Op: Add, Mode: Invisible}, ModeChange{Op: Add, Mode: ServerNotice, Arg: "ac"}}, t)
33
+
34
+	// remove snomasks
35
+	changes, unknown = ParseUserModeChanges("+s", "-cx")
36
+	assertEqual(unknown, emptyUnknown, t)
37
+	assertEqual(changes, ModeChanges{ModeChange{Op: Add, Mode: ServerNotice, Arg: "-cx"}}, t)
38
+
39
+	// remove all snomasks (arg is parsed but has no meaning)
40
+	changes, unknown = ParseUserModeChanges("-is", "ac")
41
+	assertEqual(unknown, emptyUnknown, t)
42
+	assertEqual(changes, ModeChanges{ModeChange{Op: Remove, Mode: Invisible}, ModeChange{Op: Remove, Mode: ServerNotice, Arg: "ac"}}, t)
43
+
44
+	// remove all snomasks
45
+	changes, unknown = ParseUserModeChanges("-is")
46
+	assertEqual(unknown, emptyUnknown, t)
47
+	assertEqual(changes, ModeChanges{ModeChange{Op: Remove, Mode: Invisible}, ModeChange{Op: Remove, Mode: ServerNotice}}, t)
48
+}
49
+
18 50
 func TestIssue874(t *testing.T) {
19 51
 	emptyUnknown := make(map[rune]bool)
20 52
 	modes, unknown := ParseChannelModeChanges("+k")

+ 15
- 13
irc/sno/constants.go 查看文件

@@ -7,6 +7,8 @@ package sno
7 7
 // Mask is a type of server notice mask.
8 8
 type Mask rune
9 9
 
10
+type Masks []Mask
11
+
10 12
 // Notice mask types
11 13
 const (
12 14
 	LocalAnnouncements Mask = 'a'
@@ -18,8 +20,8 @@ const (
18 20
 	LocalQuits         Mask = 'q'
19 21
 	Stats              Mask = 't'
20 22
 	LocalAccounts      Mask = 'u'
21
-	LocalXline         Mask = 'x'
22 23
 	LocalVhosts        Mask = 'v'
24
+	LocalXline         Mask = 'x'
23 25
 )
24 26
 
25 27
 var (
@@ -39,17 +41,17 @@ var (
39 41
 	}
40 42
 
41 43
 	// ValidMasks contains the snomasks that we support.
42
-	ValidMasks = map[Mask]bool{
43
-		LocalAnnouncements: true,
44
-		LocalConnects:      true,
45
-		LocalChannels:      true,
46
-		LocalKills:         true,
47
-		LocalNicks:         true,
48
-		LocalOpers:         true,
49
-		LocalQuits:         true,
50
-		Stats:              true,
51
-		LocalAccounts:      true,
52
-		LocalXline:         true,
53
-		LocalVhosts:        true,
44
+	ValidMasks = []Mask{
45
+		LocalAnnouncements,
46
+		LocalConnects,
47
+		LocalChannels,
48
+		LocalKills,
49
+		LocalNicks,
50
+		LocalOpers,
51
+		LocalQuits,
52
+		Stats,
53
+		LocalAccounts,
54
+		LocalVhosts,
55
+		LocalXline,
54 56
 	}
55 57
 )

+ 87
- 0
irc/sno/utils.go 查看文件

@@ -0,0 +1,87 @@
1
+// Copyright (c) 2020 Shivaram Lingamneni
2
+// released under the MIT license
3
+
4
+package sno
5
+
6
+import (
7
+	"strings"
8
+)
9
+
10
+func IsValidMask(r rune) bool {
11
+	for _, m := range ValidMasks {
12
+		if m == Mask(r) {
13
+			return true
14
+		}
15
+	}
16
+	return false
17
+}
18
+
19
+func (masks Masks) String() string {
20
+	var buf strings.Builder
21
+	buf.Grow(len(masks))
22
+	for _, m := range masks {
23
+		buf.WriteRune(rune(m))
24
+	}
25
+	return buf.String()
26
+}
27
+
28
+func (masks Masks) Contains(mask Mask) bool {
29
+	for _, m := range masks {
30
+		if mask == m {
31
+			return true
32
+		}
33
+	}
34
+	return false
35
+}
36
+
37
+// Evaluate changes to snomasks made with MODE. There are several cases:
38
+// adding snomasks with `/mode +s a` or `/mode +s +a`, removing them with `/mode +s -a`,
39
+// adding all with `/mode +s *` or `/mode +s +*`, removing all with `/mode +s -*` or `/mode -s`
40
+func EvaluateSnomaskChanges(add bool, arg string, currentMasks Masks) (addMasks, removeMasks Masks, newArg string) {
41
+	if add {
42
+		if len(arg) == 0 {
43
+			return
44
+		}
45
+		add := true
46
+		switch arg[0] {
47
+		case '+':
48
+			arg = arg[1:]
49
+		case '-':
50
+			add = false
51
+			arg = arg[1:]
52
+		default:
53
+			// add
54
+		}
55
+		if strings.IndexByte(arg, '*') != -1 {
56
+			if add {
57
+				for _, mask := range ValidMasks {
58
+					if !currentMasks.Contains(mask) {
59
+						addMasks = append(addMasks, mask)
60
+					}
61
+				}
62
+			} else {
63
+				removeMasks = currentMasks
64
+			}
65
+		} else {
66
+			for _, r := range arg {
67
+				if IsValidMask(r) {
68
+					m := Mask(r)
69
+					if add && !currentMasks.Contains(m) {
70
+						addMasks = append(addMasks, m)
71
+					} else if !add && currentMasks.Contains(m) {
72
+						removeMasks = append(removeMasks, m)
73
+					}
74
+				}
75
+			}
76
+		}
77
+		if len(addMasks) != 0 {
78
+			newArg = "+" + addMasks.String()
79
+		} else if len(removeMasks) != 0 {
80
+			newArg = "-" + removeMasks.String()
81
+		}
82
+	} else {
83
+		removeMasks = currentMasks
84
+		newArg = ""
85
+	}
86
+	return
87
+}

+ 53
- 0
irc/sno/utils_test.go 查看文件

@@ -0,0 +1,53 @@
1
+// Copyright (c) 2020 Shivaram Lingamneni
2
+// released under the MIT license
3
+
4
+package sno
5
+
6
+import (
7
+	"fmt"
8
+	"reflect"
9
+	"testing"
10
+)
11
+
12
+func assertEqual(supplied, expected interface{}, t *testing.T) {
13
+	if !reflect.DeepEqual(supplied, expected) {
14
+		panic(fmt.Sprintf("expected %#v but got %#v", expected, supplied))
15
+	}
16
+}
17
+
18
+func TestEvaluateSnomaskChanges(t *testing.T) {
19
+	add, remove, newArg := EvaluateSnomaskChanges(true, "*", nil)
20
+	assertEqual(add, Masks{'a', 'c', 'j', 'k', 'n', 'o', 'q', 't', 'u', 'v', 'x'}, t)
21
+	assertEqual(len(remove), 0, t)
22
+	assertEqual(newArg, "+acjknoqtuvx", t)
23
+
24
+	add, remove, newArg = EvaluateSnomaskChanges(true, "*", Masks{'a', 'u'})
25
+	assertEqual(add, Masks{'c', 'j', 'k', 'n', 'o', 'q', 't', 'v', 'x'}, t)
26
+	assertEqual(len(remove), 0, t)
27
+	assertEqual(newArg, "+cjknoqtvx", t)
28
+
29
+	add, remove, newArg = EvaluateSnomaskChanges(true, "-a", Masks{'a', 'u'})
30
+	assertEqual(len(add), 0, t)
31
+	assertEqual(remove, Masks{'a'}, t)
32
+	assertEqual(newArg, "-a", t)
33
+
34
+	add, remove, newArg = EvaluateSnomaskChanges(true, "-*", Masks{'a', 'u'})
35
+	assertEqual(len(add), 0, t)
36
+	assertEqual(remove, Masks{'a', 'u'}, t)
37
+	assertEqual(newArg, "-au", t)
38
+
39
+	add, remove, newArg = EvaluateSnomaskChanges(true, "+c", Masks{'a', 'u'})
40
+	assertEqual(add, Masks{'c'}, t)
41
+	assertEqual(len(remove), 0, t)
42
+	assertEqual(newArg, "+c", t)
43
+
44
+	add, remove, newArg = EvaluateSnomaskChanges(false, "", Masks{'a', 'u'})
45
+	assertEqual(len(add), 0, t)
46
+	assertEqual(remove, Masks{'a', 'u'}, t)
47
+	assertEqual(newArg, "", t)
48
+
49
+	add, remove, newArg = EvaluateSnomaskChanges(false, "*", Masks{'a', 'u'})
50
+	assertEqual(len(add), 0, t)
51
+	assertEqual(remove, Masks{'a', 'u'}, t)
52
+	assertEqual(newArg, "", t)
53
+}

+ 9
- 10
irc/snomanager.go 查看文件

@@ -24,11 +24,6 @@ func (m *SnoManager) AddMasks(client *Client, masks ...sno.Mask) {
24 24
 	defer m.sendListMutex.Unlock()
25 25
 
26 26
 	for _, mask := range masks {
27
-		// confirm mask is valid
28
-		if !sno.ValidMasks[mask] {
29
-			continue
30
-		}
31
-
32 27
 		currentClientList := m.sendLists[mask]
33 28
 
34 29
 		if currentClientList == nil {
@@ -101,19 +96,23 @@ func (m *SnoManager) Send(mask sno.Mask, content string) {
101 96
 	}
102 97
 }
103 98
 
104
-// String returns the snomasks currently enabled.
105
-func (m *SnoManager) String(client *Client) string {
99
+// MasksEnabled returns the snomasks currently enabled.
100
+func (m *SnoManager) MasksEnabled(client *Client) (result sno.Masks) {
106 101
 	m.sendListMutex.RLock()
107 102
 	defer m.sendListMutex.RUnlock()
108 103
 
109
-	var masks string
110 104
 	for mask, clients := range m.sendLists {
111 105
 		for c := range clients {
112 106
 			if c == client {
113
-				masks += string(mask)
107
+				result = append(result, mask)
114 108
 				break
115 109
 			}
116 110
 		}
117 111
 	}
118
-	return masks
112
+	return
113
+}
114
+
115
+func (m *SnoManager) String(client *Client) string {
116
+	masks := m.MasksEnabled(client)
117
+	return masks.String()
119 118
 }

正在加载...
取消
保存