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.

318 lines
7.4 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
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.PrefixMap == nil {
  44. return fullnick, "", ""
  45. }
  46. for i, ch := range fullnick {
  47. if mode, ok := isupport.state.PrefixMap[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.PrefixMap[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.PrefixMap {
  144. if mappedMode == mode {
  145. return prefix
  146. }
  147. }
  148. return rune(0)
  149. }
  150. // PrefixMap 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. if len(targetName) < 1 {
  165. return false
  166. }
  167. isupport.lock.RLock()
  168. defer isupport.lock.RUnlock()
  169. return strings.Contains(isupport.state.Raw["CHANTYPES"], string(targetName[0]))
  170. }
  171. // IsPermissionMode returns whether the flag is a permission mode
  172. func (isupport *ISupport) IsPermissionMode(flag rune) bool {
  173. isupport.lock.RLock()
  174. defer isupport.lock.RUnlock()
  175. return strings.ContainsRune(isupport.state.ModeOrder, flag)
  176. }
  177. // ModeTakesArgument returns true if the mode takes an argument
  178. func (isupport *ISupport) ModeTakesArgument(flag rune, plus bool) bool {
  179. isupport.lock.RLock()
  180. defer isupport.lock.RUnlock()
  181. // Permission modes always take an argument.
  182. if strings.ContainsRune(isupport.state.ModeOrder, flag) {
  183. return true
  184. }
  185. // Modes in category A and B always takes an argument
  186. if strings.ContainsRune(isupport.state.ChannelModes[0], flag) || strings.ContainsRune(isupport.state.ChannelModes[1], flag) {
  187. return true
  188. }
  189. // Modes in category C only takes one when added
  190. if plus && strings.ContainsRune(isupport.state.ChannelModes[1], flag) {
  191. return true
  192. }
  193. // Modes in category D and outside never does
  194. return false
  195. }
  196. // ChannelModeType returns a number from 0 to 3 based on what block of mode
  197. // in the CHANMODES variable it fits into. If it's not found at all, it will
  198. // return -1
  199. func (isupport *ISupport) ChannelModeType(mode rune) int {
  200. isupport.lock.RLock()
  201. defer isupport.lock.RUnlock()
  202. // User permission modes function exactly like the first block
  203. // when it comes to add/remove
  204. if strings.ContainsRune(isupport.state.ModeOrder, mode) {
  205. return 0
  206. }
  207. for i, block := range isupport.state.ChannelModes {
  208. if strings.ContainsRune(block, mode) {
  209. return i
  210. }
  211. }
  212. return -1
  213. }
  214. // Set sets an isupport key, and related structs. This should only be used
  215. // if a 005 packet contains the Key-Value pair or if it can be "polyfilled"
  216. // in some other way.
  217. func (isupport *ISupport) Set(key, value string) {
  218. key = strings.ToUpper(key)
  219. isupport.lock.Lock()
  220. if isupport.state.Raw == nil {
  221. isupport.state.Raw = make(map[string]string, 32)
  222. }
  223. isupport.state.Raw[key] = value
  224. switch key {
  225. case "PREFIX": // PREFIX=(ov)@+
  226. {
  227. split := strings.SplitN(value[1:], ")", 2)
  228. isupport.state.PrefixOrder = split[1]
  229. isupport.state.ModeOrder = split[0]
  230. isupport.state.PrefixMap = make(map[rune]rune, len(split[0]))
  231. for i, ch := range split[0] {
  232. isupport.state.PrefixMap[rune(split[1][i])] = ch
  233. }
  234. }
  235. case "CHANMODES": // CHANMODES=eIbq,k,flj,CFLNPQcgimnprstz
  236. {
  237. isupport.state.ChannelModes = strings.Split(value, ",")
  238. }
  239. }
  240. isupport.lock.Unlock()
  241. }
  242. // State gets a copy of the isupport state.
  243. func (isupport *ISupport) State() *State {
  244. return isupport.state.Copy()
  245. }
  246. // Reset clears everything.
  247. func (isupport *ISupport) Reset() {
  248. isupport.lock.Lock()
  249. isupport.state.PrefixOrder = ""
  250. isupport.state.ModeOrder = ""
  251. isupport.state.PrefixMap = nil
  252. isupport.state.ChannelModes = nil
  253. for key := range isupport.state.Raw {
  254. delete(isupport.state.Raw, key)
  255. }
  256. isupport.lock.Unlock()
  257. }