Mirror of github.com/gissleh/irc
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.

281 lines
6.3 KiB

6 years ago
  1. package list
  2. import (
  3. "sort"
  4. "strings"
  5. "sync"
  6. "git.aiterp.net/gisle/irc/isupport"
  7. )
  8. // The List of users in a channel. It has all operations one would perform on
  9. // users, like adding/removing modes and changing nicks.
  10. type List struct {
  11. mutex sync.RWMutex
  12. isupport *isupport.ISupport
  13. users []*User
  14. index map[string]*User
  15. autosort bool
  16. }
  17. // New creates a new list with the ISupport. The list can be reused between connections since the
  18. // ISupport is simply cleared and repopulated, but it should be cleared.
  19. func New(isupport *isupport.ISupport) *List {
  20. return &List{
  21. isupport: isupport,
  22. users: make([]*User, 0, 64),
  23. index: make(map[string]*User, 64),
  24. autosort: true,
  25. }
  26. }
  27. // InsertFromNamesToken inserts using a NAMES token to get the nick, user, host and prefixes.
  28. // The format is `"@+Nick@user!hostmask.example.com"`
  29. func (list *List) InsertFromNamesToken(namestoken string) (ok bool) {
  30. user := User{}
  31. // Parse prefixes and modes. @ and ! (It's IRCHighWay if you were wondering) are both
  32. // mode prefixes and that just makes a mess if leave them for last. It also supports
  33. // `multi-prefix`
  34. for i, ch := range namestoken {
  35. mode := list.isupport.Mode(ch)
  36. if mode == 0 {
  37. if i != 0 {
  38. namestoken = namestoken[i:]
  39. }
  40. break
  41. }
  42. user.Prefixes += string(ch)
  43. user.Modes += string(mode)
  44. }
  45. // Get the nick
  46. split := strings.Split(namestoken, "!")
  47. user.Nick = split[0]
  48. // Support `userhost-in-names`
  49. if len(split) == 2 {
  50. userhost := strings.Split(split[1], "@")
  51. if len(userhost) == 2 {
  52. user.User = userhost[0]
  53. user.Host = userhost[1]
  54. }
  55. }
  56. return list.Insert(user)
  57. }
  58. // Insert a user. Modes and prefixes will be cleaned up before insertion.
  59. func (list *List) Insert(user User) (ok bool) {
  60. if len(user.Modes) > 0 {
  61. // IRCv3 promises they'll be ordered by rank in WHO and NAMES replies,
  62. // but one can never be too sure with IRC.
  63. user.Modes = list.isupport.SortModes(user.Modes)
  64. if len(user.Prefixes) < len(user.Modes) {
  65. user.Prefixes = list.isupport.Prefixes(user.Modes)
  66. } else {
  67. user.Prefixes = list.isupport.SortPrefixes(user.Prefixes)
  68. }
  69. user.updatePrefixedNick()
  70. } else {
  71. user.Prefixes = ""
  72. user.updatePrefixedNick()
  73. }
  74. list.mutex.Lock()
  75. defer list.mutex.Unlock()
  76. if list.index[strings.ToLower(user.Nick)] != nil {
  77. return false
  78. }
  79. list.users = append(list.users, &user)
  80. list.index[strings.ToLower(user.Nick)] = &user
  81. if list.autosort {
  82. list.sort()
  83. }
  84. return true
  85. }
  86. // AddMode adds a mode to a user. Redundant modes will be ignored. It returns true if
  87. // the user can be found, even if the mode was redundant.
  88. func (list *List) AddMode(nick string, mode rune) (ok bool) {
  89. if !list.isupport.IsPermissionMode(mode) {
  90. return false
  91. }
  92. list.mutex.RLock()
  93. defer list.mutex.RUnlock()
  94. user := list.index[strings.ToLower(nick)]
  95. if user == nil {
  96. return false
  97. }
  98. if strings.ContainsRune(user.Modes, mode) {
  99. return true
  100. }
  101. prevHighest := user.HighestMode()
  102. user.Modes = list.isupport.SortModes(user.Modes + string(mode))
  103. user.Prefixes = list.isupport.Prefixes(user.Modes)
  104. user.updatePrefixedNick()
  105. // Only sort if the new mode changed the highest mode.
  106. if list.autosort && prevHighest != user.HighestMode() {
  107. list.sort()
  108. }
  109. return true
  110. }
  111. // RemoveMode adds a mode to a user. It returns true if
  112. // the user can be found, even if the mode was not there.
  113. func (list *List) RemoveMode(nick string, mode rune) (ok bool) {
  114. if !list.isupport.IsPermissionMode(mode) {
  115. return false
  116. }
  117. list.mutex.RLock()
  118. defer list.mutex.RUnlock()
  119. user := list.index[strings.ToLower(nick)]
  120. if user == nil {
  121. return false
  122. }
  123. if !strings.ContainsRune(user.Modes, mode) {
  124. return true
  125. }
  126. prevHighest := user.HighestMode()
  127. user.Modes = strings.Replace(user.Modes, string(mode), "", 1)
  128. user.Prefixes = strings.Replace(user.Prefixes, string(list.isupport.Prefix(mode)), "", 1)
  129. user.updatePrefixedNick()
  130. // Only sort if the new mode changed the highest mode.
  131. if list.autosort && prevHighest != user.HighestMode() {
  132. list.sort()
  133. }
  134. return true
  135. }
  136. // Rename renames a user. It will return true if user by `from` exists, or if user by `to` does not exist.
  137. func (list *List) Rename(from, to string) (ok bool) {
  138. fromKey := strings.ToLower(from)
  139. toKey := strings.ToLower(to)
  140. list.mutex.Lock()
  141. defer list.mutex.Unlock()
  142. // Sanitiy check
  143. user := list.index[fromKey]
  144. if user == nil {
  145. return false
  146. }
  147. if from == to {
  148. return true
  149. }
  150. existing := list.index[toKey]
  151. if existing != nil {
  152. return false
  153. }
  154. user.Nick = to
  155. user.updatePrefixedNick()
  156. delete(list.index, fromKey)
  157. list.index[toKey] = user
  158. if list.autosort {
  159. list.sort()
  160. }
  161. return true
  162. }
  163. // Remove a user from the userlist.
  164. func (list *List) Remove(nick string) (ok bool) {
  165. list.mutex.Lock()
  166. defer list.mutex.Unlock()
  167. user := list.index[strings.ToLower(nick)]
  168. if user == nil {
  169. return false
  170. }
  171. for i := range list.users {
  172. if list.users[i] == user {
  173. list.users = append(list.users[:i], list.users[i+1:]...)
  174. break
  175. }
  176. }
  177. delete(list.index, strings.ToLower(nick))
  178. return true
  179. }
  180. // User gets a copy of the user by nick, or an empty user if there is none.
  181. func (list *List) User(nick string) (u User, ok bool) {
  182. list.mutex.RLock()
  183. defer list.mutex.RUnlock()
  184. user := list.index[strings.ToLower(nick)]
  185. if user == nil {
  186. return User{}, false
  187. }
  188. return *user, true
  189. }
  190. // Users gets a copy of the users in the list's current state.
  191. func (list *List) Users() []User {
  192. result := make([]User, len(list.users))
  193. list.mutex.RLock()
  194. for i := range list.users {
  195. result[i] = *list.users[i]
  196. }
  197. list.mutex.RUnlock()
  198. return result
  199. }
  200. // SetAutoSort enables or disables automatic sorting, which by default is enabled.
  201. // Dislabing it makes sense when doing a massive operation. Enabling it will trigger
  202. // a sort.
  203. func (list *List) SetAutoSort(autosort bool) {
  204. list.mutex.Lock()
  205. list.autosort = autosort
  206. list.sort()
  207. list.mutex.Unlock()
  208. }
  209. // Clear removes all users in a list.
  210. func (list *List) Clear() {
  211. list.mutex.Lock()
  212. list.users = list.users[:0]
  213. for key := range list.index {
  214. delete(list.index, key)
  215. }
  216. list.mutex.Unlock()
  217. }
  218. func (list *List) sort() {
  219. sort.Slice(list.users, func(i, j int) bool {
  220. a := list.users[i]
  221. b := list.users[j]
  222. aMode := a.HighestMode()
  223. bMode := b.HighestMode()
  224. if aMode != bMode {
  225. return list.isupport.IsModeHigher(aMode, bMode)
  226. }
  227. return strings.ToLower(a.Nick) < strings.ToLower(b.Nick)
  228. })
  229. }