Преглед изворни кода

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
 	cd irc/modes && go test . && go vet .
32
 	cd irc/modes && go test . && go vet .
33
 	cd irc/mysql && go test . && go vet .
33
 	cd irc/mysql && go test . && go vet .
34
 	cd irc/passwd && go test . && go vet .
34
 	cd irc/passwd && go test . && go vet .
35
+	cd irc/sno && go test . && go vet .
35
 	cd irc/utils && go test . && go vet .
36
 	cd irc/utils && go test . && go vet .
36
 	./.check-gofmt.sh
37
 	./.check-gofmt.sh
37
 
38
 

+ 16
- 18
irc/modes.go Прегледај датотеку

75
 			}
75
 			}
76
 		} else {
76
 		} else {
77
 			// server notices are weird
77
 			// server notices are weird
78
-			if !client.HasMode(modes.Operator) {
78
+			if !client.HasMode(modes.Operator) || change.Op == modes.List {
79
 				continue
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
 				oper := client.Oper()
87
 				oper := client.Oper()
95
 				// #1176: require special operator privileges to subscribe to snomasks
88
 				// #1176: require special operator privileges to subscribe to snomasks
96
 				if oper.HasRoleCapab("snomasks") || oper.HasRoleCapab("ban") {
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
 				applied = append(applied, change)
100
 				applied = append(applied, change)
103
 			}
101
 			}
104
 		}
102
 		}

+ 1
- 3
irc/modes/modes.go Прегледај датотеку

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

+ 32
- 0
irc/modes/modes_test.go Прегледај датотеку

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
 func TestIssue874(t *testing.T) {
50
 func TestIssue874(t *testing.T) {
19
 	emptyUnknown := make(map[rune]bool)
51
 	emptyUnknown := make(map[rune]bool)
20
 	modes, unknown := ParseChannelModeChanges("+k")
52
 	modes, unknown := ParseChannelModeChanges("+k")

+ 15
- 13
irc/sno/constants.go Прегледај датотеку

7
 // Mask is a type of server notice mask.
7
 // Mask is a type of server notice mask.
8
 type Mask rune
8
 type Mask rune
9
 
9
 
10
+type Masks []Mask
11
+
10
 // Notice mask types
12
 // Notice mask types
11
 const (
13
 const (
12
 	LocalAnnouncements Mask = 'a'
14
 	LocalAnnouncements Mask = 'a'
18
 	LocalQuits         Mask = 'q'
20
 	LocalQuits         Mask = 'q'
19
 	Stats              Mask = 't'
21
 	Stats              Mask = 't'
20
 	LocalAccounts      Mask = 'u'
22
 	LocalAccounts      Mask = 'u'
21
-	LocalXline         Mask = 'x'
22
 	LocalVhosts        Mask = 'v'
23
 	LocalVhosts        Mask = 'v'
24
+	LocalXline         Mask = 'x'
23
 )
25
 )
24
 
26
 
25
 var (
27
 var (
39
 	}
41
 	}
40
 
42
 
41
 	// ValidMasks contains the snomasks that we support.
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 Прегледај датотеку

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 Прегледај датотеку

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
 	defer m.sendListMutex.Unlock()
24
 	defer m.sendListMutex.Unlock()
25
 
25
 
26
 	for _, mask := range masks {
26
 	for _, mask := range masks {
27
-		// confirm mask is valid
28
-		if !sno.ValidMasks[mask] {
29
-			continue
30
-		}
31
-
32
 		currentClientList := m.sendLists[mask]
27
 		currentClientList := m.sendLists[mask]
33
 
28
 
34
 		if currentClientList == nil {
29
 		if currentClientList == nil {
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
 	m.sendListMutex.RLock()
101
 	m.sendListMutex.RLock()
107
 	defer m.sendListMutex.RUnlock()
102
 	defer m.sendListMutex.RUnlock()
108
 
103
 
109
-	var masks string
110
 	for mask, clients := range m.sendLists {
104
 	for mask, clients := range m.sendLists {
111
 		for c := range clients {
105
 		for c := range clients {
112
 			if c == client {
106
 			if c == client {
113
-				masks += string(mask)
107
+				result = append(result, mask)
114
 				break
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
 }

Loading…
Откажи
Сачувај