|
@@ -23,12 +23,13 @@ type Channel struct {
|
23
|
23
|
flags ModeSet
|
24
|
24
|
lists map[Mode]*UserMaskSet
|
25
|
25
|
key string
|
26
|
|
- membersMutex sync.RWMutex
|
27
|
26
|
members MemberSet
|
|
27
|
+ membersCache []*Client // allow iteration over channel members without holding the lock
|
28
|
28
|
name string
|
29
|
29
|
nameCasefolded string
|
30
|
30
|
server *Server
|
31
|
31
|
createdTime time.Time
|
|
32
|
+ stateMutex sync.RWMutex
|
32
|
33
|
topic string
|
33
|
34
|
topicSetBy string
|
34
|
35
|
topicSetTime time.Time
|
|
@@ -68,28 +69,29 @@ func NewChannel(s *Server, name string, addDefaultModes bool) *Channel {
|
68
|
69
|
return channel
|
69
|
70
|
}
|
70
|
71
|
|
71
|
|
-// IsEmpty returns true if the channel has no clients.
|
72
|
|
-func (channel *Channel) IsEmpty() bool {
|
73
|
|
- channel.membersMutex.RLock()
|
74
|
|
- defer channel.membersMutex.RUnlock()
|
75
|
|
-
|
76
|
|
- return channel.isEmptyNoMutex()
|
77
|
|
-}
|
|
72
|
+func (channel *Channel) regenerateMembersCache() {
|
|
73
|
+ // this is eventually consistent even without holding the writable Lock()
|
|
74
|
+ // throughout the update; all updates to `members` while holding Lock()
|
|
75
|
+ // have a serial order, so the call to `regenerateMembersCache` that
|
|
76
|
+ // happens-after the last one will see *all* the updates
|
|
77
|
+ channel.stateMutex.RLock()
|
|
78
|
+ result := make([]*Client, len(channel.members))
|
|
79
|
+ i := 0
|
|
80
|
+ for client := range channel.members {
|
|
81
|
+ result[i] = client
|
|
82
|
+ i++
|
|
83
|
+ }
|
|
84
|
+ channel.stateMutex.RUnlock()
|
|
85
|
+ channel.stateMutex.Lock()
|
|
86
|
+ channel.membersCache = result
|
|
87
|
+ channel.stateMutex.Unlock()
|
|
88
|
+ return
|
78
|
89
|
|
79
|
|
-func (channel *Channel) isEmptyNoMutex() bool {
|
80
|
|
- return len(channel.members) == 0
|
81
|
90
|
}
|
82
|
91
|
|
83
|
92
|
// Names sends the list of users joined to the channel to the given client.
|
84
|
93
|
func (channel *Channel) Names(client *Client) {
|
85
|
|
- channel.membersMutex.RLock()
|
86
|
|
- defer channel.membersMutex.RUnlock()
|
87
|
|
-
|
88
|
|
- channel.namesNoMutex(client)
|
89
|
|
-}
|
90
|
|
-
|
91
|
|
-func (channel *Channel) namesNoMutex(client *Client) {
|
92
|
|
- currentNicks := channel.nicksNoMutex(client)
|
|
94
|
+ currentNicks := channel.nicks(client)
|
93
|
95
|
// assemble and send replies
|
94
|
96
|
maxNamLen := 480 - len(client.server.name) - len(client.nick)
|
95
|
97
|
var buffer string
|
|
@@ -115,14 +117,8 @@ func (channel *Channel) namesNoMutex(client *Client) {
|
115
|
117
|
|
116
|
118
|
// ClientIsAtLeast returns whether the client has at least the given channel privilege.
|
117
|
119
|
func (channel *Channel) ClientIsAtLeast(client *Client, permission Mode) bool {
|
118
|
|
- channel.membersMutex.RLock()
|
119
|
|
- defer channel.membersMutex.RUnlock()
|
120
|
|
-
|
121
|
|
- return channel.clientIsAtLeastNoMutex(client, permission)
|
122
|
|
-}
|
123
|
|
-
|
124
|
|
-func (channel *Channel) clientIsAtLeastNoMutex(client *Client, permission Mode) bool {
|
125
|
|
- // requires RLock()
|
|
120
|
+ channel.stateMutex.RLock()
|
|
121
|
+ defer channel.stateMutex.RUnlock()
|
126
|
122
|
|
127
|
123
|
// get voice, since it's not a part of ChannelPrivModes
|
128
|
124
|
if channel.members.HasMode(client, permission) {
|
|
@@ -164,60 +160,119 @@ func (modes ModeSet) Prefixes(isMultiPrefix bool) string {
|
164
|
160
|
return prefixes
|
165
|
161
|
}
|
166
|
162
|
|
167
|
|
-func (channel *Channel) nicksNoMutex(target *Client) []string {
|
|
163
|
+func (channel *Channel) ClientPrefixes(client *Client, isMultiPrefix bool) string {
|
|
164
|
+ channel.stateMutex.RLock()
|
|
165
|
+ defer channel.stateMutex.RUnlock()
|
|
166
|
+ modes, present := channel.members[client]
|
|
167
|
+ if !present {
|
|
168
|
+ return ""
|
|
169
|
+ } else {
|
|
170
|
+ return modes.Prefixes(isMultiPrefix)
|
|
171
|
+ }
|
|
172
|
+}
|
|
173
|
+
|
|
174
|
+func (channel *Channel) ClientHasPrivsOver(client *Client, target *Client) bool {
|
|
175
|
+ channel.stateMutex.RLock()
|
|
176
|
+ defer channel.stateMutex.RUnlock()
|
|
177
|
+
|
|
178
|
+ clientModes := channel.members[client]
|
|
179
|
+ targetModes := channel.members[target]
|
|
180
|
+ result := false
|
|
181
|
+ for _, mode := range ChannelPrivModes {
|
|
182
|
+ if clientModes[mode] {
|
|
183
|
+ result = true
|
|
184
|
+ // admins cannot kick other admins
|
|
185
|
+ if mode == ChannelAdmin && targetModes[ChannelAdmin] {
|
|
186
|
+ result = false
|
|
187
|
+ }
|
|
188
|
+ break
|
|
189
|
+ } else if channel.members[target][mode] {
|
|
190
|
+ break
|
|
191
|
+ }
|
|
192
|
+ }
|
|
193
|
+ return result
|
|
194
|
+}
|
|
195
|
+
|
|
196
|
+func (channel *Channel) nicks(target *Client) []string {
|
168
|
197
|
isMultiPrefix := (target != nil) && target.capabilities.Has(caps.MultiPrefix)
|
169
|
198
|
isUserhostInNames := (target != nil) && target.capabilities.Has(caps.UserhostInNames)
|
170
|
|
- nicks := make([]string, len(channel.members))
|
|
199
|
+
|
|
200
|
+ // slightly cumbersome: get the mutex and copy both the client pointers and
|
|
201
|
+ // the mode prefixes
|
|
202
|
+ channel.stateMutex.RLock()
|
|
203
|
+ length := len(channel.members)
|
|
204
|
+ clients := make([]*Client, length)
|
|
205
|
+ result := make([]string, length)
|
171
|
206
|
i := 0
|
172
|
207
|
for client, modes := range channel.members {
|
173
|
|
- nicks[i] += modes.Prefixes(isMultiPrefix)
|
|
208
|
+ clients[i] = client
|
|
209
|
+ result[i] = modes.Prefixes(isMultiPrefix)
|
|
210
|
+ i++
|
|
211
|
+ }
|
|
212
|
+ channel.stateMutex.RUnlock()
|
|
213
|
+
|
|
214
|
+ i = 0
|
|
215
|
+ for i < length {
|
174
|
216
|
if isUserhostInNames {
|
175
|
|
- nicks[i] += client.nickMaskString
|
|
217
|
+ result[i] += clients[i].getNickMaskString()
|
176
|
218
|
} else {
|
177
|
|
- nicks[i] += client.nick
|
|
219
|
+ result[i] += clients[i].getNick()
|
178
|
220
|
}
|
179
|
221
|
i++
|
180
|
222
|
}
|
181
|
|
- return nicks
|
|
223
|
+
|
|
224
|
+ return result
|
|
225
|
+}
|
|
226
|
+
|
|
227
|
+func (channel *Channel) hasClient(client *Client) bool {
|
|
228
|
+ channel.stateMutex.RLock()
|
|
229
|
+ defer channel.stateMutex.RUnlock()
|
|
230
|
+ _, present := channel.members[client]
|
|
231
|
+ return present
|
182
|
232
|
}
|
183
|
233
|
|
184
|
234
|
// <mode> <mode params>
|
185
|
|
-func (channel *Channel) modeStringNoLock(client *Client) (str string) {
|
186
|
|
- // RLock()
|
187
|
|
- isMember := client.flags[Operator] || channel.members.Has(client)
|
188
|
|
- // RUnlock()
|
|
235
|
+func (channel *Channel) modeStrings(client *Client) (result []string) {
|
|
236
|
+ isMember := client.HasMode(Operator) || channel.hasClient(client)
|
189
|
237
|
showKey := isMember && (channel.key != "")
|
190
|
238
|
showUserLimit := channel.userLimit > 0
|
191
|
239
|
|
|
240
|
+ modes := "+"
|
|
241
|
+
|
192
|
242
|
// flags with args
|
193
|
243
|
if showKey {
|
194
|
|
- str += Key.String()
|
|
244
|
+ modes += Key.String()
|
195
|
245
|
}
|
196
|
246
|
if showUserLimit {
|
197
|
|
- str += UserLimit.String()
|
|
247
|
+ modes += UserLimit.String()
|
198
|
248
|
}
|
199
|
249
|
|
|
250
|
+ channel.stateMutex.RLock()
|
|
251
|
+ defer channel.stateMutex.RUnlock()
|
|
252
|
+
|
200
|
253
|
// flags
|
201
|
254
|
for mode := range channel.flags {
|
202
|
|
- str += mode.String()
|
|
255
|
+ modes += mode.String()
|
203
|
256
|
}
|
204
|
257
|
|
205
|
|
- str = "+" + str
|
|
258
|
+ result = []string{modes}
|
206
|
259
|
|
207
|
260
|
// args for flags with args: The order must match above to keep
|
208
|
261
|
// positional arguments in place.
|
209
|
262
|
if showKey {
|
210
|
|
- str += " " + channel.key
|
|
263
|
+ result = append(result, channel.key)
|
211
|
264
|
}
|
212
|
265
|
if showUserLimit {
|
213
|
|
- str += " " + strconv.FormatUint(channel.userLimit, 10)
|
|
266
|
+ result = append(result, strconv.FormatUint(channel.userLimit, 10))
|
214
|
267
|
}
|
215
|
268
|
|
216
|
|
- return str
|
|
269
|
+ return
|
217
|
270
|
}
|
218
|
271
|
|
219
|
272
|
// IsFull returns true if this channel is at its' members limit.
|
220
|
273
|
func (channel *Channel) IsFull() bool {
|
|
274
|
+ channel.stateMutex.RLock()
|
|
275
|
+ defer channel.stateMutex.RUnlock()
|
221
|
276
|
return (channel.userLimit > 0) && (uint64(len(channel.members)) >= channel.userLimit)
|
222
|
277
|
}
|
223
|
278
|
|
|
@@ -229,9 +284,7 @@ func (channel *Channel) CheckKey(key string) bool {
|
229
|
284
|
// Join joins the given client to this channel (if they can be joined).
|
230
|
285
|
//TODO(dan): /SAJOIN and maybe a ForceJoin function?
|
231
|
286
|
func (channel *Channel) Join(client *Client, key string) {
|
232
|
|
- channel.membersMutex.Lock()
|
233
|
|
- defer channel.membersMutex.Unlock()
|
234
|
|
- if channel.members.Has(client) {
|
|
287
|
+ if channel.hasClient(client) {
|
235
|
288
|
// already joined, no message needs to be sent
|
236
|
289
|
return
|
237
|
290
|
}
|
|
@@ -261,7 +314,7 @@ func (channel *Channel) Join(client *Client, key string) {
|
261
|
314
|
|
262
|
315
|
client.server.logger.Debug("join", fmt.Sprintf("%s joined channel %s", client.nick, channel.name))
|
263
|
316
|
|
264
|
|
- for member := range channel.members {
|
|
317
|
+ for _, member := range channel.Members() {
|
265
|
318
|
if member.capabilities.Has(caps.ExtendedJoin) {
|
266
|
319
|
member.Send(nil, client.nickMaskString, "JOIN", channel.name, client.account.Name, client.realname)
|
267
|
320
|
} else {
|
|
@@ -269,8 +322,13 @@ func (channel *Channel) Join(client *Client, key string) {
|
269
|
322
|
}
|
270
|
323
|
}
|
271
|
324
|
|
272
|
|
- client.channels.Add(channel)
|
|
325
|
+ channel.stateMutex.Lock()
|
273
|
326
|
channel.members.Add(client)
|
|
327
|
+ firstJoin := len(channel.members) == 1
|
|
328
|
+ channel.stateMutex.Unlock()
|
|
329
|
+ channel.regenerateMembersCache()
|
|
330
|
+
|
|
331
|
+ client.addChannel(channel)
|
274
|
332
|
|
275
|
333
|
// give channel mode if necessary
|
276
|
334
|
var newChannel bool
|
|
@@ -281,20 +339,25 @@ func (channel *Channel) Join(client *Client, key string) {
|
281
|
339
|
chanReg := client.server.loadChannelNoMutex(tx, channel.nameCasefolded)
|
282
|
340
|
|
283
|
341
|
if chanReg == nil {
|
284
|
|
- if len(channel.members) == 1 {
|
|
342
|
+ if firstJoin {
|
|
343
|
+ channel.stateMutex.Lock()
|
285
|
344
|
channel.createdTime = time.Now()
|
286
|
345
|
channel.members[client][ChannelOperator] = true
|
|
346
|
+ channel.stateMutex.Unlock()
|
287
|
347
|
givenMode = &ChannelOperator
|
288
|
348
|
newChannel = true
|
289
|
349
|
}
|
290
|
350
|
} else {
|
291
|
351
|
// we should only do this on registered channels
|
292
|
352
|
if client.account != nil && client.account.Name == chanReg.Founder {
|
|
353
|
+ channel.stateMutex.Lock()
|
293
|
354
|
channel.members[client][ChannelFounder] = true
|
|
355
|
+ channel.stateMutex.Unlock()
|
294
|
356
|
givenMode = &ChannelFounder
|
295
|
357
|
}
|
296
|
|
- if len(channel.members) == 1 {
|
|
358
|
+ if firstJoin {
|
297
|
359
|
// apply other details if new channel
|
|
360
|
+ channel.stateMutex.Lock()
|
298
|
361
|
channel.topic = chanReg.Topic
|
299
|
362
|
channel.topicSetBy = chanReg.TopicSetBy
|
300
|
363
|
channel.topicSetTime = chanReg.TopicSetTime
|
|
@@ -309,6 +372,7 @@ func (channel *Channel) Join(client *Client, key string) {
|
309
|
372
|
for _, mask := range chanReg.Invitelist {
|
310
|
373
|
channel.lists[InviteMask].Add(mask)
|
311
|
374
|
}
|
|
375
|
+ channel.stateMutex.Unlock()
|
312
|
376
|
}
|
313
|
377
|
}
|
314
|
378
|
return nil
|
|
@@ -321,11 +385,11 @@ func (channel *Channel) Join(client *Client, key string) {
|
321
|
385
|
}
|
322
|
386
|
// don't sent topic when it's an entirely new channel
|
323
|
387
|
if !newChannel {
|
324
|
|
- channel.getTopicNoMutex(client) // we already have Lock
|
|
388
|
+ channel.SendTopic(client)
|
325
|
389
|
}
|
326
|
|
- channel.namesNoMutex(client)
|
|
390
|
+ channel.Names(client)
|
327
|
391
|
if givenMode != nil {
|
328
|
|
- for member := range channel.members {
|
|
392
|
+ for _, member := range channel.Members() {
|
329
|
393
|
member.Send(nil, client.server.name, "MODE", channel.name, fmt.Sprintf("+%v", *givenMode), client.nick)
|
330
|
394
|
}
|
331
|
395
|
}
|
|
@@ -333,58 +397,50 @@ func (channel *Channel) Join(client *Client, key string) {
|
333
|
397
|
|
334
|
398
|
// Part parts the given client from this channel, with the given message.
|
335
|
399
|
func (channel *Channel) Part(client *Client, message string) {
|
336
|
|
- channel.membersMutex.Lock()
|
337
|
|
- defer channel.membersMutex.Unlock()
|
338
|
|
-
|
339
|
|
- if !channel.members.Has(client) {
|
|
400
|
+ if !channel.hasClient(client) {
|
340
|
401
|
client.Send(nil, client.server.name, ERR_NOTONCHANNEL, channel.name, "You're not on that channel")
|
341
|
402
|
return
|
342
|
403
|
}
|
343
|
404
|
|
344
|
|
- for member := range channel.members {
|
|
405
|
+ for _, member := range channel.Members() {
|
345
|
406
|
member.Send(nil, client.nickMaskString, "PART", channel.name, message)
|
346
|
407
|
}
|
347
|
|
- channel.quitNoMutex(client)
|
|
408
|
+ channel.Quit(client)
|
348
|
409
|
|
349
|
410
|
client.server.logger.Debug("part", fmt.Sprintf("%s left channel %s", client.nick, channel.name))
|
350
|
411
|
}
|
351
|
412
|
|
352
|
|
-// GetTopic sends the channel topic to the given client.
|
353
|
|
-func (channel *Channel) GetTopic(client *Client) {
|
354
|
|
- channel.membersMutex.RLock()
|
355
|
|
- defer channel.membersMutex.RUnlock()
|
356
|
|
-
|
357
|
|
- channel.getTopicNoMutex(client)
|
358
|
|
-}
|
359
|
|
-
|
360
|
|
-// GetTopic sends the channel topic to the given client without getting the membersMutex.
|
361
|
|
-// This is required because of channel joins.
|
362
|
|
-func (channel *Channel) getTopicNoMutex(client *Client) {
|
363
|
|
- if !channel.members.Has(client) {
|
|
413
|
+// SendTopic sends the channel topic to the given client.
|
|
414
|
+func (channel *Channel) SendTopic(client *Client) {
|
|
415
|
+ if !channel.hasClient(client) {
|
364
|
416
|
client.Send(nil, client.server.name, ERR_NOTONCHANNEL, client.nick, channel.name, "You're not on that channel")
|
365
|
417
|
return
|
366
|
418
|
}
|
367
|
419
|
|
368
|
|
- if channel.topic == "" {
|
369
|
|
- client.Send(nil, client.server.name, RPL_NOTOPIC, client.nick, channel.name, "No topic is set")
|
|
420
|
+ channel.stateMutex.RLock()
|
|
421
|
+ name := channel.name
|
|
422
|
+ topic := channel.topic
|
|
423
|
+ topicSetBy := channel.topicSetBy
|
|
424
|
+ topicSetTime := channel.topicSetTime
|
|
425
|
+ channel.stateMutex.RUnlock()
|
|
426
|
+
|
|
427
|
+ if topic == "" {
|
|
428
|
+ client.Send(nil, client.server.name, RPL_NOTOPIC, client.nick, name, "No topic is set")
|
370
|
429
|
return
|
371
|
430
|
}
|
372
|
431
|
|
373
|
|
- client.Send(nil, client.server.name, RPL_TOPIC, client.nick, channel.name, channel.topic)
|
374
|
|
- client.Send(nil, client.server.name, RPL_TOPICTIME, client.nick, channel.name, channel.topicSetBy, strconv.FormatInt(channel.topicSetTime.Unix(), 10))
|
|
432
|
+ client.Send(nil, client.server.name, RPL_TOPIC, client.nick, name, topic)
|
|
433
|
+ client.Send(nil, client.server.name, RPL_TOPICTIME, client.nick, name, topicSetBy, strconv.FormatInt(topicSetTime.Unix(), 10))
|
375
|
434
|
}
|
376
|
435
|
|
377
|
436
|
// SetTopic sets the topic of this channel, if the client is allowed to do so.
|
378
|
437
|
func (channel *Channel) SetTopic(client *Client, topic string) {
|
379
|
|
- channel.membersMutex.RLock()
|
380
|
|
- defer channel.membersMutex.RUnlock()
|
381
|
|
-
|
382
|
|
- if !(client.flags[Operator] || channel.members.Has(client)) {
|
|
438
|
+ if !(client.flags[Operator] || channel.hasClient(client)) {
|
383
|
439
|
client.Send(nil, client.server.name, ERR_NOTONCHANNEL, channel.name, "You're not on that channel")
|
384
|
440
|
return
|
385
|
441
|
}
|
386
|
442
|
|
387
|
|
- if channel.flags[OpOnlyTopic] && !channel.ClientIsAtLeast(client, ChannelOperator) {
|
|
443
|
+ if channel.HasMode(OpOnlyTopic) && !channel.ClientIsAtLeast(client, ChannelOperator) {
|
388
|
444
|
client.Send(nil, client.server.name, ERR_CHANOPRIVSNEEDED, channel.name, "You're not a channel operator")
|
389
|
445
|
return
|
390
|
446
|
}
|
|
@@ -393,12 +449,14 @@ func (channel *Channel) SetTopic(client *Client, topic string) {
|
393
|
449
|
topic = topic[:client.server.limits.TopicLen]
|
394
|
450
|
}
|
395
|
451
|
|
|
452
|
+ channel.stateMutex.Lock()
|
396
|
453
|
channel.topic = topic
|
397
|
454
|
channel.topicSetBy = client.nickMaskString
|
398
|
455
|
channel.topicSetTime = time.Now()
|
|
456
|
+ channel.stateMutex.Unlock()
|
399
|
457
|
|
400
|
|
- for member := range channel.members {
|
401
|
|
- member.Send(nil, client.nickMaskString, "TOPIC", channel.name, channel.topic)
|
|
458
|
+ for _, member := range channel.Members() {
|
|
459
|
+ member.Send(nil, client.nickMaskString, "TOPIC", channel.name, topic)
|
402
|
460
|
}
|
403
|
461
|
|
404
|
462
|
// update saved channel topic for registered chans
|
|
@@ -422,13 +480,14 @@ func (channel *Channel) SetTopic(client *Client, topic string) {
|
422
|
480
|
|
423
|
481
|
// CanSpeak returns true if the client can speak on this channel.
|
424
|
482
|
func (channel *Channel) CanSpeak(client *Client) bool {
|
425
|
|
- channel.membersMutex.RLock()
|
426
|
|
- defer channel.membersMutex.RUnlock()
|
|
483
|
+ channel.stateMutex.RLock()
|
|
484
|
+ defer channel.stateMutex.RUnlock()
|
427
|
485
|
|
428
|
|
- if channel.flags[NoOutside] && !channel.members.Has(client) {
|
|
486
|
+ _, hasClient := channel.members[client]
|
|
487
|
+ if channel.flags[NoOutside] && !hasClient {
|
429
|
488
|
return false
|
430
|
489
|
}
|
431
|
|
- if channel.flags[Moderated] && !channel.clientIsAtLeastNoMutex(client, Voice) {
|
|
490
|
+ if channel.flags[Moderated] && !channel.ClientIsAtLeast(client, Voice) {
|
432
|
491
|
return false
|
433
|
492
|
}
|
434
|
493
|
if channel.flags[RegisteredOnly] && client.account == &NoAccount {
|
|
@@ -449,15 +508,12 @@ func (channel *Channel) sendMessage(msgid, cmd string, requiredCaps []caps.Capab
|
449
|
508
|
return
|
450
|
509
|
}
|
451
|
510
|
|
452
|
|
- channel.membersMutex.RLock()
|
453
|
|
- defer channel.membersMutex.RUnlock()
|
454
|
|
-
|
455
|
511
|
// for STATUSMSG
|
456
|
512
|
var minPrefixMode Mode
|
457
|
513
|
if minPrefix != nil {
|
458
|
514
|
minPrefixMode = *minPrefix
|
459
|
515
|
}
|
460
|
|
- for member := range channel.members {
|
|
516
|
+ for _, member := range channel.Members() {
|
461
|
517
|
if minPrefix != nil && !channel.ClientIsAtLeast(member, minPrefixMode) {
|
462
|
518
|
// STATUSMSG
|
463
|
519
|
continue
|
|
@@ -505,15 +561,12 @@ func (channel *Channel) sendSplitMessage(msgid, cmd string, minPrefix *Mode, cli
|
505
|
561
|
return
|
506
|
562
|
}
|
507
|
563
|
|
508
|
|
- channel.membersMutex.RLock()
|
509
|
|
- defer channel.membersMutex.RUnlock()
|
510
|
|
-
|
511
|
564
|
// for STATUSMSG
|
512
|
565
|
var minPrefixMode Mode
|
513
|
566
|
if minPrefix != nil {
|
514
|
567
|
minPrefixMode = *minPrefix
|
515
|
568
|
}
|
516
|
|
- for member := range channel.members {
|
|
569
|
+ for _, member := range channel.Members() {
|
517
|
570
|
if minPrefix != nil && !channel.ClientIsAtLeast(member, minPrefixMode) {
|
518
|
571
|
// STATUSMSG
|
519
|
572
|
continue
|
|
@@ -534,35 +587,8 @@ func (channel *Channel) sendSplitMessage(msgid, cmd string, minPrefix *Mode, cli
|
534
|
587
|
}
|
535
|
588
|
}
|
536
|
589
|
|
537
|
|
-func (channel *Channel) applyModeFlag(client *Client, mode Mode,
|
538
|
|
- op ModeOp) bool {
|
539
|
|
- if !channel.ClientIsAtLeast(client, ChannelOperator) {
|
540
|
|
- client.Send(nil, client.server.name, ERR_CHANOPRIVSNEEDED, channel.name, "You're not a channel operator")
|
541
|
|
- return false
|
542
|
|
- }
|
543
|
|
-
|
544
|
|
- switch op {
|
545
|
|
- case Add:
|
546
|
|
- if channel.flags[mode] {
|
547
|
|
- return false
|
548
|
|
- }
|
549
|
|
- channel.flags[mode] = true
|
550
|
|
- return true
|
551
|
|
-
|
552
|
|
- case Remove:
|
553
|
|
- if !channel.flags[mode] {
|
554
|
|
- return false
|
555
|
|
- }
|
556
|
|
- delete(channel.flags, mode)
|
557
|
|
- return true
|
558
|
|
- }
|
559
|
|
- return false
|
560
|
|
-}
|
561
|
|
-
|
562
|
590
|
func (channel *Channel) applyModeMemberNoMutex(client *Client, mode Mode,
|
563
|
591
|
op ModeOp, nick string) *ModeChange {
|
564
|
|
- // requires Lock()
|
565
|
|
-
|
566
|
592
|
if nick == "" {
|
567
|
593
|
//TODO(dan): shouldn't this be handled before it reaches this function?
|
568
|
594
|
client.Send(nil, client.server.name, ERR_NEEDMOREPARAMS, "MODE", "Not enough parameters")
|
|
@@ -576,35 +602,28 @@ func (channel *Channel) applyModeMemberNoMutex(client *Client, mode Mode,
|
576
|
602
|
return nil
|
577
|
603
|
}
|
578
|
604
|
|
579
|
|
- if !channel.members.Has(target) {
|
580
|
|
- client.Send(nil, client.server.name, ERR_USERNOTINCHANNEL, client.nick, channel.name, "They aren't on that channel")
|
581
|
|
- return nil
|
|
605
|
+ channel.stateMutex.Lock()
|
|
606
|
+ modeset, exists := channel.members[target]
|
|
607
|
+ var already bool
|
|
608
|
+ if exists {
|
|
609
|
+ enable := op == Add
|
|
610
|
+ already = modeset[mode] == enable
|
|
611
|
+ modeset[mode] = enable
|
582
|
612
|
}
|
|
613
|
+ channel.stateMutex.Unlock()
|
583
|
614
|
|
584
|
|
- switch op {
|
585
|
|
- case Add:
|
586
|
|
- if channel.members[target][mode] {
|
587
|
|
- return nil
|
588
|
|
- }
|
589
|
|
- channel.members[target][mode] = true
|
590
|
|
- return &ModeChange{
|
591
|
|
- op: Add,
|
592
|
|
- mode: mode,
|
593
|
|
- arg: nick,
|
594
|
|
- }
|
595
|
|
-
|
596
|
|
- case Remove:
|
597
|
|
- if !channel.members[target][mode] {
|
598
|
|
- return nil
|
599
|
|
- }
|
600
|
|
- channel.members[target][mode] = false
|
|
615
|
+ if !exists {
|
|
616
|
+ client.Send(nil, client.server.name, ERR_USERNOTINCHANNEL, client.nick, channel.name, "They aren't on that channel")
|
|
617
|
+ return nil
|
|
618
|
+ } else if already {
|
|
619
|
+ return nil
|
|
620
|
+ } else {
|
601
|
621
|
return &ModeChange{
|
602
|
|
- op: Remove,
|
|
622
|
+ op: op,
|
603
|
623
|
mode: mode,
|
604
|
624
|
arg: nick,
|
605
|
625
|
}
|
606
|
626
|
}
|
607
|
|
- return nil
|
608
|
627
|
}
|
609
|
628
|
|
610
|
629
|
// ShowMaskList shows the given list to the client.
|
|
@@ -622,11 +641,15 @@ func (channel *Channel) ShowMaskList(client *Client, mode Mode) {
|
622
|
641
|
rplendoflist = RPL_ENDOFINVITELIST
|
623
|
642
|
}
|
624
|
643
|
|
625
|
|
- // send out responses
|
|
644
|
+ nick := client.getNick()
|
|
645
|
+ channel.stateMutex.RLock()
|
|
646
|
+ // XXX don't acquire any new locks in this section, besides Socket.Write
|
626
|
647
|
for mask := range channel.lists[mode].masks {
|
627
|
|
- client.Send(nil, client.server.name, rpllist, client.nick, channel.name, mask)
|
|
648
|
+ client.Send(nil, client.server.name, rpllist, nick, channel.name, mask)
|
628
|
649
|
}
|
629
|
|
- client.Send(nil, client.server.name, rplendoflist, client.nick, channel.name, "End of list")
|
|
650
|
+ channel.stateMutex.RUnlock()
|
|
651
|
+
|
|
652
|
+ client.Send(nil, client.server.name, rplendoflist, nick, channel.name, "End of list")
|
630
|
653
|
}
|
631
|
654
|
|
632
|
655
|
func (channel *Channel) applyModeMask(client *Client, mode Mode, op ModeOp, mask string) bool {
|
|
@@ -657,51 +680,52 @@ func (channel *Channel) applyModeMask(client *Client, mode Mode, op ModeOp, mask
|
657
|
680
|
return false
|
658
|
681
|
}
|
659
|
682
|
|
660
|
|
-// Quit removes the given client from the channel, and also updates friends with the latest client list.
|
661
|
|
-func (channel *Channel) Quit(client *Client, friends *ClientSet) {
|
662
|
|
- channel.membersMutex.Lock()
|
663
|
|
- defer channel.membersMutex.Unlock()
|
664
|
|
-
|
665
|
|
- channel.quitNoMutex(client)
|
666
|
|
-
|
667
|
|
- for friend := range channel.members {
|
668
|
|
- friends.Add(friend)
|
669
|
|
- }
|
670
|
|
-}
|
671
|
|
-
|
672
|
|
-func (channel *Channel) quitNoMutex(client *Client) {
|
|
683
|
+// Quit removes the given client from the channel
|
|
684
|
+func (channel *Channel) Quit(client *Client) {
|
|
685
|
+ channel.stateMutex.Lock()
|
673
|
686
|
channel.members.Remove(client)
|
674
|
|
- client.channels.Remove(channel)
|
|
687
|
+ empty := len(channel.members) == 0
|
|
688
|
+ channel.stateMutex.Unlock()
|
|
689
|
+ channel.regenerateMembersCache()
|
|
690
|
+
|
|
691
|
+ client.removeChannel(channel)
|
675
|
692
|
|
676
|
|
- if channel.isEmptyNoMutex() {
|
|
693
|
+ //TODO(slingamn) fold this operation into a channelmanager type
|
|
694
|
+ if empty {
|
677
|
695
|
channel.server.channels.Remove(channel)
|
678
|
696
|
}
|
679
|
697
|
}
|
680
|
698
|
|
681
|
|
-func (channel *Channel) kickNoMutex(client *Client, target *Client, comment string) {
|
682
|
|
- // needs a Lock()
|
683
|
|
-
|
684
|
|
- if !(client.flags[Operator] || channel.members.Has(client)) {
|
|
699
|
+func (channel *Channel) Kick(client *Client, target *Client, comment string) {
|
|
700
|
+ if !(client.flags[Operator] || channel.hasClient(client)) {
|
685
|
701
|
client.Send(nil, client.server.name, ERR_NOTONCHANNEL, channel.name, "You're not on that channel")
|
686
|
702
|
return
|
687
|
703
|
}
|
688
|
|
- if !channel.clientIsAtLeastNoMutex(client, ChannelOperator) {
|
|
704
|
+ if !channel.ClientIsAtLeast(client, ChannelOperator) {
|
689
|
705
|
client.Send(nil, client.server.name, ERR_CANNOTSENDTOCHAN, channel.name, "Cannot send to channel")
|
690
|
706
|
return
|
691
|
707
|
}
|
692
|
|
- if !channel.members.Has(target) {
|
|
708
|
+ if !channel.hasClient(target) {
|
693
|
709
|
client.Send(nil, client.server.name, ERR_USERNOTINCHANNEL, client.nick, channel.name, "They aren't on that channel")
|
694
|
710
|
return
|
695
|
711
|
}
|
|
712
|
+ if !channel.ClientHasPrivsOver(client, target) {
|
|
713
|
+ client.Send(nil, client.server.name, ERR_CHANOPRIVSNEEDED, channel.name, "You're not a channel operator")
|
|
714
|
+ return
|
|
715
|
+ }
|
696
|
716
|
|
697
|
|
- if len(comment) > client.server.limits.KickLen {
|
698
|
|
- comment = comment[:client.server.limits.KickLen]
|
|
717
|
+ kicklimit := client.server.getLimits().KickLen
|
|
718
|
+ if len(comment) > kicklimit {
|
|
719
|
+ comment = comment[:kicklimit]
|
699
|
720
|
}
|
700
|
721
|
|
701
|
|
- for member := range channel.members {
|
702
|
|
- member.Send(nil, client.nickMaskString, "KICK", channel.name, target.nick, comment)
|
|
722
|
+ clientMask := client.getNickMaskString()
|
|
723
|
+ targetNick := target.getNick()
|
|
724
|
+ for _, member := range channel.Members() {
|
|
725
|
+ member.Send(nil, clientMask, "KICK", channel.name, targetNick, comment)
|
703
|
726
|
}
|
704
|
|
- channel.quitNoMutex(target)
|
|
727
|
+
|
|
728
|
+ channel.Quit(target)
|
705
|
729
|
}
|
706
|
730
|
|
707
|
731
|
// Invite invites the given client to the channel, if the inviter can do so.
|
|
@@ -711,23 +735,22 @@ func (channel *Channel) Invite(invitee *Client, inviter *Client) {
|
711
|
735
|
return
|
712
|
736
|
}
|
713
|
737
|
|
714
|
|
- channel.membersMutex.RLock()
|
715
|
|
- defer channel.membersMutex.RUnlock()
|
716
|
|
-
|
717
|
|
- if !channel.members.Has(inviter) {
|
|
738
|
+ if !channel.hasClient(inviter) {
|
718
|
739
|
inviter.Send(nil, inviter.server.name, ERR_NOTONCHANNEL, channel.name, "You're not on that channel")
|
719
|
740
|
return
|
720
|
741
|
}
|
721
|
742
|
|
722
|
743
|
//TODO(dan): handle this more nicely, keep a list of last X invited channels on invitee rather than explicitly modifying the invite list?
|
723
|
744
|
if channel.flags[InviteOnly] {
|
724
|
|
- channel.lists[InviteMask].Add(invitee.nickMaskCasefolded)
|
|
745
|
+ nmc := invitee.getNickCasefolded()
|
|
746
|
+ channel.stateMutex.Lock()
|
|
747
|
+ channel.lists[InviteMask].Add(nmc)
|
|
748
|
+ channel.stateMutex.Unlock()
|
725
|
749
|
}
|
726
|
750
|
|
727
|
|
- // send invite-notify
|
728
|
|
- for member := range channel.members {
|
|
751
|
+ for _, member := range channel.Members() {
|
729
|
752
|
if member.capabilities.Has(caps.InviteNotify) && member != inviter && member != invitee && channel.ClientIsAtLeast(member, Halfop) {
|
730
|
|
- member.Send(nil, inviter.nickMaskString, "INVITE", invitee.nick, channel.name)
|
|
753
|
+ member.Send(nil, inviter.getNickMaskString(), "INVITE", invitee.getNick(), channel.name)
|
731
|
754
|
}
|
732
|
755
|
}
|
733
|
756
|
|