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.

314 lines
7.3 KiB

6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
  1. package isupport
  2. import (
  3. "strconv"
  4. "strings"
  5. "sync"
  6. )
  7. // ISupport is a data structure containing server instructions about
  8. // supported modes, encodings, lengths, prefixes, and so on. It is built
  9. // from the 005 numeric's data, and has helper methods that makes sense
  10. // of it. It's thread-safe through a reader/writer lock, so the locks will
  11. // only block in the short duration post-registration when the 005s come in
  12. type ISupport struct {
  13. lock sync.RWMutex
  14. state State
  15. }
  16. // Get gets an isupport key. This is unprocessed data, and a helper should
  17. // be used if available.
  18. func (isupport *ISupport) Get(key string) (value string, ok bool) {
  19. isupport.lock.RLock()
  20. value, ok = isupport.state.Raw[key]
  21. isupport.lock.RUnlock()
  22. return
  23. }
  24. // Number gets a key and converts it to a number.
  25. func (isupport *ISupport) Number(key string) (value int, ok bool) {
  26. isupport.lock.RLock()
  27. strValue, ok := isupport.state.Raw[key]
  28. isupport.lock.RUnlock()
  29. if !ok {
  30. return 0, ok
  31. }
  32. value, err := strconv.Atoi(strValue)
  33. if err != nil {
  34. return value, false
  35. }
  36. return value, ok
  37. }
  38. // ParsePrefixedNick parses a full nick into its components.
  39. // Example: "@+HammerTime62" -> `"HammerTime62", "ov", "@+"`
  40. func (isupport *ISupport) ParsePrefixedNick(fullnick string) (nick, modes, prefixes string) {
  41. isupport.lock.RLock()
  42. defer isupport.lock.RUnlock()
  43. if fullnick == "" || isupport.state.Prefixes == nil {
  44. return fullnick, "", ""
  45. }
  46. for i, ch := range fullnick {
  47. if mode, ok := isupport.state.Prefixes[ch]; ok {
  48. modes += string(mode)
  49. prefixes += string(ch)
  50. } else {
  51. nick = fullnick[i:]
  52. break
  53. }
  54. }
  55. return nick, modes, prefixes
  56. }
  57. // HighestPrefix gets the highest-level prefix declared by PREFIX
  58. func (isupport *ISupport) HighestPrefix(prefixes string) rune {
  59. isupport.lock.RLock()
  60. defer isupport.lock.RUnlock()
  61. if len(prefixes) == 1 {
  62. return rune(prefixes[0])
  63. }
  64. for _, prefix := range isupport.state.PrefixOrder {
  65. if strings.ContainsRune(prefixes, prefix) {
  66. return prefix
  67. }
  68. }
  69. return rune(0)
  70. }
  71. // HighestMode gets the highest-level mode declared by PREFIX
  72. func (isupport *ISupport) HighestMode(modes string) rune {
  73. isupport.lock.RLock()
  74. defer isupport.lock.RUnlock()
  75. if len(modes) == 1 {
  76. return rune(modes[0])
  77. }
  78. for _, mode := range isupport.state.ModeOrder {
  79. if strings.ContainsRune(modes, mode) {
  80. return mode
  81. }
  82. }
  83. return rune(0)
  84. }
  85. // IsModeHigher returns true if `current` is a higher mode than `other`.
  86. func (isupport *ISupport) IsModeHigher(current rune, other rune) bool {
  87. isupport.lock.RLock()
  88. defer isupport.lock.RUnlock()
  89. if current == other {
  90. return false
  91. }
  92. if current == 0 {
  93. return false
  94. }
  95. if other == 0 {
  96. return true
  97. }
  98. for _, mode := range isupport.state.ModeOrder {
  99. if mode == current {
  100. return true
  101. } else if mode == other {
  102. return false
  103. }
  104. }
  105. return false
  106. }
  107. // SortModes returns the modes in order. Any unknown modes will be omitted.
  108. func (isupport *ISupport) SortModes(modes string) string {
  109. result := ""
  110. for _, ch := range isupport.state.ModeOrder {
  111. for _, ch2 := range modes {
  112. if ch2 == ch {
  113. result += string(ch)
  114. }
  115. }
  116. }
  117. return result
  118. }
  119. // SortPrefixes returns the prefixes in order. Any unknown prefixes will be omitted.
  120. func (isupport *ISupport) SortPrefixes(prefixes string) string {
  121. result := ""
  122. for _, ch := range isupport.state.PrefixOrder {
  123. for _, ch2 := range prefixes {
  124. if ch2 == ch {
  125. result += string(ch)
  126. }
  127. }
  128. }
  129. return result
  130. }
  131. // Mode gets the mode for the prefix.
  132. func (isupport *ISupport) Mode(prefix rune) rune {
  133. isupport.lock.RLock()
  134. defer isupport.lock.RUnlock()
  135. return isupport.state.Prefixes[prefix]
  136. }
  137. // Prefix gets the prefix for the mode. It's a bit slower
  138. // than the other way around, but is a far less frequently
  139. // used.
  140. func (isupport *ISupport) Prefix(mode rune) rune {
  141. isupport.lock.RLock()
  142. defer isupport.lock.RUnlock()
  143. for prefix, mappedMode := range isupport.state.Prefixes {
  144. if mappedMode == mode {
  145. return prefix
  146. }
  147. }
  148. return rune(0)
  149. }
  150. // Prefixes gets the prefixes in the order of the modes, skipping any
  151. // invalid modes.
  152. func (isupport *ISupport) Prefixes(modes string) string {
  153. result := ""
  154. for _, mode := range modes {
  155. prefix := isupport.Prefix(mode)
  156. if prefix != mode {
  157. result += string(prefix)
  158. }
  159. }
  160. return result
  161. }
  162. // IsChannel returns whether the target name is a channel.
  163. func (isupport *ISupport) IsChannel(targetName string) bool {
  164. isupport.lock.RLock()
  165. defer isupport.lock.RUnlock()
  166. return strings.Contains(isupport.state.Raw["CHANTYPES"], string(targetName[0]))
  167. }
  168. // IsPermissionMode returns whether the flag is a permission mode
  169. func (isupport *ISupport) IsPermissionMode(flag rune) bool {
  170. isupport.lock.RLock()
  171. defer isupport.lock.RUnlock()
  172. return strings.ContainsRune(isupport.state.ModeOrder, flag)
  173. }
  174. // ModeTakesArgument returns true if the mode takes an argument
  175. func (isupport *ISupport) ModeTakesArgument(flag rune, plus bool) bool {
  176. isupport.lock.RLock()
  177. defer isupport.lock.RUnlock()
  178. // Permission modes always take an argument.
  179. if strings.ContainsRune(isupport.state.ModeOrder, flag) {
  180. return true
  181. }
  182. // Modes in category A and B always takes an argument
  183. if strings.ContainsRune(isupport.state.ChannelModes[0], flag) || strings.ContainsRune(isupport.state.ChannelModes[1], flag) {
  184. return true
  185. }
  186. // Modes in category C only takes one when added
  187. if plus && strings.ContainsRune(isupport.state.ChannelModes[1], flag) {
  188. return true
  189. }
  190. // Modes in category D and outside never does
  191. return false
  192. }
  193. // ChannelModeType returns a number from 0 to 3 based on what block of mode
  194. // in the CHANMODES variable it fits into. If it's not found at all, it will
  195. // return -1
  196. func (isupport *ISupport) ChannelModeType(mode rune) int {
  197. isupport.lock.RLock()
  198. defer isupport.lock.RUnlock()
  199. // User permission modes function exactly like the first block
  200. // when it comes to add/remove
  201. if strings.ContainsRune(isupport.state.ModeOrder, mode) {
  202. return 0
  203. }
  204. for i, block := range isupport.state.ChannelModes {
  205. if strings.ContainsRune(block, mode) {
  206. return i
  207. }
  208. }
  209. return -1
  210. }
  211. // Set sets an isupport key, and related structs. This should only be used
  212. // if a 005 packet contains the Key-Value pair or if it can be "polyfilled"
  213. // in some other way.
  214. func (isupport *ISupport) Set(key, value string) {
  215. key = strings.ToUpper(key)
  216. isupport.lock.Lock()
  217. if isupport.state.Raw == nil {
  218. isupport.state.Raw = make(map[string]string, 32)
  219. }
  220. isupport.state.Raw[key] = value
  221. switch key {
  222. case "PREFIX": // PREFIX=(ov)@+
  223. {
  224. split := strings.SplitN(value[1:], ")", 2)
  225. isupport.state.PrefixOrder = split[1]
  226. isupport.state.ModeOrder = split[0]
  227. isupport.state.Prefixes = make(map[rune]rune, len(split[0]))
  228. for i, ch := range split[0] {
  229. isupport.state.Prefixes[rune(split[1][i])] = ch
  230. }
  231. }
  232. case "CHANMODES": // CHANMODES=eIbq,k,flj,CFLNPQcgimnprstz
  233. {
  234. isupport.state.ChannelModes = strings.Split(value, ",")
  235. }
  236. }
  237. isupport.lock.Unlock()
  238. }
  239. // State gets a copy of the isupport state.
  240. func (isupport *ISupport) State() *State {
  241. return isupport.state.Copy()
  242. }
  243. // Reset clears everything.
  244. func (isupport *ISupport) Reset() {
  245. isupport.lock.Lock()
  246. isupport.state.PrefixOrder = ""
  247. isupport.state.ModeOrder = ""
  248. isupport.state.Prefixes = nil
  249. isupport.state.ChannelModes = nil
  250. for key := range isupport.state.Raw {
  251. delete(isupport.state.Raw, key)
  252. }
  253. isupport.lock.Unlock()
  254. }