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.5KB

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