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.

331 lines
7.3 KiB

6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
  1. package list
  2. import (
  3. "sort"
  4. "strings"
  5. "sync"
  6. "github.com/gissleh/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. ok = list.Insert(user)
  57. if !ok {
  58. // Patch the user's modes and prefixes since this is up to date information.
  59. list.mutex.Lock()
  60. for _, existing := range list.users {
  61. if existing.Nick == user.Nick {
  62. existing.Modes = user.Modes
  63. existing.Prefixes = user.Prefixes
  64. existing.updatePrefixedNick()
  65. break
  66. }
  67. }
  68. list.mutex.Unlock()
  69. }
  70. return ok
  71. }
  72. // Insert a user. Modes and prefixes will be cleaned up before insertion.
  73. func (list *List) Insert(user User) (ok bool) {
  74. if len(user.Modes) > 0 {
  75. // IRCv3 promises they'll be ordered by rank in WHO and NAMES replies,
  76. // but one can never be too sure with IRC.
  77. user.Modes = list.isupport.SortModes(user.Modes)
  78. if len(user.Prefixes) < len(user.Modes) {
  79. user.Prefixes = list.isupport.Prefixes(user.Modes)
  80. } else {
  81. user.Prefixes = list.isupport.SortPrefixes(user.Prefixes)
  82. }
  83. user.updatePrefixedNick()
  84. } else {
  85. user.Prefixes = ""
  86. user.updatePrefixedNick()
  87. }
  88. list.mutex.Lock()
  89. defer list.mutex.Unlock()
  90. if list.index[strings.ToLower(user.Nick)] != nil {
  91. return false
  92. }
  93. list.users = append(list.users, &user)
  94. list.index[strings.ToLower(user.Nick)] = &user
  95. if list.autosort {
  96. list.sort()
  97. }
  98. return true
  99. }
  100. // AddMode adds a mode to a user. Redundant modes will be ignored. It returns true if
  101. // the user can be found, even if the mode was redundant.
  102. func (list *List) AddMode(nick string, mode rune) (ok bool) {
  103. if !list.isupport.IsPermissionMode(mode) {
  104. return false
  105. }
  106. list.mutex.RLock()
  107. defer list.mutex.RUnlock()
  108. user := list.index[strings.ToLower(nick)]
  109. if user == nil {
  110. return false
  111. }
  112. if strings.ContainsRune(user.Modes, mode) {
  113. return true
  114. }
  115. prevHighest := user.HighestMode()
  116. user.Modes = list.isupport.SortModes(user.Modes + string(mode))
  117. user.Prefixes = list.isupport.Prefixes(user.Modes)
  118. user.updatePrefixedNick()
  119. // Only sort if the new mode changed the highest mode.
  120. if list.autosort && prevHighest != user.HighestMode() {
  121. list.sort()
  122. }
  123. return true
  124. }
  125. // RemoveMode adds a mode to a user. It returns true if
  126. // the user can be found, even if the mode was not there.
  127. func (list *List) RemoveMode(nick string, mode rune) (ok bool) {
  128. if !list.isupport.IsPermissionMode(mode) {
  129. return false
  130. }
  131. list.mutex.RLock()
  132. defer list.mutex.RUnlock()
  133. user := list.index[strings.ToLower(nick)]
  134. if user == nil {
  135. return false
  136. }
  137. if !strings.ContainsRune(user.Modes, mode) {
  138. return true
  139. }
  140. prevHighest := user.HighestMode()
  141. user.Modes = strings.Replace(user.Modes, string(mode), "", 1)
  142. user.Prefixes = strings.Replace(user.Prefixes, string(list.isupport.Prefix(mode)), "", 1)
  143. user.updatePrefixedNick()
  144. // Only sort if the new mode changed the highest mode.
  145. if list.autosort && prevHighest != user.HighestMode() {
  146. list.sort()
  147. }
  148. return true
  149. }
  150. // Rename renames a user. It will return true if user by `from` exists, or if user by `to` does not exist.
  151. func (list *List) Rename(from, to string) (ok bool) {
  152. fromKey := strings.ToLower(from)
  153. toKey := strings.ToLower(to)
  154. list.mutex.Lock()
  155. defer list.mutex.Unlock()
  156. // Sanitiy check
  157. user := list.index[fromKey]
  158. if user == nil {
  159. return false
  160. }
  161. if from == to {
  162. return true
  163. }
  164. existing := list.index[toKey]
  165. if existing != nil {
  166. return false
  167. }
  168. user.Nick = to
  169. user.updatePrefixedNick()
  170. delete(list.index, fromKey)
  171. list.index[toKey] = user
  172. if list.autosort {
  173. list.sort()
  174. }
  175. return true
  176. }
  177. // Remove a user from the userlist.
  178. func (list *List) Remove(nick string) (ok bool) {
  179. list.mutex.Lock()
  180. defer list.mutex.Unlock()
  181. user := list.index[strings.ToLower(nick)]
  182. if user == nil {
  183. return false
  184. }
  185. for i := range list.users {
  186. if list.users[i] == user {
  187. list.users = append(list.users[:i], list.users[i+1:]...)
  188. break
  189. }
  190. }
  191. delete(list.index, strings.ToLower(nick))
  192. return true
  193. }
  194. // User gets a copy of the user by nick, or an empty user if there is none.
  195. func (list *List) User(nick string) (u User, ok bool) {
  196. list.mutex.RLock()
  197. defer list.mutex.RUnlock()
  198. user := list.index[strings.ToLower(nick)]
  199. if user == nil {
  200. return User{}, false
  201. }
  202. return *user, true
  203. }
  204. // Users gets a copy of the users in the list's current state.
  205. func (list *List) Users() []User {
  206. result := make([]User, len(list.users))
  207. list.mutex.RLock()
  208. for i := range list.users {
  209. result[i] = *list.users[i]
  210. }
  211. list.mutex.RUnlock()
  212. return result
  213. }
  214. // Patch allows editing a limited subset of the user's properties.
  215. func (list *List) Patch(nick string, patch UserPatch) (ok bool) {
  216. list.mutex.Lock()
  217. defer list.mutex.Unlock()
  218. for _, user := range list.users {
  219. if strings.EqualFold(nick, user.Nick) {
  220. if patch.Account != "" || patch.ClearAccount {
  221. user.Account = patch.Account
  222. }
  223. if patch.Away != "" || patch.ClearAway {
  224. user.Away = patch.Away
  225. }
  226. if patch.User != "" {
  227. user.User = patch.User
  228. }
  229. if patch.Host != "" {
  230. user.Host = patch.Host
  231. }
  232. return true
  233. }
  234. }
  235. return false
  236. }
  237. // SetAutoSort enables or disables automatic sorting, which by default is enabled.
  238. // Dislabing it makes sense when doing a massive operation. Enabling it will trigger
  239. // a sort.
  240. func (list *List) SetAutoSort(autosort bool) {
  241. list.mutex.Lock()
  242. list.autosort = autosort
  243. list.sort()
  244. list.mutex.Unlock()
  245. }
  246. // Clear removes all users in a list.
  247. func (list *List) Clear() {
  248. list.mutex.Lock()
  249. list.users = list.users[:0]
  250. for key := range list.index {
  251. delete(list.index, key)
  252. }
  253. list.mutex.Unlock()
  254. }
  255. // Immutable gets an immutable version of the list.
  256. func (list *List) Immutable() Immutable {
  257. return Immutable{list: list}
  258. }
  259. func (list *List) sort() {
  260. sort.Slice(list.users, func(i, j int) bool {
  261. a := list.users[i]
  262. b := list.users[j]
  263. aMode := a.HighestMode()
  264. bMode := b.HighestMode()
  265. if aMode != bMode {
  266. return list.isupport.IsModeHigher(aMode, bMode)
  267. }
  268. return strings.ToLower(a.Nick) < strings.ToLower(b.Nick)
  269. })
  270. }