-
-
Notifications
You must be signed in to change notification settings - Fork 17
/
Copy pathisupport.go
157 lines (126 loc) · 3.47 KB
/
isupport.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
package irc
import (
"errors"
"strings"
"sync"
)
// ISupportTracker tracks the ISUPPORT values returned by servers and provides a
// convenient way to access them.
//
// From http://www.irc.org/tech_docs/draft-brocklesby-irc-isupport-03.txt
//
// 005 RPL_ISUPPORT.
type ISupportTracker struct {
sync.RWMutex
data map[string]string
}
// NewISupportTracker creates a new tracker instance with a set of sane defaults
// if the server is missing them.
func NewISupportTracker() *ISupportTracker {
return &ISupportTracker{
data: map[string]string{
"PREFIX": "(ov)@+",
},
}
}
// Handle needs to be called for all 005 IRC messages. All other messages will
// be ignored.
func (t *ISupportTracker) Handle(msg *Message) error {
// Ensure only ISupport messages go through here
if msg.Command != "005" {
return nil
}
if len(msg.Params) < 2 {
return errors.New("malformed RPL_ISUPPORT message")
}
// Check for really old servers (or servers which based 005 off of rfc2812).
if !strings.HasSuffix(msg.Trailing(), "server") {
return errors.New("received invalid RPL_ISUPPORT message")
}
t.Lock()
defer t.Unlock()
for _, param := range msg.Params[1 : len(msg.Params)-1] {
data := strings.SplitN(param, "=", 2)
if len(data) < 2 {
t.data[data[0]] = ""
continue
}
// TODO: this should properly handle decoding values containing \xHH
t.data[data[0]] = data[1]
}
return nil
}
// IsEnabled will check for boolean ISupport values. Note that for ISupport
// boolean true simply means the value exists.
func (t *ISupportTracker) IsEnabled(key string) bool {
t.RLock()
defer t.RUnlock()
_, ok := t.data[key]
return ok
}
// GetList will check for list ISupport values.
func (t *ISupportTracker) GetList(key string) ([]string, bool) {
t.RLock()
defer t.RUnlock()
data, ok := t.data[key]
if !ok {
return nil, false
}
return strings.Split(data, ","), true
}
// GetMap will check for map ISupport values.
func (t *ISupportTracker) GetMap(key string) (map[string]string, bool) {
t.RLock()
defer t.RUnlock()
data, ok := t.data[key]
if !ok {
return nil, false
}
ret := make(map[string]string)
for _, v := range strings.Split(data, ",") {
innerData := strings.SplitN(v, ":", 2)
if len(innerData) != 2 {
return nil, false
}
ret[innerData[0]] = innerData[1]
}
return ret, true
}
// GetRaw will get the raw ISupport values.
func (t *ISupportTracker) GetRaw(key string) (string, bool) {
t.RLock()
defer t.RUnlock()
ret, ok := t.data[key]
return ret, ok
}
// GetPrefixMap gets the mapping of mode to symbol for the PREFIX value.
// Unfortunately, this is fairly specific, so it can only be used with PREFIX.
func (t *ISupportTracker) GetPrefixMap() (map[rune]rune, bool) {
// Sample: (qaohv)~&@%+
prefix, _ := t.GetRaw("PREFIX")
// We only care about the symbols
i := strings.IndexByte(prefix, ')')
if len(prefix) == 0 || prefix[0] != '(' || i < 0 {
// "Invalid prefix format"
return nil, false
}
// We loop through the string using range so we get bytes, then we throw the
// two results together in the map.
symbols := make([]rune, 0, len(prefix)/2-1) // ~&@%+
for _, r := range prefix[i+1:] {
symbols = append(symbols, r)
}
modes := make([]rune, 0, len(symbols)) // qaohv
for _, r := range prefix[1:i] {
modes = append(modes, r)
}
if len(modes) != len(symbols) {
// "Mismatched modes and symbols"
return nil, false
}
prefixes := make(map[rune]rune)
for k := range symbols {
prefixes[symbols[k]] = modes[k]
}
return prefixes, true
}