You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

usermaskset.go 3.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. // Copyright (c) 2012-2014 Jeremy Latt
  2. // Copyright (c) 2016-2018 Daniel Oaks
  3. // Copyright (c) 2019-2020 Shivaram Lingamneni
  4. // released under the MIT license
  5. package irc
  6. import (
  7. "regexp"
  8. "strings"
  9. "sync"
  10. "sync/atomic"
  11. "time"
  12. "github.com/ergochat/ergo/irc/utils"
  13. )
  14. type MaskInfo struct {
  15. TimeCreated time.Time
  16. CreatorNickmask string
  17. CreatorAccount string
  18. }
  19. // UserMaskSet holds a set of client masks and lets you match hostnames to them.
  20. type UserMaskSet struct {
  21. sync.RWMutex
  22. serialCacheUpdateMutex sync.Mutex
  23. masks map[string]MaskInfo
  24. regexp atomic.Pointer[regexp.Regexp]
  25. muteRegexp atomic.Pointer[regexp.Regexp]
  26. }
  27. func NewUserMaskSet() *UserMaskSet {
  28. return new(UserMaskSet)
  29. }
  30. // Add adds the given mask to this set.
  31. func (set *UserMaskSet) Add(mask, creatorNickmask, creatorAccount string) (maskAdded string, err error) {
  32. casefoldedMask, err := CanonicalizeMaskWildcard(mask)
  33. if err != nil {
  34. return
  35. }
  36. set.serialCacheUpdateMutex.Lock()
  37. defer set.serialCacheUpdateMutex.Unlock()
  38. set.Lock()
  39. if set.masks == nil {
  40. set.masks = make(map[string]MaskInfo)
  41. }
  42. _, present := set.masks[casefoldedMask]
  43. if !present {
  44. maskAdded = casefoldedMask
  45. set.masks[casefoldedMask] = MaskInfo{
  46. TimeCreated: time.Now().UTC(),
  47. CreatorNickmask: creatorNickmask,
  48. CreatorAccount: creatorAccount,
  49. }
  50. }
  51. set.Unlock()
  52. if !present {
  53. set.setRegexp()
  54. }
  55. return
  56. }
  57. // Remove removes the given mask from this set.
  58. func (set *UserMaskSet) Remove(mask string) (maskRemoved string, err error) {
  59. mask, err = CanonicalizeMaskWildcard(mask)
  60. if err != nil {
  61. return
  62. }
  63. set.serialCacheUpdateMutex.Lock()
  64. defer set.serialCacheUpdateMutex.Unlock()
  65. set.Lock()
  66. _, removed := set.masks[mask]
  67. if removed {
  68. maskRemoved = mask
  69. delete(set.masks, mask)
  70. }
  71. set.Unlock()
  72. if removed {
  73. set.setRegexp()
  74. }
  75. return
  76. }
  77. func (set *UserMaskSet) SetMasks(masks map[string]MaskInfo) {
  78. set.Lock()
  79. set.masks = masks
  80. set.Unlock()
  81. set.setRegexp()
  82. }
  83. func (set *UserMaskSet) Masks() (result map[string]MaskInfo) {
  84. set.RLock()
  85. defer set.RUnlock()
  86. result = make(map[string]MaskInfo, len(set.masks))
  87. for mask, info := range set.masks {
  88. result[mask] = info
  89. }
  90. return
  91. }
  92. // Match matches the given n!u@h against the standard (non-ext) bans.
  93. func (set *UserMaskSet) Match(userhost string) bool {
  94. regexp := set.regexp.Load()
  95. if regexp == nil {
  96. return false
  97. }
  98. return regexp.MatchString(userhost)
  99. }
  100. // MatchMute matches the given NUH against the mute extbans.
  101. func (set *UserMaskSet) MatchMute(userhost string) bool {
  102. regexp := set.MuteRegexp()
  103. if regexp == nil {
  104. return false
  105. }
  106. return regexp.MatchString(userhost)
  107. }
  108. func (set *UserMaskSet) MuteRegexp() *regexp.Regexp {
  109. return set.muteRegexp.Load()
  110. }
  111. func (set *UserMaskSet) Length() int {
  112. set.RLock()
  113. defer set.RUnlock()
  114. return len(set.masks)
  115. }
  116. func (set *UserMaskSet) setRegexp() {
  117. set.RLock()
  118. maskExprs := make([]string, 0, len(set.masks))
  119. var muteExprs []string
  120. for mask := range set.masks {
  121. if strings.HasPrefix(mask, "m:") {
  122. muteExprs = append(muteExprs, mask[2:])
  123. } else {
  124. maskExprs = append(maskExprs, mask)
  125. }
  126. }
  127. set.RUnlock()
  128. compileMasks := func(masks []string) *regexp.Regexp {
  129. if len(masks) == 0 {
  130. return nil
  131. }
  132. re, _ := utils.CompileMasks(masks)
  133. return re
  134. }
  135. re := compileMasks(maskExprs)
  136. muteRe := compileMasks(muteExprs)
  137. set.regexp.Store(re)
  138. set.muteRegexp.Store(muteRe)
  139. }