|
@@ -34,7 +34,6 @@ type Channel struct {
|
34
|
34
|
key string
|
35
|
35
|
forward string
|
36
|
36
|
members MemberSet
|
37
|
|
- membersCache []*Client // allow iteration over channel members without holding the lock
|
38
|
37
|
name string
|
39
|
38
|
nameCasefolded string
|
40
|
39
|
server *Server
|
|
@@ -54,6 +53,9 @@ type Channel struct {
|
54
|
53
|
dirtyBits uint
|
55
|
54
|
settings ChannelSettings
|
56
|
55
|
uuid utils.UUID
|
|
56
|
+ // these caches are paired to allow iteration over channel members without holding the lock
|
|
57
|
+ membersCache []*Client
|
|
58
|
+ memberDataCache []*memberData
|
57
|
59
|
}
|
58
|
60
|
|
59
|
61
|
// NewChannel creates a new channel from a `Server` and a `name`
|
|
@@ -421,16 +423,19 @@ func (channel *Channel) AcceptTransfer(client *Client) (err error) {
|
421
|
423
|
|
422
|
424
|
func (channel *Channel) regenerateMembersCache() {
|
423
|
425
|
channel.stateMutex.RLock()
|
424
|
|
- result := make([]*Client, len(channel.members))
|
|
426
|
+ membersCache := make([]*Client, len(channel.members))
|
|
427
|
+ dataCache := make([]*memberData, len(channel.members))
|
425
|
428
|
i := 0
|
426
|
|
- for client := range channel.members {
|
427
|
|
- result[i] = client
|
|
429
|
+ for client, info := range channel.members {
|
|
430
|
+ membersCache[i] = client
|
|
431
|
+ dataCache[i] = info
|
428
|
432
|
i++
|
429
|
433
|
}
|
430
|
434
|
channel.stateMutex.RUnlock()
|
431
|
435
|
|
432
|
436
|
channel.stateMutex.Lock()
|
433
|
|
- channel.membersCache = result
|
|
437
|
+ channel.membersCache = membersCache
|
|
438
|
+ channel.memberDataCache = dataCache
|
434
|
439
|
channel.stateMutex.Unlock()
|
435
|
440
|
}
|
436
|
441
|
|
|
@@ -438,6 +443,8 @@ func (channel *Channel) regenerateMembersCache() {
|
438
|
443
|
func (channel *Channel) Names(client *Client, rb *ResponseBuffer) {
|
439
|
444
|
channel.stateMutex.RLock()
|
440
|
445
|
clientData, isJoined := channel.members[client]
|
|
446
|
+ chname := channel.name
|
|
447
|
+ membersCache, memberDataCache := channel.membersCache, channel.memberDataCache
|
441
|
448
|
channel.stateMutex.RUnlock()
|
442
|
449
|
isOper := client.HasRoleCapabs("sajoin")
|
443
|
450
|
respectAuditorium := channel.flags.HasMode(modes.Auditorium) && !isOper &&
|
|
@@ -445,52 +452,32 @@ func (channel *Channel) Names(client *Client, rb *ResponseBuffer) {
|
445
|
452
|
isMultiPrefix := rb.session.capabilities.Has(caps.MultiPrefix)
|
446
|
453
|
isUserhostInNames := rb.session.capabilities.Has(caps.UserhostInNames)
|
447
|
454
|
|
448
|
|
- maxNamLen := 480 - len(client.server.name) - len(client.Nick())
|
449
|
|
- var namesLines []string
|
450
|
|
- var buffer strings.Builder
|
|
455
|
+ maxNamLen := 480 - len(client.server.name) - len(client.Nick()) - len(chname)
|
|
456
|
+ var tl utils.TokenLineBuilder
|
|
457
|
+ tl.Initialize(maxNamLen, " ")
|
451
|
458
|
if isJoined || !channel.flags.HasMode(modes.Secret) || isOper {
|
452
|
|
- for _, target := range channel.Members() {
|
|
459
|
+ for i, target := range membersCache {
|
|
460
|
+ if !isJoined && target.HasMode(modes.Invisible) && !isOper {
|
|
461
|
+ continue
|
|
462
|
+ }
|
453
|
463
|
var nick string
|
454
|
464
|
if isUserhostInNames {
|
455
|
465
|
nick = target.NickMaskString()
|
456
|
466
|
} else {
|
457
|
467
|
nick = target.Nick()
|
458
|
468
|
}
|
459
|
|
- channel.stateMutex.RLock()
|
460
|
|
- memberData, _ := channel.members[target]
|
461
|
|
- channel.stateMutex.RUnlock()
|
462
|
|
- modeSet := memberData.modes
|
463
|
|
- if modeSet == nil {
|
464
|
|
- continue
|
465
|
|
- }
|
466
|
|
- if !isJoined && target.HasMode(modes.Invisible) && !isOper {
|
467
|
|
- continue
|
468
|
|
- }
|
469
|
|
- if respectAuditorium && modeSet.HighestChannelUserMode() == modes.Mode(0) {
|
|
469
|
+ memberData := memberDataCache[i]
|
|
470
|
+ if respectAuditorium && memberData.modes.HighestChannelUserMode() == modes.Mode(0) {
|
470
|
471
|
continue
|
471
|
472
|
}
|
472
|
|
- prefix := modeSet.Prefixes(isMultiPrefix)
|
473
|
|
- if buffer.Len()+len(nick)+len(prefix)+1 > maxNamLen {
|
474
|
|
- namesLines = append(namesLines, buffer.String())
|
475
|
|
- buffer.Reset()
|
476
|
|
- }
|
477
|
|
- if buffer.Len() > 0 {
|
478
|
|
- buffer.WriteString(" ")
|
479
|
|
- }
|
480
|
|
- buffer.WriteString(prefix)
|
481
|
|
- buffer.WriteString(nick)
|
482
|
|
- }
|
483
|
|
- if buffer.Len() > 0 {
|
484
|
|
- namesLines = append(namesLines, buffer.String())
|
|
473
|
+ tl.AddParts(memberData.modes.Prefixes(isMultiPrefix), nick)
|
485
|
474
|
}
|
486
|
475
|
}
|
487
|
476
|
|
488
|
|
- for _, line := range namesLines {
|
489
|
|
- if buffer.Len() > 0 {
|
490
|
|
- rb.Add(nil, client.server.name, RPL_NAMREPLY, client.nick, "=", channel.name, line)
|
491
|
|
- }
|
|
477
|
+ for _, line := range tl.Lines() {
|
|
478
|
+ rb.Add(nil, client.server.name, RPL_NAMREPLY, client.nick, "=", chname, line)
|
492
|
479
|
}
|
493
|
|
- rb.Add(nil, client.server.name, RPL_ENDOFNAMES, client.nick, channel.name, client.t("End of NAMES list"))
|
|
480
|
+ rb.Add(nil, client.server.name, RPL_ENDOFNAMES, client.nick, chname, client.t("End of NAMES list"))
|
494
|
481
|
}
|
495
|
482
|
|
496
|
483
|
// does `clientMode` give you privileges to grant/remove `targetMode` to/from people,
|
|
@@ -514,7 +501,7 @@ func channelUserModeHasPrivsOver(clientMode modes.Mode, targetMode modes.Mode) b
|
514
|
501
|
// ClientIsAtLeast returns whether the client has at least the given channel privilege.
|
515
|
502
|
func (channel *Channel) ClientIsAtLeast(client *Client, permission modes.Mode) bool {
|
516
|
503
|
channel.stateMutex.RLock()
|
517
|
|
- memberData := channel.members[client]
|
|
504
|
+ memberData, present := channel.members[client]
|
518
|
505
|
founder := channel.registeredFounder
|
519
|
506
|
channel.stateMutex.RUnlock()
|
520
|
507
|
|
|
@@ -522,6 +509,10 @@ func (channel *Channel) ClientIsAtLeast(client *Client, permission modes.Mode) b
|
522
|
509
|
return true
|
523
|
510
|
}
|
524
|
511
|
|
|
512
|
+ if !present {
|
|
513
|
+ return false
|
|
514
|
+ }
|
|
515
|
+
|
525
|
516
|
for _, mode := range modes.ChannelUserModes {
|
526
|
517
|
if memberData.modes.HasMode(mode) {
|
527
|
518
|
return true
|
|
@@ -571,20 +562,20 @@ func (channel *Channel) setMemberStatus(client *Client, status alwaysOnChannelSt
|
571
|
562
|
}
|
572
|
563
|
channel.stateMutex.Lock()
|
573
|
564
|
defer channel.stateMutex.Unlock()
|
574
|
|
- if _, ok := channel.members[client]; !ok {
|
575
|
|
- return
|
|
565
|
+ if mData, ok := channel.members[client]; ok {
|
|
566
|
+ mData.modes.Clear()
|
|
567
|
+ for _, mode := range status.Modes {
|
|
568
|
+ mData.modes.SetMode(modes.Mode(mode), true)
|
|
569
|
+ }
|
|
570
|
+ mData.joinTime = status.JoinTime
|
576
|
571
|
}
|
577
|
|
- memberData := channel.members[client]
|
578
|
|
- memberData.modes = newModes
|
579
|
|
- memberData.joinTime = status.JoinTime
|
580
|
|
- channel.members[client] = memberData
|
581
|
572
|
}
|
582
|
573
|
|
583
|
574
|
func (channel *Channel) ClientHasPrivsOver(client *Client, target *Client) bool {
|
584
|
575
|
channel.stateMutex.RLock()
|
585
|
576
|
founder := channel.registeredFounder
|
586
|
|
- clientModes := channel.members[client].modes
|
587
|
|
- targetModes := channel.members[target].modes
|
|
577
|
+ clientData, clientOK := channel.members[client]
|
|
578
|
+ targetData, targetOK := channel.members[target]
|
588
|
579
|
channel.stateMutex.RUnlock()
|
589
|
580
|
|
590
|
581
|
if founder != "" {
|
|
@@ -595,7 +586,11 @@ func (channel *Channel) ClientHasPrivsOver(client *Client, target *Client) bool
|
595
|
586
|
}
|
596
|
587
|
}
|
597
|
588
|
|
598
|
|
- return channelUserModeHasPrivsOver(clientModes.HighestChannelUserMode(), targetModes.HighestChannelUserMode())
|
|
589
|
+ return clientOK && targetOK &&
|
|
590
|
+ channelUserModeHasPrivsOver(
|
|
591
|
+ clientData.modes.HighestChannelUserMode(),
|
|
592
|
+ targetData.modes.HighestChannelUserMode(),
|
|
593
|
+ )
|
599
|
594
|
}
|
600
|
595
|
|
601
|
596
|
func (channel *Channel) hasClient(client *Client) bool {
|
|
@@ -1319,9 +1314,9 @@ func (channel *Channel) SendSplitMessage(command string, minPrefixMode modes.Mod
|
1319
|
1314
|
|
1320
|
1315
|
if channel.flags.HasMode(modes.OpModerated) {
|
1321
|
1316
|
channel.stateMutex.RLock()
|
1322
|
|
- cuData := channel.members[client]
|
|
1317
|
+ cuData, ok := channel.members[client]
|
1323
|
1318
|
channel.stateMutex.RUnlock()
|
1324
|
|
- if cuData.modes.HighestChannelUserMode() == modes.Mode(0) {
|
|
1319
|
+ if !ok || cuData.modes.HighestChannelUserMode() == modes.Mode(0) {
|
1325
|
1320
|
// max(statusmsg_minmode, halfop)
|
1326
|
1321
|
if minPrefixMode == modes.Mode(0) || minPrefixMode == modes.Voice {
|
1327
|
1322
|
minPrefixMode = modes.Halfop
|
|
@@ -1490,6 +1485,7 @@ func (channel *Channel) Purge(source string) {
|
1490
|
1485
|
chname := channel.name
|
1491
|
1486
|
members := channel.membersCache
|
1492
|
1487
|
channel.membersCache = nil
|
|
1488
|
+ channel.memberDataCache = nil
|
1493
|
1489
|
channel.members = make(MemberSet)
|
1494
|
1490
|
// TODO try to prevent Purge racing against (pending) Join?
|
1495
|
1491
|
channel.stateMutex.Unlock()
|