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.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  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. "sort"
  8. "strings"
  9. "github.com/oragono/oragono/irc/utils"
  10. )
  11. var (
  12. // SupportedUserModes are the user modes that we actually support (modifying).
  13. SupportedUserModes = Modes{
  14. Bot, Invisible, Operator, RegisteredOnly, ServerNotice, UserRoleplaying,
  15. }
  16. // SupportedChannelModes are the channel modes that we support.
  17. SupportedChannelModes = Modes{
  18. BanMask, ChanRoleplaying, ExceptMask, InviteMask, InviteOnly, Key,
  19. Moderated, NoOutside, OpOnlyTopic, RegisteredOnly, Secret, UserLimit,
  20. NoCTCP,
  21. }
  22. )
  23. // ModeOp is an operation performed with modes
  24. type ModeOp rune
  25. const (
  26. // Add is used when adding the given key.
  27. Add ModeOp = '+'
  28. // List is used when listing modes (for instance, listing the current bans on a channel).
  29. List ModeOp = '='
  30. // Remove is used when taking away the given key.
  31. Remove ModeOp = '-'
  32. )
  33. // Mode represents a user/channel/server mode
  34. type Mode rune
  35. func (mode Mode) String() string {
  36. return string(mode)
  37. }
  38. // ModeChange is a single mode changing
  39. type ModeChange struct {
  40. Mode Mode
  41. Op ModeOp
  42. Arg string
  43. }
  44. // ModeChanges are a collection of 'ModeChange's
  45. type ModeChanges []ModeChange
  46. func (changes ModeChanges) Strings() (result []string) {
  47. if len(changes) == 0 {
  48. return
  49. }
  50. var builder strings.Builder
  51. op := changes[0].Op
  52. builder.WriteRune(rune(op))
  53. for _, change := range changes {
  54. if change.Op != op {
  55. op = change.Op
  56. builder.WriteRune(rune(op))
  57. }
  58. builder.WriteRune(rune(change.Mode))
  59. }
  60. result = append(result, builder.String())
  61. for _, change := range changes {
  62. if change.Arg == "" {
  63. continue
  64. }
  65. result = append(result, change.Arg)
  66. }
  67. return
  68. }
  69. // Modes is just a raw list of modes
  70. type Modes []Mode
  71. func (modes Modes) String() string {
  72. strs := make([]string, len(modes))
  73. for index, mode := range modes {
  74. strs[index] = mode.String()
  75. }
  76. return strings.Join(strs, "")
  77. }
  78. // User Modes
  79. const (
  80. Bot Mode = 'B'
  81. Invisible Mode = 'i'
  82. LocalOperator Mode = 'O'
  83. Operator Mode = 'o'
  84. Restricted Mode = 'r'
  85. RegisteredOnly Mode = 'R'
  86. ServerNotice Mode = 's'
  87. TLS Mode = 'Z'
  88. UserNoCTCP Mode = 'T'
  89. UserRoleplaying Mode = 'E'
  90. WallOps Mode = 'w'
  91. )
  92. // Channel Modes
  93. const (
  94. BanMask Mode = 'b' // arg
  95. ChanRoleplaying Mode = 'E' // flag
  96. ExceptMask Mode = 'e' // arg
  97. InviteMask Mode = 'I' // arg
  98. InviteOnly Mode = 'i' // flag
  99. Key Mode = 'k' // flag arg
  100. Moderated Mode = 'm' // flag
  101. NoOutside Mode = 'n' // flag
  102. OpOnlyTopic Mode = 't' // flag
  103. // RegisteredOnly mode is reused here from umode definition
  104. Secret Mode = 's' // flag
  105. UserLimit Mode = 'l' // flag arg
  106. NoCTCP Mode = 'C' // flag
  107. )
  108. var (
  109. ChannelFounder Mode = 'q' // arg
  110. ChannelAdmin Mode = 'a' // arg
  111. ChannelOperator Mode = 'o' // arg
  112. Halfop Mode = 'h' // arg
  113. Voice Mode = 'v' // arg
  114. // ChannelUserModes holds the list of all modes that can be applied to a user in a channel,
  115. // including Voice, in descending order of precedence
  116. ChannelUserModes = Modes{
  117. ChannelFounder, ChannelAdmin, ChannelOperator, Halfop, Voice,
  118. }
  119. ChannelModePrefixes = map[Mode]string{
  120. ChannelFounder: "~",
  121. ChannelAdmin: "&",
  122. ChannelOperator: "@",
  123. Halfop: "%",
  124. Voice: "+",
  125. }
  126. )
  127. //
  128. // channel membership prefixes
  129. //
  130. // SplitChannelMembershipPrefixes takes a target and returns the prefixes on it, then the name.
  131. func SplitChannelMembershipPrefixes(target string) (prefixes string, name string) {
  132. name = target
  133. for i := 0; i < len(name); i++ {
  134. switch name[i] {
  135. case '~', '&', '@', '%', '+':
  136. prefixes = target[:i+1]
  137. name = target[i+1:]
  138. default:
  139. return
  140. }
  141. }
  142. return
  143. }
  144. // GetLowestChannelModePrefix returns the lowest channel prefix mode out of the given prefixes.
  145. func GetLowestChannelModePrefix(prefixes string) (lowest Mode) {
  146. for i, mode := range ChannelUserModes {
  147. if strings.Contains(prefixes, ChannelModePrefixes[mode]) {
  148. lowest = ChannelUserModes[i]
  149. }
  150. }
  151. return
  152. }
  153. //
  154. // commands
  155. //
  156. // ParseUserModeChanges returns the valid changes, and the list of unknown chars.
  157. func ParseUserModeChanges(params ...string) (ModeChanges, map[rune]bool) {
  158. changes := make(ModeChanges, 0)
  159. unknown := make(map[rune]bool)
  160. op := List
  161. if 0 < len(params) {
  162. modeArg := params[0]
  163. skipArgs := 1
  164. for _, mode := range modeArg {
  165. if mode == '-' || mode == '+' {
  166. op = ModeOp(mode)
  167. continue
  168. }
  169. change := ModeChange{
  170. Mode: Mode(mode),
  171. Op: op,
  172. }
  173. // put arg into modechange if needed
  174. switch Mode(mode) {
  175. case ServerNotice:
  176. // always require arg
  177. if len(params) > skipArgs {
  178. change.Arg = params[skipArgs]
  179. skipArgs++
  180. } else {
  181. continue
  182. }
  183. }
  184. var isKnown bool
  185. for _, supportedMode := range SupportedUserModes {
  186. if rune(supportedMode) == mode {
  187. isKnown = true
  188. break
  189. }
  190. }
  191. if !isKnown {
  192. unknown[mode] = true
  193. continue
  194. }
  195. changes = append(changes, change)
  196. }
  197. }
  198. return changes, unknown
  199. }
  200. // ParseChannelModeChanges returns the valid changes, and the list of unknown chars.
  201. func ParseChannelModeChanges(params ...string) (ModeChanges, map[rune]bool) {
  202. changes := make(ModeChanges, 0)
  203. unknown := make(map[rune]bool)
  204. op := List
  205. if 0 < len(params) {
  206. modeArg := params[0]
  207. skipArgs := 1
  208. for _, mode := range modeArg {
  209. if mode == '-' || mode == '+' {
  210. op = ModeOp(mode)
  211. continue
  212. }
  213. change := ModeChange{
  214. Mode: Mode(mode),
  215. Op: op,
  216. }
  217. // put arg into modechange if needed
  218. switch Mode(mode) {
  219. case BanMask, ExceptMask, InviteMask:
  220. if len(params) > skipArgs {
  221. change.Arg = params[skipArgs]
  222. skipArgs++
  223. } else {
  224. change.Op = List
  225. }
  226. case ChannelFounder, ChannelAdmin, ChannelOperator, Halfop, Voice:
  227. if len(params) > skipArgs {
  228. change.Arg = params[skipArgs]
  229. skipArgs++
  230. } else {
  231. continue
  232. }
  233. case UserLimit:
  234. // don't require value when removing
  235. if change.Op == Add {
  236. if len(params) > skipArgs {
  237. change.Arg = params[skipArgs]
  238. skipArgs++
  239. } else {
  240. continue
  241. }
  242. }
  243. case Key:
  244. // #874: +k is technically a type B mode, requiring a parameter
  245. // both for add and remove. so attempt to consume a parameter,
  246. // but allow remove (but not add) even if no parameter is available.
  247. // however, the remove parameter should always display as "*", matching
  248. // the freenode behavior.
  249. if len(params) > skipArgs {
  250. if change.Op == Add {
  251. change.Arg = params[skipArgs]
  252. }
  253. skipArgs++
  254. } else if change.Op == Add {
  255. continue
  256. }
  257. if change.Op == Remove {
  258. change.Arg = "*"
  259. }
  260. }
  261. var isKnown bool
  262. for _, supportedMode := range SupportedChannelModes {
  263. if rune(supportedMode) == mode {
  264. isKnown = true
  265. break
  266. }
  267. }
  268. for _, supportedMode := range ChannelUserModes {
  269. if rune(supportedMode) == mode {
  270. isKnown = true
  271. break
  272. }
  273. }
  274. if !isKnown {
  275. unknown[mode] = true
  276. continue
  277. }
  278. changes = append(changes, change)
  279. }
  280. }
  281. return changes, unknown
  282. }
  283. // ModeSet holds a set of modes.
  284. type ModeSet [2]uint32
  285. // valid modes go from 65 ('A') to 122 ('z'), making at most 58 possible values;
  286. // subtract 65 from the mode value and use that bit of the uint32 to represent it
  287. const (
  288. minMode = 65 // 'A'
  289. maxMode = 122 // 'z'
  290. )
  291. // returns a pointer to a new ModeSet
  292. func NewModeSet() *ModeSet {
  293. var set ModeSet
  294. return &set
  295. }
  296. // test whether `mode` is set
  297. func (set *ModeSet) HasMode(mode Mode) bool {
  298. if set == nil {
  299. return false
  300. }
  301. return utils.BitsetGet(set[:], uint(mode)-minMode)
  302. }
  303. // set `mode` to be on or off, return whether the value actually changed
  304. func (set *ModeSet) SetMode(mode Mode, on bool) (applied bool) {
  305. return utils.BitsetSet(set[:], uint(mode)-minMode, on)
  306. }
  307. // copy the contents of another modeset on top of this one
  308. func (set *ModeSet) Copy(other *ModeSet) {
  309. utils.BitsetCopy(set[:], other[:])
  310. }
  311. // return the modes in the set as a slice
  312. func (set *ModeSet) AllModes() (result []Mode) {
  313. if set == nil {
  314. return
  315. }
  316. var i Mode
  317. for i = minMode; i <= maxMode; i++ {
  318. if set.HasMode(i) {
  319. result = append(result, i)
  320. }
  321. }
  322. return
  323. }
  324. // String returns the modes in this set.
  325. func (set *ModeSet) String() (result string) {
  326. if set == nil {
  327. return
  328. }
  329. var buf strings.Builder
  330. for _, mode := range set.AllModes() {
  331. buf.WriteRune(rune(mode))
  332. }
  333. return buf.String()
  334. }
  335. // Prefixes returns a list of prefixes for the given set of channel modes.
  336. func (set *ModeSet) Prefixes(isMultiPrefix bool) (prefixes string) {
  337. if set == nil {
  338. return
  339. }
  340. // add prefixes in order from highest to lowest privs
  341. for _, mode := range ChannelUserModes {
  342. if set.HasMode(mode) {
  343. prefixes += ChannelModePrefixes[mode]
  344. }
  345. }
  346. if !isMultiPrefix && len(prefixes) > 1 {
  347. prefixes = string(prefixes[0])
  348. }
  349. return prefixes
  350. }
  351. // HighestChannelUserMode returns the most privileged channel-user mode
  352. // (e.g., ChannelFounder, Halfop, Voice) present in the ModeSet.
  353. // If no such modes are present, or `set` is nil, returns the zero mode.
  354. func (set *ModeSet) HighestChannelUserMode() (result Mode) {
  355. for _, mode := range ChannelUserModes {
  356. if set.HasMode(mode) {
  357. return mode
  358. }
  359. }
  360. return
  361. }
  362. type ByCodepoint Modes
  363. func (a ByCodepoint) Len() int { return len(a) }
  364. func (a ByCodepoint) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
  365. func (a ByCodepoint) Less(i, j int) bool { return a[i] < a[j] }
  366. func RplMyInfo() (param1, param2, param3 string) {
  367. userModes := make(Modes, len(SupportedUserModes))
  368. copy(userModes, SupportedUserModes)
  369. sort.Sort(ByCodepoint(userModes))
  370. channelModes := make(Modes, len(SupportedChannelModes)+len(ChannelUserModes))
  371. copy(channelModes, SupportedChannelModes)
  372. copy(channelModes[len(SupportedChannelModes):], ChannelUserModes)
  373. sort.Sort(ByCodepoint(channelModes))
  374. // XXX enumerate these by hand, i can't see any way to DRY this
  375. channelParametrizedModes := Modes{BanMask, ExceptMask, InviteMask, Key, UserLimit}
  376. channelParametrizedModes = append(channelParametrizedModes, ChannelUserModes...)
  377. sort.Sort(ByCodepoint(channelParametrizedModes))
  378. return userModes.String(), channelModes.String(), channelParametrizedModes.String()
  379. }