|
@@ -4,50 +4,108 @@
|
4
|
4
|
package irc
|
5
|
5
|
|
6
|
6
|
import (
|
|
7
|
+ "errors"
|
7
|
8
|
"strconv"
|
8
|
9
|
"strings"
|
|
10
|
+ "sync"
|
9
|
11
|
|
10
|
12
|
"github.com/goshuirc/irc-go/ircmsg"
|
11
|
13
|
)
|
12
|
14
|
|
13
|
|
-// alertMonitors alerts everyone monitoring us that we're online.
|
14
|
|
-func (client *Client) alertMonitors() {
|
15
|
|
- // get monitors
|
16
|
|
- client.server.monitoringMutex.RLock()
|
17
|
|
- monitors := client.server.monitoring[client.nickCasefolded]
|
18
|
|
- client.server.monitoringMutex.RUnlock()
|
19
|
|
-
|
20
|
|
- // alert monitors
|
21
|
|
- for _, mClient := range monitors {
|
22
|
|
- // don't have to notify ourselves
|
23
|
|
- if mClient != client {
|
24
|
|
- mClient.SendFromClient("", client, nil, RPL_MONONLINE, mClient.nick, client.nickMaskString)
|
25
|
|
- }
|
|
15
|
+type MonitorManager struct {
|
|
16
|
+ sync.RWMutex
|
|
17
|
+ // client -> nicks it's watching
|
|
18
|
+ watching map[*Client]map[string]bool
|
|
19
|
+ // nick -> clients watching it
|
|
20
|
+ watchedby map[string]map[*Client]bool
|
|
21
|
+ // (all nicks must be normalized externally by casefolding)
|
|
22
|
+}
|
|
23
|
+
|
|
24
|
+func NewMonitorManager() *MonitorManager {
|
|
25
|
+ mm := MonitorManager{
|
|
26
|
+ watching: make(map[*Client]map[string]bool),
|
|
27
|
+ watchedby: make(map[string]map[*Client]bool),
|
26
|
28
|
}
|
|
29
|
+ return &mm
|
27
|
30
|
}
|
28
|
31
|
|
29
|
|
-// clearMonitorList clears our MONITOR list.
|
30
|
|
-func (client *Client) clearMonitorList() {
|
31
|
|
- // lockin' everything
|
32
|
|
- client.monitoringMutex.Lock()
|
33
|
|
- defer client.monitoringMutex.Unlock()
|
34
|
|
- client.server.monitoringMutex.Lock()
|
35
|
|
- defer client.server.monitoringMutex.Unlock()
|
36
|
|
-
|
37
|
|
- for name := range client.monitoring {
|
38
|
|
- // just removes current client from the list
|
39
|
|
- orig := client.server.monitoring[name]
|
40
|
|
- var index int
|
41
|
|
- for i, cli := range orig {
|
42
|
|
- if cli == client {
|
43
|
|
- index = i
|
44
|
|
- break
|
|
32
|
+var MonitorLimitExceeded = errors.New("Monitor limit exceeded")
|
|
33
|
+
|
|
34
|
+// alertMonitors alerts everyone monitoring us that we're online.
|
|
35
|
+func (manager *MonitorManager) alertMonitors(client *Client, online bool) {
|
|
36
|
+ cfnick := client.getNickCasefolded()
|
|
37
|
+ nick := client.getNick()
|
|
38
|
+ var watchers []*Client
|
|
39
|
+ // safely copy the list of clients watching our nick
|
|
40
|
+ manager.RLock()
|
|
41
|
+ for client := range manager.watchedby[cfnick] {
|
|
42
|
+ watchers = append(watchers, client)
|
|
43
|
+ }
|
|
44
|
+ manager.RUnlock()
|
|
45
|
+
|
|
46
|
+ command := RPL_MONOFFLINE
|
|
47
|
+ if online {
|
|
48
|
+ command = RPL_MONONLINE
|
|
49
|
+ }
|
|
50
|
+
|
|
51
|
+ // asynchronously send all the notifications
|
|
52
|
+ go func() {
|
|
53
|
+ for _, mClient := range watchers {
|
|
54
|
+ // don't have to notify ourselves
|
|
55
|
+ if mClient != client {
|
|
56
|
+ mClient.SendFromClient("", client, nil, command, mClient.getNick(), nick)
|
45
|
57
|
}
|
46
|
58
|
}
|
47
|
|
- client.server.monitoring[name] = append(orig[:index], orig[index+1:]...)
|
|
59
|
+ }()
|
|
60
|
+}
|
|
61
|
+
|
|
62
|
+// clearMonitorList clears our MONITOR list.
|
|
63
|
+func (manager *MonitorManager) clearMonitorList(client *Client) {
|
|
64
|
+ manager.Lock()
|
|
65
|
+ defer manager.Unlock()
|
|
66
|
+
|
|
67
|
+ for nick, _ := range manager.watching[client] {
|
|
68
|
+ delete(manager.watchedby[nick], client)
|
|
69
|
+ }
|
|
70
|
+ delete(manager.watching, client)
|
|
71
|
+}
|
|
72
|
+
|
|
73
|
+func (manager *MonitorManager) addMonitor(client *Client, nick string, limit int) error {
|
|
74
|
+ manager.Lock()
|
|
75
|
+ defer manager.Unlock()
|
|
76
|
+
|
|
77
|
+ if manager.watching[client] == nil {
|
|
78
|
+ manager.watching[client] = make(map[string]bool)
|
|
79
|
+ }
|
|
80
|
+ if manager.watchedby[nick] == nil {
|
|
81
|
+ manager.watchedby[nick] = make(map[*Client]bool)
|
48
|
82
|
}
|
49
|
83
|
|
50
|
|
- client.monitoring = make(map[string]bool)
|
|
84
|
+ if len(manager.watching[client]) >= limit {
|
|
85
|
+ return MonitorLimitExceeded
|
|
86
|
+ }
|
|
87
|
+
|
|
88
|
+ manager.watching[client][nick] = true
|
|
89
|
+ manager.watchedby[nick][client] = true
|
|
90
|
+ return nil
|
|
91
|
+}
|
|
92
|
+
|
|
93
|
+func (manager *MonitorManager) removeMonitor(client *Client, nick string) error {
|
|
94
|
+ manager.Lock()
|
|
95
|
+ defer manager.Unlock()
|
|
96
|
+ // deleting from nil maps is fine
|
|
97
|
+ delete(manager.watching[client], nick)
|
|
98
|
+ delete(manager.watchedby[nick], client)
|
|
99
|
+ return nil
|
|
100
|
+}
|
|
101
|
+
|
|
102
|
+func (manager *MonitorManager) listMonitors(client *Client) (nicks []string) {
|
|
103
|
+ manager.RLock()
|
|
104
|
+ defer manager.RUnlock()
|
|
105
|
+ for nick := range manager.watching[client] {
|
|
106
|
+ nicks = append(nicks, nick)
|
|
107
|
+ }
|
|
108
|
+ return nicks
|
51
|
109
|
}
|
52
|
110
|
|
53
|
111
|
var (
|
|
@@ -64,7 +122,7 @@ func monitorHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool
|
64
|
122
|
handler, exists := metadataSubcommands[strings.ToLower(msg.Params[0])]
|
65
|
123
|
|
66
|
124
|
if !exists {
|
67
|
|
- client.Send(nil, server.name, ERR_UNKNOWNERROR, client.nick, "MONITOR", msg.Params[0], "Unknown subcommand")
|
|
125
|
+ client.Send(nil, server.name, ERR_UNKNOWNERROR, client.getNick(), "MONITOR", msg.Params[0], "Unknown subcommand")
|
68
|
126
|
return false
|
69
|
127
|
}
|
70
|
128
|
|
|
@@ -73,49 +131,17 @@ func monitorHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool
|
73
|
131
|
|
74
|
132
|
func monitorRemoveHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
75
|
133
|
if len(msg.Params) < 2 {
|
76
|
|
- client.Send(nil, server.name, ERR_NEEDMOREPARAMS, client.nick, msg.Command, "Not enough parameters")
|
|
134
|
+ client.Send(nil, server.name, ERR_NEEDMOREPARAMS, client.getNick(), msg.Command, "Not enough parameters")
|
77
|
135
|
return false
|
78
|
136
|
}
|
79
|
137
|
|
80
|
138
|
targets := strings.Split(msg.Params[1], ",")
|
81
|
|
- for len(targets) > 0 {
|
82
|
|
- // check name length
|
83
|
|
- if len(targets[0]) < 1 {
|
84
|
|
- targets = targets[1:]
|
85
|
|
- continue
|
86
|
|
- }
|
87
|
|
-
|
88
|
|
- // remove target
|
89
|
|
- casefoldedTarget, err := CasefoldName(targets[0])
|
|
139
|
+ for _, target := range targets {
|
|
140
|
+ cfnick, err := CasefoldName(target)
|
90
|
141
|
if err != nil {
|
91
|
|
- // skip silently I guess
|
92
|
|
- targets = targets[1:]
|
93
|
142
|
continue
|
94
|
143
|
}
|
95
|
|
-
|
96
|
|
- client.monitoringMutex.Lock()
|
97
|
|
- client.server.monitoringMutex.Lock()
|
98
|
|
-
|
99
|
|
- if client.monitoring[casefoldedTarget] {
|
100
|
|
- // just removes current client from the list
|
101
|
|
- orig := server.monitoring[casefoldedTarget]
|
102
|
|
- var index int
|
103
|
|
- for i, cli := range orig {
|
104
|
|
- if cli == client {
|
105
|
|
- index = i
|
106
|
|
- break
|
107
|
|
- }
|
108
|
|
- }
|
109
|
|
- server.monitoring[casefoldedTarget] = append(orig[:index], orig[index+1:]...)
|
110
|
|
-
|
111
|
|
- delete(client.monitoring, casefoldedTarget)
|
112
|
|
- }
|
113
|
|
-
|
114
|
|
- client.monitoringMutex.Unlock()
|
115
|
|
- client.server.monitoringMutex.Unlock()
|
116
|
|
-
|
117
|
|
- // remove first element of targets list
|
118
|
|
- targets = targets[1:]
|
|
144
|
+ server.monitorManager.removeMonitor(client, cfnick)
|
119
|
145
|
}
|
120
|
146
|
|
121
|
147
|
return false
|
|
@@ -123,88 +149,68 @@ func monitorRemoveHandler(server *Server, client *Client, msg ircmsg.IrcMessage)
|
123
|
149
|
|
124
|
150
|
func monitorAddHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
125
|
151
|
if len(msg.Params) < 2 {
|
126
|
|
- client.Send(nil, server.name, ERR_NEEDMOREPARAMS, client.nick, msg.Command, "Not enough parameters")
|
|
152
|
+ client.Send(nil, server.name, ERR_NEEDMOREPARAMS, client.getNick(), msg.Command, "Not enough parameters")
|
127
|
153
|
return false
|
128
|
154
|
}
|
129
|
155
|
|
130
|
156
|
var online []string
|
131
|
157
|
var offline []string
|
132
|
158
|
|
|
159
|
+ limit := server.getLimits().MonitorEntries
|
|
160
|
+
|
133
|
161
|
targets := strings.Split(msg.Params[1], ",")
|
134
|
|
- for len(targets) > 0 {
|
|
162
|
+ for _, target := range targets {
|
135
|
163
|
// check name length
|
136
|
|
- if len(targets[0]) < 1 || len(targets[0]) > server.limits.NickLen {
|
137
|
|
- targets = targets[1:]
|
|
164
|
+ if len(target) < 1 || len(targets) > server.limits.NickLen {
|
138
|
165
|
continue
|
139
|
166
|
}
|
140
|
167
|
|
141
|
|
- // check the monitor list length
|
142
|
|
- if len(client.monitoring) >= server.limits.MonitorEntries {
|
143
|
|
- client.Send(nil, server.name, ERR_MONLISTFULL, client.nick, strconv.Itoa(server.limits.MonitorEntries), strings.Join(targets, ","))
|
144
|
|
- break
|
145
|
|
- }
|
146
|
|
-
|
147
|
168
|
// add target
|
148
|
169
|
casefoldedTarget, err := CasefoldName(targets[0])
|
149
|
170
|
if err != nil {
|
150
|
|
- // skip silently I guess
|
151
|
|
- targets = targets[1:]
|
152
|
171
|
continue
|
153
|
172
|
}
|
154
|
173
|
|
155
|
|
- client.monitoringMutex.Lock()
|
156
|
|
- client.server.monitoringMutex.Lock()
|
157
|
|
-
|
158
|
|
- if !client.monitoring[casefoldedTarget] {
|
159
|
|
- client.monitoring[casefoldedTarget] = true
|
160
|
|
-
|
161
|
|
- orig := server.monitoring[casefoldedTarget]
|
162
|
|
- server.monitoring[casefoldedTarget] = append(orig, client)
|
|
174
|
+ err = server.monitorManager.addMonitor(client, casefoldedTarget, limit)
|
|
175
|
+ if err == MonitorLimitExceeded {
|
|
176
|
+ client.Send(nil, server.name, ERR_MONLISTFULL, client.getNick(), strconv.Itoa(server.limits.MonitorEntries), strings.Join(targets, ","))
|
|
177
|
+ break
|
|
178
|
+ } else if err != nil {
|
|
179
|
+ continue
|
163
|
180
|
}
|
164
|
181
|
|
165
|
|
- client.monitoringMutex.Unlock()
|
166
|
|
- client.server.monitoringMutex.Unlock()
|
167
|
|
-
|
168
|
182
|
// add to online / offline lists
|
169
|
|
- target := server.clients.Get(casefoldedTarget)
|
170
|
|
- if target == nil {
|
|
183
|
+ if target := server.clients.Get(casefoldedTarget); target == nil {
|
171
|
184
|
offline = append(offline, targets[0])
|
172
|
185
|
} else {
|
173
|
|
- online = append(online, target.nickMaskString)
|
|
186
|
+ online = append(online, target.getNick())
|
174
|
187
|
}
|
175
|
|
-
|
176
|
|
- // remove first element of targets list
|
177
|
|
- targets = targets[1:]
|
178
|
188
|
}
|
179
|
189
|
|
180
|
190
|
if len(online) > 0 {
|
181
|
|
- client.Send(nil, server.name, RPL_MONONLINE, client.nick, strings.Join(online, ","))
|
|
191
|
+ client.Send(nil, server.name, RPL_MONONLINE, client.getNick(), strings.Join(online, ","))
|
182
|
192
|
}
|
183
|
193
|
if len(offline) > 0 {
|
184
|
|
- client.Send(nil, server.name, RPL_MONOFFLINE, client.nick, strings.Join(offline, ","))
|
|
194
|
+ client.Send(nil, server.name, RPL_MONOFFLINE, client.getNick(), strings.Join(offline, ","))
|
185
|
195
|
}
|
186
|
196
|
|
187
|
197
|
return false
|
188
|
198
|
}
|
189
|
199
|
|
190
|
200
|
func monitorClearHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
191
|
|
- client.clearMonitorList()
|
192
|
|
-
|
|
201
|
+ server.monitorManager.clearMonitorList(client)
|
193
|
202
|
return false
|
194
|
203
|
}
|
195
|
204
|
|
196
|
205
|
func monitorListHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
197
|
|
- var monitorList []string
|
198
|
|
- client.monitoringMutex.RLock()
|
199
|
|
- for name := range client.monitoring {
|
200
|
|
- monitorList = append(monitorList, name)
|
201
|
|
- }
|
202
|
|
- client.monitoringMutex.RUnlock()
|
|
206
|
+ monitorList := server.monitorManager.listMonitors(client)
|
203
|
207
|
|
204
|
208
|
for _, line := range argsToStrings(maxLastArgLength, monitorList, ",") {
|
205
|
|
- client.Send(nil, server.name, RPL_MONLIST, client.nick, line)
|
|
209
|
+ client.Send(nil, server.name, RPL_MONLIST, client.getNick(), line)
|
206
|
210
|
}
|
207
|
211
|
|
|
212
|
+ client.Send(nil, server.name, RPL_ENDOFMONLIST, "End of MONITOR list")
|
|
213
|
+
|
208
|
214
|
return false
|
209
|
215
|
}
|
210
|
216
|
|
|
@@ -212,27 +218,25 @@ func monitorStatusHandler(server *Server, client *Client, msg ircmsg.IrcMessage)
|
212
|
218
|
var online []string
|
213
|
219
|
var offline []string
|
214
|
220
|
|
215
|
|
- client.monitoringMutex.RLock()
|
216
|
|
- monitoring := client.monitoring
|
217
|
|
- client.monitoringMutex.RUnlock()
|
|
221
|
+ monitorList := server.monitorManager.listMonitors(client)
|
218
|
222
|
|
219
|
|
- for name := range monitoring {
|
|
223
|
+ for _, name := range monitorList {
|
220
|
224
|
target := server.clients.Get(name)
|
221
|
225
|
if target == nil {
|
222
|
226
|
offline = append(offline, name)
|
223
|
227
|
} else {
|
224
|
|
- online = append(online, target.nickMaskString)
|
|
228
|
+ online = append(online, target.getNick())
|
225
|
229
|
}
|
226
|
230
|
}
|
227
|
231
|
|
228
|
232
|
if len(online) > 0 {
|
229
|
233
|
for _, line := range argsToStrings(maxLastArgLength, online, ",") {
|
230
|
|
- client.Send(nil, server.name, RPL_MONONLINE, client.nick, line)
|
|
234
|
+ client.Send(nil, server.name, RPL_MONONLINE, client.getNick(), line)
|
231
|
235
|
}
|
232
|
236
|
}
|
233
|
237
|
if len(offline) > 0 {
|
234
|
238
|
for _, line := range argsToStrings(maxLastArgLength, offline, ",") {
|
235
|
|
- client.Send(nil, server.name, RPL_MONOFFLINE, client.nick, line)
|
|
239
|
+ client.Send(nil, server.name, RPL_MONOFFLINE, client.getNick(), line)
|
236
|
240
|
}
|
237
|
241
|
}
|
238
|
242
|
|