|
@@ -20,7 +20,8 @@ import (
|
20
|
20
|
)
|
21
|
21
|
|
22
|
22
|
type ChannelSettings struct {
|
23
|
|
- History HistoryStatus
|
|
23
|
+ History HistoryStatus
|
|
24
|
+ QueryCutoff HistoryCutoff
|
24
|
25
|
}
|
25
|
26
|
|
26
|
27
|
// Channel represents a channel that clients can join.
|
|
@@ -109,7 +110,7 @@ func (channel *Channel) IsLoaded() bool {
|
109
|
110
|
}
|
110
|
111
|
|
111
|
112
|
func (channel *Channel) resizeHistory(config *Config) {
|
112
|
|
- status, _ := channel.historyStatus(config)
|
|
113
|
+ status, _, _ := channel.historyStatus(config)
|
113
|
114
|
if status == HistoryEphemeral {
|
114
|
115
|
channel.history.Resize(config.History.ChannelLength, time.Duration(config.History.AutoresizeWindow))
|
115
|
116
|
} else {
|
|
@@ -443,11 +444,11 @@ func (channel *Channel) regenerateMembersCache() {
|
443
|
444
|
// Names sends the list of users joined to the channel to the given client.
|
444
|
445
|
func (channel *Channel) Names(client *Client, rb *ResponseBuffer) {
|
445
|
446
|
channel.stateMutex.RLock()
|
446
|
|
- clientModes, isJoined := channel.members[client]
|
|
447
|
+ clientData, isJoined := channel.members[client]
|
447
|
448
|
channel.stateMutex.RUnlock()
|
448
|
449
|
isOper := client.HasMode(modes.Operator)
|
449
|
450
|
respectAuditorium := channel.flags.HasMode(modes.Auditorium) && !isOper &&
|
450
|
|
- (!isJoined || clientModes.HighestChannelUserMode() == modes.Mode(0))
|
|
451
|
+ (!isJoined || clientData.modes.HighestChannelUserMode() == modes.Mode(0))
|
451
|
452
|
isMultiPrefix := rb.session.capabilities.Has(caps.MultiPrefix)
|
452
|
453
|
isUserhostInNames := rb.session.capabilities.Has(caps.UserhostInNames)
|
453
|
454
|
|
|
@@ -463,8 +464,9 @@ func (channel *Channel) Names(client *Client, rb *ResponseBuffer) {
|
463
|
464
|
nick = target.Nick()
|
464
|
465
|
}
|
465
|
466
|
channel.stateMutex.RLock()
|
466
|
|
- modeSet := channel.members[target]
|
|
467
|
+ memberData, _ := channel.members[target]
|
467
|
468
|
channel.stateMutex.RUnlock()
|
|
469
|
+ modeSet := memberData.modes
|
468
|
470
|
if modeSet == nil {
|
469
|
471
|
continue
|
470
|
472
|
}
|
|
@@ -519,7 +521,7 @@ func channelUserModeHasPrivsOver(clientMode modes.Mode, targetMode modes.Mode) b
|
519
|
521
|
// ClientIsAtLeast returns whether the client has at least the given channel privilege.
|
520
|
522
|
func (channel *Channel) ClientIsAtLeast(client *Client, permission modes.Mode) bool {
|
521
|
523
|
channel.stateMutex.RLock()
|
522
|
|
- clientModes := channel.members[client]
|
|
524
|
+ memberData := channel.members[client]
|
523
|
525
|
founder := channel.registeredFounder
|
524
|
526
|
channel.stateMutex.RUnlock()
|
525
|
527
|
|
|
@@ -528,7 +530,7 @@ func (channel *Channel) ClientIsAtLeast(client *Client, permission modes.Mode) b
|
528
|
530
|
}
|
529
|
531
|
|
530
|
532
|
for _, mode := range modes.ChannelUserModes {
|
531
|
|
- if clientModes.HasMode(mode) {
|
|
533
|
+ if memberData.modes.HasMode(mode) {
|
532
|
534
|
return true
|
533
|
535
|
}
|
534
|
536
|
if mode == permission {
|
|
@@ -541,35 +543,37 @@ func (channel *Channel) ClientIsAtLeast(client *Client, permission modes.Mode) b
|
541
|
543
|
func (channel *Channel) ClientPrefixes(client *Client, isMultiPrefix bool) string {
|
542
|
544
|
channel.stateMutex.RLock()
|
543
|
545
|
defer channel.stateMutex.RUnlock()
|
544
|
|
- modes, present := channel.members[client]
|
|
546
|
+ memberData, present := channel.members[client]
|
545
|
547
|
if !present {
|
546
|
548
|
return ""
|
547
|
549
|
} else {
|
548
|
|
- return modes.Prefixes(isMultiPrefix)
|
|
550
|
+ return memberData.modes.Prefixes(isMultiPrefix)
|
549
|
551
|
}
|
550
|
552
|
}
|
551
|
553
|
|
552
|
|
-func (channel *Channel) ClientStatus(client *Client) (present bool, cModes modes.Modes) {
|
|
554
|
+func (channel *Channel) ClientStatus(client *Client) (present bool, joinTimeSecs int64, cModes modes.Modes) {
|
553
|
555
|
channel.stateMutex.RLock()
|
554
|
556
|
defer channel.stateMutex.RUnlock()
|
555
|
|
- modes, present := channel.members[client]
|
556
|
|
- return present, modes.AllModes()
|
|
557
|
+ memberData, present := channel.members[client]
|
|
558
|
+ return present, time.Unix(0, memberData.joinTime).Unix(), memberData.modes.AllModes()
|
557
|
559
|
}
|
558
|
560
|
|
559
|
561
|
// helper for persisting channel-user modes for always-on clients;
|
560
|
562
|
// return the channel name and all channel-user modes for a client
|
561
|
|
-func (channel *Channel) nameAndModes(client *Client) (chname string, modeStr string) {
|
|
563
|
+func (channel *Channel) alwaysOnStatus(client *Client) (chname string, status alwaysOnChannelStatus) {
|
562
|
564
|
channel.stateMutex.RLock()
|
563
|
565
|
defer channel.stateMutex.RUnlock()
|
564
|
566
|
chname = channel.name
|
565
|
|
- modeStr = channel.members[client].String()
|
|
567
|
+ data := channel.members[client]
|
|
568
|
+ status.Modes = data.modes.String()
|
|
569
|
+ status.JoinTime = data.joinTime
|
566
|
570
|
return
|
567
|
571
|
}
|
568
|
572
|
|
569
|
573
|
// overwrite any existing channel-user modes with the stored ones
|
570
|
|
-func (channel *Channel) setModesForClient(client *Client, modeStr string) {
|
|
574
|
+func (channel *Channel) setMemberStatus(client *Client, status alwaysOnChannelStatus) {
|
571
|
575
|
newModes := modes.NewModeSet()
|
572
|
|
- for _, mode := range modeStr {
|
|
576
|
+ for _, mode := range status.Modes {
|
573
|
577
|
newModes.SetMode(modes.Mode(mode), true)
|
574
|
578
|
}
|
575
|
579
|
channel.stateMutex.Lock()
|
|
@@ -577,14 +581,17 @@ func (channel *Channel) setModesForClient(client *Client, modeStr string) {
|
577
|
581
|
if _, ok := channel.members[client]; !ok {
|
578
|
582
|
return
|
579
|
583
|
}
|
580
|
|
- channel.members[client] = newModes
|
|
584
|
+ memberData := channel.members[client]
|
|
585
|
+ memberData.modes = newModes
|
|
586
|
+ memberData.joinTime = status.JoinTime
|
|
587
|
+ channel.members[client] = memberData
|
581
|
588
|
}
|
582
|
589
|
|
583
|
590
|
func (channel *Channel) ClientHasPrivsOver(client *Client, target *Client) bool {
|
584
|
591
|
channel.stateMutex.RLock()
|
585
|
592
|
founder := channel.registeredFounder
|
586
|
|
- clientModes := channel.members[client]
|
587
|
|
- targetModes := channel.members[target]
|
|
593
|
+ clientModes := channel.members[client].modes
|
|
594
|
+ targetModes := channel.members[target].modes
|
588
|
595
|
channel.stateMutex.RUnlock()
|
589
|
596
|
|
590
|
597
|
if founder != "" {
|
|
@@ -612,7 +619,7 @@ func (channel *Channel) modeStrings(client *Client) (result []string) {
|
612
|
619
|
channel.stateMutex.RLock()
|
613
|
620
|
defer channel.stateMutex.RUnlock()
|
614
|
621
|
|
615
|
|
- isMember := hasPrivs || channel.members[client] != nil
|
|
622
|
+ isMember := hasPrivs || channel.members.Has(client)
|
616
|
623
|
showKey := isMember && (channel.key != "")
|
617
|
624
|
showUserLimit := channel.userLimit > 0
|
618
|
625
|
showForward := channel.forward != ""
|
|
@@ -660,18 +667,38 @@ func (channel *Channel) IsEmpty() bool {
|
660
|
667
|
|
661
|
668
|
// figure out where history is being stored: persistent, ephemeral, or neither
|
662
|
669
|
// target is only needed if we're doing persistent history
|
663
|
|
-func (channel *Channel) historyStatus(config *Config) (status HistoryStatus, target string) {
|
|
670
|
+func (channel *Channel) historyStatus(config *Config) (status HistoryStatus, target string, restrictions HistoryCutoff) {
|
664
|
671
|
if !config.History.Enabled {
|
665
|
|
- return HistoryDisabled, ""
|
|
672
|
+ return HistoryDisabled, "", HistoryCutoffNone
|
666
|
673
|
}
|
667
|
674
|
|
668
|
675
|
channel.stateMutex.RLock()
|
669
|
676
|
target = channel.nameCasefolded
|
670
|
|
- historyStatus := channel.settings.History
|
|
677
|
+ settings := channel.settings
|
671
|
678
|
registered := channel.registeredFounder != ""
|
672
|
679
|
channel.stateMutex.RUnlock()
|
673
|
680
|
|
674
|
|
- return channelHistoryStatus(config, registered, historyStatus), target
|
|
681
|
+ restrictions = settings.QueryCutoff
|
|
682
|
+ if restrictions == HistoryCutoffDefault {
|
|
683
|
+ restrictions = config.History.Restrictions.queryCutoff
|
|
684
|
+ }
|
|
685
|
+
|
|
686
|
+ return channelHistoryStatus(config, registered, settings.History), target, restrictions
|
|
687
|
+}
|
|
688
|
+
|
|
689
|
+func (channel *Channel) joinTimeCutoff(client *Client) (present bool, cutoff time.Time) {
|
|
690
|
+ account := client.Account()
|
|
691
|
+
|
|
692
|
+ channel.stateMutex.RLock()
|
|
693
|
+ defer channel.stateMutex.RUnlock()
|
|
694
|
+ if data, ok := channel.members[client]; ok {
|
|
695
|
+ present = true
|
|
696
|
+ // report a cutoff of zero, i.e., no restriction, if the user is privileged
|
|
697
|
+ if !((account != "" && account == channel.registeredFounder) || data.modes.HasMode(modes.ChannelFounder) || data.modes.HasMode(modes.ChannelAdmin) || data.modes.HasMode(modes.ChannelOperator)) {
|
|
698
|
+ cutoff = time.Unix(0, data.joinTime)
|
|
699
|
+ }
|
|
700
|
+ }
|
|
701
|
+ return
|
675
|
702
|
}
|
676
|
703
|
|
677
|
704
|
func channelHistoryStatus(config *Config, registered bool, storedStatus HistoryStatus) (result HistoryStatus) {
|
|
@@ -697,7 +724,7 @@ func (channel *Channel) AddHistoryItem(item history.Item, account string) (err e
|
697
|
724
|
return
|
698
|
725
|
}
|
699
|
726
|
|
700
|
|
- status, target := channel.historyStatus(channel.server.Config())
|
|
727
|
+ status, target, _ := channel.historyStatus(channel.server.Config())
|
701
|
728
|
if status == HistoryPersistent {
|
702
|
729
|
err = channel.server.historyDB.AddChannelItem(target, item, account)
|
703
|
730
|
} else if status == HistoryEphemeral {
|
|
@@ -785,7 +812,7 @@ func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *Resp
|
785
|
812
|
givenMode = persistentMode
|
786
|
813
|
}
|
787
|
814
|
if givenMode != 0 {
|
788
|
|
- channel.members[client].SetMode(givenMode, true)
|
|
815
|
+ channel.members[client].modes.SetMode(givenMode, true)
|
789
|
816
|
}
|
790
|
817
|
}()
|
791
|
818
|
|
|
@@ -825,9 +852,9 @@ func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *Resp
|
825
|
852
|
for _, member := range channel.Members() {
|
826
|
853
|
if respectAuditorium {
|
827
|
854
|
channel.stateMutex.RLock()
|
828
|
|
- memberModes, ok := channel.members[member]
|
|
855
|
+ memberData, ok := channel.members[member]
|
829
|
856
|
channel.stateMutex.RUnlock()
|
830
|
|
- if !ok || memberModes.HighestChannelUserMode() == modes.Mode(0) {
|
|
857
|
+ if !ok || memberData.modes.HighestChannelUserMode() == modes.Mode(0) {
|
831
|
858
|
continue
|
832
|
859
|
}
|
833
|
860
|
}
|
|
@@ -955,7 +982,7 @@ func (channel *Channel) playJoinForSession(session *Session) {
|
955
|
982
|
func (channel *Channel) Part(client *Client, message string, rb *ResponseBuffer) {
|
956
|
983
|
channel.stateMutex.RLock()
|
957
|
984
|
chname := channel.name
|
958
|
|
- clientModes, ok := channel.members[client]
|
|
985
|
+ clientData, ok := channel.members[client]
|
959
|
986
|
channel.stateMutex.RUnlock()
|
960
|
987
|
|
961
|
988
|
if !ok {
|
|
@@ -974,15 +1001,15 @@ func (channel *Channel) Part(client *Client, message string, rb *ResponseBuffer)
|
974
|
1001
|
params = append(params, message)
|
975
|
1002
|
}
|
976
|
1003
|
respectAuditorium := channel.flags.HasMode(modes.Auditorium) &&
|
977
|
|
- clientModes.HighestChannelUserMode() == modes.Mode(0)
|
|
1004
|
+ clientData.modes.HighestChannelUserMode() == modes.Mode(0)
|
978
|
1005
|
var cache MessageCache
|
979
|
1006
|
cache.Initialize(channel.server, splitMessage.Time, splitMessage.Msgid, details.nickMask, details.accountName, nil, "PART", params...)
|
980
|
1007
|
for _, member := range channel.Members() {
|
981
|
1008
|
if respectAuditorium {
|
982
|
1009
|
channel.stateMutex.RLock()
|
983
|
|
- memberModes, ok := channel.members[member]
|
|
1010
|
+ memberData, ok := channel.members[member]
|
984
|
1011
|
channel.stateMutex.RUnlock()
|
985
|
|
- if !ok || memberModes.HighestChannelUserMode() == modes.Mode(0) {
|
|
1012
|
+ if !ok || memberData.modes.HighestChannelUserMode() == modes.Mode(0) {
|
986
|
1013
|
continue
|
987
|
1014
|
}
|
988
|
1015
|
}
|
|
@@ -1022,12 +1049,12 @@ func (channel *Channel) Resume(session *Session, timestamp time.Time) {
|
1022
|
1049
|
|
1023
|
1050
|
func (channel *Channel) resumeAndAnnounce(session *Session) {
|
1024
|
1051
|
channel.stateMutex.RLock()
|
1025
|
|
- modeSet := channel.members[session.client]
|
|
1052
|
+ memberData, found := channel.members[session.client]
|
1026
|
1053
|
channel.stateMutex.RUnlock()
|
1027
|
|
- if modeSet == nil {
|
|
1054
|
+ if !found {
|
1028
|
1055
|
return
|
1029
|
1056
|
}
|
1030
|
|
- oldModes := modeSet.String()
|
|
1057
|
+ oldModes := memberData.modes.String()
|
1031
|
1058
|
if 0 < len(oldModes) {
|
1032
|
1059
|
oldModes = "+" + oldModes
|
1033
|
1060
|
}
|
|
@@ -1271,8 +1298,9 @@ func (channel *Channel) SetTopic(client *Client, topic string, rb *ResponseBuffe
|
1271
|
1298
|
// CanSpeak returns true if the client can speak on this channel, otherwise it returns false along with the channel mode preventing the client from speaking.
|
1272
|
1299
|
func (channel *Channel) CanSpeak(client *Client) (bool, modes.Mode) {
|
1273
|
1300
|
channel.stateMutex.RLock()
|
1274
|
|
- clientModes, hasClient := channel.members[client]
|
|
1301
|
+ memberData, hasClient := channel.members[client]
|
1275
|
1302
|
channel.stateMutex.RUnlock()
|
|
1303
|
+ clientModes := memberData.modes
|
1276
|
1304
|
|
1277
|
1305
|
if !hasClient && channel.flags.HasMode(modes.NoOutside) {
|
1278
|
1306
|
// TODO: enforce regular +b bans on -n channels?
|
|
@@ -1347,9 +1375,9 @@ func (channel *Channel) SendSplitMessage(command string, minPrefixMode modes.Mod
|
1347
|
1375
|
|
1348
|
1376
|
if channel.flags.HasMode(modes.OpModerated) {
|
1349
|
1377
|
channel.stateMutex.RLock()
|
1350
|
|
- cuModes := channel.members[client]
|
|
1378
|
+ cuData := channel.members[client]
|
1351
|
1379
|
channel.stateMutex.RUnlock()
|
1352
|
|
- if cuModes.HighestChannelUserMode() == modes.Mode(0) {
|
|
1380
|
+ if cuData.modes.HighestChannelUserMode() == modes.Mode(0) {
|
1353
|
1381
|
// max(statusmsg_minmode, halfop)
|
1354
|
1382
|
if minPrefixMode == modes.Mode(0) || minPrefixMode == modes.Voice {
|
1355
|
1383
|
minPrefixMode = modes.Halfop
|
|
@@ -1402,9 +1430,9 @@ func (channel *Channel) applyModeToMember(client *Client, change modes.ModeChang
|
1402
|
1430
|
change.Arg = target.Nick()
|
1403
|
1431
|
|
1404
|
1432
|
channel.stateMutex.Lock()
|
1405
|
|
- modeset, exists := channel.members[target]
|
|
1433
|
+ memberData, exists := channel.members[target]
|
1406
|
1434
|
if exists {
|
1407
|
|
- if modeset.SetMode(change.Mode, change.Op == modes.Add) {
|
|
1435
|
+ if memberData.modes.SetMode(change.Mode, change.Op == modes.Add) {
|
1408
|
1436
|
applied = true
|
1409
|
1437
|
result = change
|
1410
|
1438
|
}
|
|
@@ -1590,19 +1618,19 @@ func (channel *Channel) auditoriumFriends(client *Client) (friends []*Client) {
|
1590
|
1618
|
channel.stateMutex.RLock()
|
1591
|
1619
|
defer channel.stateMutex.RUnlock()
|
1592
|
1620
|
|
1593
|
|
- clientModes := channel.members[client]
|
1594
|
|
- if clientModes == nil {
|
|
1621
|
+ clientData, found := channel.members[client]
|
|
1622
|
+ if !found {
|
1595
|
1623
|
return // non-members have no friends
|
1596
|
1624
|
}
|
1597
|
1625
|
if !channel.flags.HasMode(modes.Auditorium) {
|
1598
|
1626
|
return channel.membersCache // default behavior for members
|
1599
|
1627
|
}
|
1600
|
|
- if clientModes.HighestChannelUserMode() != modes.Mode(0) {
|
|
1628
|
+ if clientData.modes.HighestChannelUserMode() != modes.Mode(0) {
|
1601
|
1629
|
return channel.membersCache // +v and up can see everyone in the auditorium
|
1602
|
1630
|
}
|
1603
|
1631
|
// without +v, your friends are those with +v and up
|
1604
|
|
- for member, memberModes := range channel.members {
|
1605
|
|
- if memberModes.HighestChannelUserMode() != modes.Mode(0) {
|
|
1632
|
+ for member, memberData := range channel.members {
|
|
1633
|
+ if memberData.modes.HighestChannelUserMode() != modes.Mode(0) {
|
1606
|
1634
|
friends = append(friends, member)
|
1607
|
1635
|
}
|
1608
|
1636
|
}
|