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.

modes.go 8.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. // Copyright (c) 2012-2014 Jeremy Latt
  2. // Copyright (c) 2014-2015 Edmund Huber
  3. // Copyright (c) 2016-2017 Daniel Oaks <daniel@danieloaks.net>
  4. // released under the MIT license
  5. package modes
  6. import (
  7. "strings"
  8. "sync"
  9. )
  10. var (
  11. // SupportedUserModes are the user modes that we actually support (modifying).
  12. SupportedUserModes = Modes{
  13. Away, Bot, Invisible, Operator, RegisteredOnly, ServerNotice, UserRoleplaying,
  14. }
  15. // SupportedChannelModes are the channel modes that we support.
  16. SupportedChannelModes = Modes{
  17. BanMask, ChanRoleplaying, ExceptMask, InviteMask, InviteOnly, Key,
  18. Moderated, NoOutside, OpOnlyTopic, RegisteredOnly, Secret, UserLimit,
  19. }
  20. )
  21. // ModeOp is an operation performed with modes
  22. type ModeOp rune
  23. func (op ModeOp) String() string {
  24. return string(op)
  25. }
  26. const (
  27. // Add is used when adding the given key.
  28. Add ModeOp = '+'
  29. // List is used when listing modes (for instance, listing the current bans on a channel).
  30. List ModeOp = '='
  31. // Remove is used when taking away the given key.
  32. Remove ModeOp = '-'
  33. )
  34. // Mode represents a user/channel/server mode
  35. type Mode rune
  36. func (mode Mode) String() string {
  37. return string(mode)
  38. }
  39. // ModeChange is a single mode changing
  40. type ModeChange struct {
  41. Mode Mode
  42. Op ModeOp
  43. Arg string
  44. }
  45. func (change *ModeChange) String() (str string) {
  46. if (change.Op == Add) || (change.Op == Remove) {
  47. str = change.Op.String()
  48. }
  49. str += change.Mode.String()
  50. if change.Arg != "" {
  51. str += " " + change.Arg
  52. }
  53. return
  54. }
  55. // ModeChanges are a collection of 'ModeChange's
  56. type ModeChanges []ModeChange
  57. func (changes ModeChanges) String() string {
  58. if len(changes) == 0 {
  59. return ""
  60. }
  61. op := changes[0].Op
  62. str := changes[0].Op.String()
  63. for _, change := range changes {
  64. if change.Op != op {
  65. op = change.Op
  66. str += change.Op.String()
  67. }
  68. str += change.Mode.String()
  69. }
  70. for _, change := range changes {
  71. if change.Arg == "" {
  72. continue
  73. }
  74. str += " " + change.Arg
  75. }
  76. return str
  77. }
  78. // Modes is just a raw list of modes
  79. type Modes []Mode
  80. func (modes Modes) String() string {
  81. strs := make([]string, len(modes))
  82. for index, mode := range modes {
  83. strs[index] = mode.String()
  84. }
  85. return strings.Join(strs, "")
  86. }
  87. // User Modes
  88. const (
  89. Away Mode = 'a'
  90. Bot Mode = 'B'
  91. Invisible Mode = 'i'
  92. LocalOperator Mode = 'O'
  93. Operator Mode = 'o'
  94. Restricted Mode = 'r'
  95. RegisteredOnly Mode = 'R'
  96. ServerNotice Mode = 's'
  97. TLS Mode = 'Z'
  98. UserRoleplaying Mode = 'E'
  99. WallOps Mode = 'w'
  100. )
  101. // Channel Modes
  102. const (
  103. BanMask Mode = 'b' // arg
  104. ChanRoleplaying Mode = 'E' // flag
  105. ExceptMask Mode = 'e' // arg
  106. InviteMask Mode = 'I' // arg
  107. InviteOnly Mode = 'i' // flag
  108. Key Mode = 'k' // flag arg
  109. Moderated Mode = 'm' // flag
  110. NoOutside Mode = 'n' // flag
  111. OpOnlyTopic Mode = 't' // flag
  112. // RegisteredOnly mode is reused here from umode definition
  113. Secret Mode = 's' // flag
  114. UserLimit Mode = 'l' // flag arg
  115. )
  116. var (
  117. ChannelFounder Mode = 'q' // arg
  118. ChannelAdmin Mode = 'a' // arg
  119. ChannelOperator Mode = 'o' // arg
  120. Halfop Mode = 'h' // arg
  121. Voice Mode = 'v' // arg
  122. // ChannelPrivModes holds the list of modes that are privileged, ie founder/op/halfop, in order.
  123. // voice is not in this list because it cannot perform channel operator actions.
  124. ChannelPrivModes = Modes{
  125. ChannelFounder, ChannelAdmin, ChannelOperator, Halfop,
  126. }
  127. ChannelModePrefixes = map[Mode]string{
  128. ChannelFounder: "~",
  129. ChannelAdmin: "&",
  130. ChannelOperator: "@",
  131. Halfop: "%",
  132. Voice: "+",
  133. }
  134. )
  135. //
  136. // channel membership prefixes
  137. //
  138. // SplitChannelMembershipPrefixes takes a target and returns the prefixes on it, then the name.
  139. func SplitChannelMembershipPrefixes(target string) (prefixes string, name string) {
  140. name = target
  141. for {
  142. if len(name) > 0 && strings.Contains("~&@%+", string(name[0])) {
  143. prefixes += string(name[0])
  144. name = name[1:]
  145. } else {
  146. break
  147. }
  148. }
  149. return prefixes, name
  150. }
  151. // GetLowestChannelModePrefix returns the lowest channel prefix mode out of the given prefixes.
  152. func GetLowestChannelModePrefix(prefixes string) *Mode {
  153. var lowest *Mode
  154. if strings.Contains(prefixes, "+") {
  155. lowest = &Voice
  156. } else {
  157. for i, mode := range ChannelPrivModes {
  158. if strings.Contains(prefixes, ChannelModePrefixes[mode]) {
  159. lowest = &ChannelPrivModes[i]
  160. }
  161. }
  162. }
  163. return lowest
  164. }
  165. //
  166. // commands
  167. //
  168. // ParseUserModeChanges returns the valid changes, and the list of unknown chars.
  169. func ParseUserModeChanges(params ...string) (ModeChanges, map[rune]bool) {
  170. changes := make(ModeChanges, 0)
  171. unknown := make(map[rune]bool)
  172. op := List
  173. if 0 < len(params) {
  174. modeArg := params[0]
  175. skipArgs := 1
  176. for _, mode := range modeArg {
  177. if mode == '-' || mode == '+' {
  178. op = ModeOp(mode)
  179. continue
  180. }
  181. change := ModeChange{
  182. Mode: Mode(mode),
  183. Op: op,
  184. }
  185. // put arg into modechange if needed
  186. switch Mode(mode) {
  187. case ServerNotice:
  188. // always require arg
  189. if len(params) > skipArgs {
  190. change.Arg = params[skipArgs]
  191. skipArgs++
  192. } else {
  193. continue
  194. }
  195. }
  196. var isKnown bool
  197. for _, supportedMode := range SupportedUserModes {
  198. if rune(supportedMode) == mode {
  199. isKnown = true
  200. break
  201. }
  202. }
  203. if !isKnown {
  204. unknown[mode] = true
  205. continue
  206. }
  207. changes = append(changes, change)
  208. }
  209. }
  210. return changes, unknown
  211. }
  212. // ParseChannelModeChanges returns the valid changes, and the list of unknown chars.
  213. func ParseChannelModeChanges(params ...string) (ModeChanges, map[rune]bool) {
  214. changes := make(ModeChanges, 0)
  215. unknown := make(map[rune]bool)
  216. op := List
  217. if 0 < len(params) {
  218. modeArg := params[0]
  219. skipArgs := 1
  220. for _, mode := range modeArg {
  221. if mode == '-' || mode == '+' {
  222. op = ModeOp(mode)
  223. continue
  224. }
  225. change := ModeChange{
  226. Mode: Mode(mode),
  227. Op: op,
  228. }
  229. // put arg into modechange if needed
  230. switch Mode(mode) {
  231. case BanMask, ExceptMask, InviteMask:
  232. if len(params) > skipArgs {
  233. change.Arg = params[skipArgs]
  234. skipArgs++
  235. } else {
  236. change.Op = List
  237. }
  238. case ChannelFounder, ChannelAdmin, ChannelOperator, Halfop, Voice:
  239. if len(params) > skipArgs {
  240. change.Arg = params[skipArgs]
  241. skipArgs++
  242. } else {
  243. continue
  244. }
  245. case Key, UserLimit:
  246. // don't require value when removing
  247. if change.Op == Add {
  248. if len(params) > skipArgs {
  249. change.Arg = params[skipArgs]
  250. skipArgs++
  251. } else {
  252. continue
  253. }
  254. }
  255. }
  256. var isKnown bool
  257. for _, supportedMode := range SupportedChannelModes {
  258. if rune(supportedMode) == mode {
  259. isKnown = true
  260. break
  261. }
  262. }
  263. for _, supportedMode := range ChannelPrivModes {
  264. if rune(supportedMode) == mode {
  265. isKnown = true
  266. break
  267. }
  268. }
  269. if mode == rune(Voice) {
  270. isKnown = true
  271. }
  272. if !isKnown {
  273. unknown[mode] = true
  274. continue
  275. }
  276. changes = append(changes, change)
  277. }
  278. }
  279. return changes, unknown
  280. }
  281. // ModeSet holds a set of modes.
  282. type ModeSet struct {
  283. sync.RWMutex // tier 0
  284. modes map[Mode]bool
  285. }
  286. // returns a pointer to a new ModeSet
  287. func NewModeSet() *ModeSet {
  288. return &ModeSet{
  289. modes: make(map[Mode]bool),
  290. }
  291. }
  292. // test whether `mode` is set
  293. func (set *ModeSet) HasMode(mode Mode) bool {
  294. set.RLock()
  295. defer set.RUnlock()
  296. return set.modes[mode]
  297. }
  298. // set `mode` to be on or off, return whether the value actually changed
  299. func (set *ModeSet) SetMode(mode Mode, on bool) (applied bool) {
  300. set.Lock()
  301. defer set.Unlock()
  302. previouslyOn := set.modes[mode]
  303. needsApply := (on != previouslyOn)
  304. if on && needsApply {
  305. set.modes[mode] = true
  306. } else if !on && needsApply {
  307. delete(set.modes, mode)
  308. }
  309. return needsApply
  310. }
  311. // return the modes in the set as a slice
  312. func (set *ModeSet) AllModes() (result []Mode) {
  313. set.RLock()
  314. defer set.RUnlock()
  315. for mode := range set.modes {
  316. result = append(result, mode)
  317. }
  318. return
  319. }
  320. // String returns the modes in this set.
  321. func (set *ModeSet) String() string {
  322. set.RLock()
  323. defer set.RUnlock()
  324. if len(set.modes) == 0 {
  325. return ""
  326. }
  327. var result []byte
  328. for mode := range set.modes {
  329. result = append(result, mode.String()...)
  330. }
  331. return string(result)
  332. }
  333. // Prefixes returns a list of prefixes for the given set of channel modes.
  334. func (set *ModeSet) Prefixes(isMultiPrefix bool) (prefixes string) {
  335. set.RLock()
  336. defer set.RUnlock()
  337. // add prefixes in order from highest to lowest privs
  338. for _, mode := range ChannelPrivModes {
  339. if set.modes[mode] {
  340. prefixes += ChannelModePrefixes[mode]
  341. }
  342. }
  343. if set.modes[Voice] {
  344. prefixes += ChannelModePrefixes[Voice]
  345. }
  346. if !isMultiPrefix && len(prefixes) > 1 {
  347. prefixes = string(prefixes[0])
  348. }
  349. return prefixes
  350. }