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 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627
  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 irc
  6. import (
  7. "strconv"
  8. "strings"
  9. "github.com/DanielOaks/girc-go/ircmsg"
  10. "github.com/tidwall/buntdb"
  11. )
  12. // ModeOp is an operation performed with modes
  13. type ModeOp rune
  14. func (op ModeOp) String() string {
  15. return string(op)
  16. }
  17. const (
  18. Add ModeOp = '+'
  19. List ModeOp = '='
  20. Remove ModeOp = '-'
  21. )
  22. // Mode represents a user/channel/server mode
  23. type Mode rune
  24. func (mode Mode) String() string {
  25. return string(mode)
  26. }
  27. // ModeChange is a single mode changing
  28. type ModeChange struct {
  29. mode Mode
  30. op ModeOp
  31. arg string
  32. }
  33. func (change *ModeChange) String() (str string) {
  34. if (change.op == Add) || (change.op == Remove) {
  35. str = change.op.String()
  36. }
  37. str += change.mode.String()
  38. if change.arg != "" {
  39. str += " " + change.arg
  40. }
  41. return
  42. }
  43. // ModeChanges are a collection of 'ModeChange's
  44. type ModeChanges []ModeChange
  45. func (changes ModeChanges) String() string {
  46. if len(changes) == 0 {
  47. return ""
  48. }
  49. op := changes[0].op
  50. str := changes[0].op.String()
  51. for _, change := range changes {
  52. if change.op != op {
  53. op = change.op
  54. str += change.op.String()
  55. }
  56. str += change.mode.String()
  57. }
  58. for _, change := range changes {
  59. if change.arg == "" {
  60. continue
  61. }
  62. str += " " + change.arg
  63. }
  64. return str
  65. }
  66. // Modes is just a raw list of modes
  67. type Modes []Mode
  68. func (modes Modes) String() string {
  69. strs := make([]string, len(modes))
  70. for index, mode := range modes {
  71. strs[index] = mode.String()
  72. }
  73. return strings.Join(strs, "")
  74. }
  75. // User Modes
  76. const (
  77. Away Mode = 'a'
  78. Invisible Mode = 'i'
  79. LocalOperator Mode = 'O'
  80. Operator Mode = 'o'
  81. Restricted Mode = 'r'
  82. ServerNotice Mode = 's' // deprecated
  83. TLS Mode = 'Z'
  84. UserRoleplaying Mode = 'E'
  85. WallOps Mode = 'w'
  86. )
  87. var (
  88. SupportedUserModes = Modes{
  89. Away, Invisible, Operator, UserRoleplaying,
  90. }
  91. // supportedUserModesString acts as a cache for when we introduce users
  92. supportedUserModesString = SupportedUserModes.String()
  93. )
  94. // Channel Modes
  95. const (
  96. BanMask Mode = 'b' // arg
  97. ChanRoleplaying Mode = 'E' // flag
  98. ExceptMask Mode = 'e' // arg
  99. InviteMask Mode = 'I' // arg
  100. InviteOnly Mode = 'i' // flag
  101. Key Mode = 'k' // flag arg
  102. Moderated Mode = 'm' // flag
  103. NoOutside Mode = 'n' // flag
  104. OpOnlyTopic Mode = 't' // flag
  105. RegisteredOnly Mode = 'r' // flag
  106. Secret Mode = 's' // flag
  107. UserLimit Mode = 'l' // flag arg
  108. )
  109. var (
  110. ChannelFounder Mode = 'q' // arg
  111. ChannelAdmin Mode = 'a' // arg
  112. ChannelOperator Mode = 'o' // arg
  113. Halfop Mode = 'h' // arg
  114. Voice Mode = 'v' // arg
  115. SupportedChannelModes = Modes{
  116. BanMask, ExceptMask, InviteMask, InviteOnly, Key, NoOutside,
  117. OpOnlyTopic, Secret, UserLimit, ChanRoleplaying,
  118. }
  119. // supportedChannelModesString acts as a cache for when we introduce users
  120. supportedChannelModesString = SupportedChannelModes.String()
  121. DefaultChannelModes = Modes{
  122. NoOutside, OpOnlyTopic,
  123. }
  124. // ChannelPrivModes holds the list of modes that are privileged, ie founder/op/halfop, in order.
  125. // voice is not in this list because it cannot perform channel operator actions.
  126. ChannelPrivModes = Modes{
  127. ChannelFounder, ChannelAdmin, ChannelOperator, Halfop,
  128. }
  129. ChannelModePrefixes = map[Mode]string{
  130. ChannelFounder: "~",
  131. ChannelAdmin: "&",
  132. ChannelOperator: "@",
  133. Halfop: "%",
  134. Voice: "+",
  135. }
  136. )
  137. //
  138. // channel membership prefixes
  139. //
  140. // SplitChannelMembershipPrefixes takes a target and returns the prefixes on it, then the name.
  141. func SplitChannelMembershipPrefixes(target string) (prefixes string, name string) {
  142. name = target
  143. for {
  144. if len(name) > 0 && strings.Contains("~&@%+", string(name[0])) {
  145. prefixes += string(name[0])
  146. name = name[1:]
  147. } else {
  148. break
  149. }
  150. }
  151. return prefixes, name
  152. }
  153. // GetLowestChannelModePrefix returns the lowest channel prefix mode out of the given prefixes.
  154. func GetLowestChannelModePrefix(prefixes string) *Mode {
  155. var lowest *Mode
  156. if strings.Contains(prefixes, "+") {
  157. lowest = &Voice
  158. } else {
  159. for i, mode := range ChannelPrivModes {
  160. if strings.Contains(prefixes, ChannelModePrefixes[mode]) {
  161. lowest = &ChannelPrivModes[i]
  162. }
  163. }
  164. }
  165. return lowest
  166. }
  167. //
  168. // commands
  169. //
  170. // MODE <target> [<modestring> [<mode arguments>...]]
  171. func modeHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
  172. _, errChan := CasefoldChannel(msg.Params[0])
  173. if errChan == nil {
  174. return cmodeHandler(server, client, msg)
  175. }
  176. return umodeHandler(server, client, msg)
  177. }
  178. // applyUserModeChanges applies the given changes, and returns the applied changes.
  179. func (client *Client) applyUserModeChanges(changes ModeChanges) ModeChanges {
  180. applied := make(ModeChanges, 0)
  181. for _, change := range changes {
  182. switch change.mode {
  183. case Invisible, ServerNotice, WallOps, UserRoleplaying:
  184. switch change.op {
  185. case Add:
  186. if client.flags[change.mode] {
  187. continue
  188. }
  189. client.flags[change.mode] = true
  190. applied = append(applied, change)
  191. case Remove:
  192. if !client.flags[change.mode] {
  193. continue
  194. }
  195. delete(client.flags, change.mode)
  196. applied = append(applied, change)
  197. }
  198. case Operator, LocalOperator:
  199. if change.op == Remove {
  200. if !client.flags[change.mode] {
  201. continue
  202. }
  203. delete(client.flags, change.mode)
  204. applied = append(applied, change)
  205. }
  206. }
  207. // can't do anything to TLS mode
  208. }
  209. // return the changes we could actually apply
  210. return applied
  211. }
  212. // MODE <target> [<modestring> [<mode arguments>...]]
  213. func umodeHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
  214. nickname, err := CasefoldName(msg.Params[0])
  215. target := server.clients.Get(nickname)
  216. if err != nil || target == nil {
  217. if len(msg.Params[0]) > 0 {
  218. client.Send(nil, server.name, ERR_NOSUCHNICK, client.nick, msg.Params[0], "No such nick")
  219. }
  220. return false
  221. }
  222. if client != target && msg.Command != "SAMODE" {
  223. if len(msg.Params) > 1 {
  224. client.Send(nil, server.name, ERR_USERSDONTMATCH, client.nick, "Can't change modes for other users")
  225. } else {
  226. client.Send(nil, server.name, ERR_USERSDONTMATCH, client.nick, "Can't view modes for other users")
  227. }
  228. return false
  229. }
  230. // assemble changes
  231. changes := make(ModeChanges, 0)
  232. applied := make(ModeChanges, 0)
  233. if len(msg.Params) > 1 {
  234. modeArg := msg.Params[1]
  235. op := ModeOp(modeArg[0])
  236. if (op == Add) || (op == Remove) {
  237. modeArg = modeArg[1:]
  238. } else {
  239. client.Send(nil, server.name, ERR_UNKNOWNMODE, client.nick, string(modeArg[0]), "is an unknown mode character to me")
  240. return false
  241. }
  242. for _, mode := range modeArg {
  243. if mode == '-' || mode == '+' {
  244. op = ModeOp(mode)
  245. continue
  246. }
  247. changes = append(changes, ModeChange{
  248. mode: Mode(mode),
  249. op: op,
  250. })
  251. }
  252. applied = target.applyUserModeChanges(changes)
  253. }
  254. if len(applied) > 0 {
  255. client.Send(nil, client.nickMaskString, "MODE", target.nick, applied.String())
  256. } else if client == target {
  257. client.Send(nil, target.nickMaskString, RPL_UMODEIS, target.nick, target.ModeString())
  258. }
  259. return false
  260. }
  261. // ParseChannelModeChanges returns the valid changes, and the list of unknown chars.
  262. func ParseChannelModeChanges(params ...string) (ModeChanges, map[rune]bool) {
  263. changes := make(ModeChanges, 0)
  264. unknown := make(map[rune]bool)
  265. if 0 < len(params) {
  266. modeArg := params[0]
  267. op := ModeOp(modeArg[0])
  268. if (op == Add) || (op == Remove) {
  269. modeArg = modeArg[1:]
  270. } else {
  271. unknown[rune(modeArg[0])] = true
  272. return changes, unknown
  273. }
  274. skipArgs := 1
  275. for _, mode := range modeArg {
  276. if mode == '-' || mode == '+' {
  277. op = ModeOp(mode)
  278. continue
  279. }
  280. change := ModeChange{
  281. mode: Mode(mode),
  282. op: op,
  283. }
  284. // put arg into modechange if needed
  285. switch Mode(mode) {
  286. case BanMask, ExceptMask, InviteMask:
  287. if len(params) > skipArgs {
  288. change.arg = params[skipArgs]
  289. skipArgs++
  290. } else {
  291. change.op = List
  292. }
  293. case ChannelFounder, ChannelAdmin, ChannelOperator, Halfop, Voice:
  294. if len(params) > skipArgs {
  295. change.arg = params[skipArgs]
  296. skipArgs++
  297. } else {
  298. continue
  299. }
  300. case Key, UserLimit:
  301. // don't require value when removing
  302. if change.op == Add {
  303. if len(params) > skipArgs {
  304. change.arg = params[skipArgs]
  305. skipArgs++
  306. } else {
  307. continue
  308. }
  309. }
  310. }
  311. var isKnown bool
  312. for _, supportedMode := range SupportedChannelModes {
  313. if rune(supportedMode) == mode {
  314. isKnown = true
  315. break
  316. }
  317. }
  318. if !isKnown {
  319. unknown[mode] = true
  320. }
  321. changes = append(changes, change)
  322. }
  323. }
  324. return changes, unknown
  325. }
  326. // ApplyChannelModeChanges applies a given set of mode changes.
  327. func ApplyChannelModeChanges(channel *Channel, client *Client, isSamode bool, changes ModeChanges) ModeChanges {
  328. // so we only output one warning for each list type when full
  329. listFullWarned := make(map[Mode]bool)
  330. clientIsOp := channel.clientIsAtLeastNoMutex(client, ChannelOperator)
  331. var alreadySentPrivError bool
  332. applied := make(ModeChanges, 0)
  333. for _, change := range changes {
  334. // chan priv modes are checked specially so ignore them
  335. // means regular users can't view ban/except lists... but I'm not worried about that
  336. if isSamode && ChannelModePrefixes[change.mode] == "" && !clientIsOp {
  337. if !alreadySentPrivError {
  338. alreadySentPrivError = true
  339. client.Send(nil, client.server.name, ERR_CHANOPRIVSNEEDED, channel.name, "You're not a channel operator")
  340. }
  341. continue
  342. }
  343. switch change.mode {
  344. case BanMask, ExceptMask, InviteMask:
  345. mask := change.arg
  346. list := channel.lists[change.mode]
  347. if list == nil {
  348. // This should never happen, but better safe than panicky.
  349. client.Send(nil, client.server.name, ERR_UNKNOWNERROR, client.nick, "MODE", "Could not complete MODE command")
  350. return changes
  351. }
  352. if (change.op == List) || (mask == "") {
  353. channel.ShowMaskList(client, change.mode)
  354. continue
  355. }
  356. // confirm mask looks valid
  357. mask, err := Casefold(mask)
  358. if err != nil {
  359. continue
  360. }
  361. switch change.op {
  362. case Add:
  363. if len(list.masks) >= client.server.limits.ChanListModes {
  364. if !listFullWarned[change.mode] {
  365. client.Send(nil, client.server.name, ERR_BANLISTFULL, client.nick, channel.name, change.mode.String(), "Channel list is full")
  366. listFullWarned[change.mode] = true
  367. }
  368. continue
  369. }
  370. list.Add(mask)
  371. applied = append(applied, change)
  372. case Remove:
  373. list.Remove(mask)
  374. applied = append(applied, change)
  375. }
  376. case UserLimit:
  377. switch change.op {
  378. case Add:
  379. val, err := strconv.ParseUint(change.arg, 10, 64)
  380. if err == nil {
  381. channel.userLimit = val
  382. applied = append(applied, change)
  383. }
  384. case Remove:
  385. channel.userLimit = 0
  386. applied = append(applied, change)
  387. }
  388. case Key:
  389. switch change.op {
  390. case Add:
  391. channel.key = change.arg
  392. case Remove:
  393. channel.key = ""
  394. }
  395. applied = append(applied, change)
  396. case InviteOnly, Moderated, NoOutside, OpOnlyTopic, RegisteredOnly, Secret, ChanRoleplaying:
  397. switch change.op {
  398. case Add:
  399. if channel.flags[change.mode] {
  400. continue
  401. }
  402. channel.flags[change.mode] = true
  403. applied = append(applied, change)
  404. case Remove:
  405. if !channel.flags[change.mode] {
  406. continue
  407. }
  408. delete(channel.flags, change.mode)
  409. applied = append(applied, change)
  410. }
  411. case ChannelFounder, ChannelAdmin, ChannelOperator, Halfop, Voice:
  412. // make sure client has privs to edit the given prefix
  413. hasPrivs := isSamode
  414. if !hasPrivs {
  415. for _, mode := range ChannelPrivModes {
  416. if channel.members[client][mode] {
  417. hasPrivs = true
  418. // Admins can't give other people Admin or remove it from others,
  419. // standard for that channel mode, we worry about this later
  420. if mode == ChannelAdmin && change.mode == ChannelAdmin {
  421. hasPrivs = false
  422. }
  423. break
  424. } else if mode == change.mode {
  425. break
  426. }
  427. }
  428. }
  429. casefoldedName, err := CasefoldName(change.arg)
  430. if err != nil {
  431. continue
  432. }
  433. if !hasPrivs {
  434. if change.op == Remove && casefoldedName == client.nickCasefolded {
  435. // success!
  436. } else {
  437. if !alreadySentPrivError {
  438. alreadySentPrivError = true
  439. client.Send(nil, client.server.name, ERR_CHANOPRIVSNEEDED, channel.name, "You're not a channel operator")
  440. }
  441. continue
  442. }
  443. }
  444. change := channel.applyModeMemberNoMutex(client, change.mode, change.op, change.arg)
  445. if change != nil {
  446. applied = append(applied, *change)
  447. }
  448. }
  449. }
  450. return applied
  451. }
  452. // MODE <target> [<modestring> [<mode arguments>...]]
  453. func cmodeHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
  454. channelName, err := CasefoldChannel(msg.Params[0])
  455. channel := server.channels.Get(channelName)
  456. if err != nil || channel == nil {
  457. client.Send(nil, server.name, ERR_NOSUCHCHANNEL, client.nick, msg.Params[0], "No such channel")
  458. return false
  459. }
  460. channel.membersMutex.Lock()
  461. defer channel.membersMutex.Unlock()
  462. // applied mode changes
  463. applied := make(ModeChanges, 0)
  464. if 1 < len(msg.Params) {
  465. // parse out real mode changes
  466. params := msg.Params[1:]
  467. changes, unknown := ParseChannelModeChanges(params...)
  468. // alert for unknown mode changes
  469. for char := range unknown {
  470. client.Send(nil, server.name, ERR_UNKNOWNMODE, client.nick, string(char), "is an unknown mode character to me")
  471. }
  472. if len(unknown) == 1 && len(changes) == 0 {
  473. return false
  474. }
  475. // apply mode changes
  476. applied = ApplyChannelModeChanges(channel, client, msg.Command == "SAMODE", changes)
  477. }
  478. // save changes to banlist/exceptlist/invexlist
  479. var banlistUpdated, exceptlistUpdated, invexlistUpdated bool
  480. for _, change := range applied {
  481. if change.mode == BanMask {
  482. banlistUpdated = true
  483. } else if change.mode == ExceptMask {
  484. exceptlistUpdated = true
  485. } else if change.mode == InviteMask {
  486. invexlistUpdated = true
  487. }
  488. }
  489. server.registeredChannelsMutex.Lock()
  490. if 0 < len(applied) && server.registeredChannels[channel.nameCasefolded] != nil && (banlistUpdated || exceptlistUpdated || invexlistUpdated) {
  491. server.store.Update(func(tx *buntdb.Tx) error {
  492. chanInfo := server.loadChannelNoMutex(tx, channel.nameCasefolded)
  493. if banlistUpdated {
  494. var banlist []string
  495. for mask := range channel.lists[BanMask].masks {
  496. banlist = append(banlist, mask)
  497. }
  498. chanInfo.Banlist = banlist
  499. }
  500. if exceptlistUpdated {
  501. var exceptlist []string
  502. for mask := range channel.lists[ExceptMask].masks {
  503. exceptlist = append(exceptlist, mask)
  504. }
  505. chanInfo.Exceptlist = exceptlist
  506. }
  507. if invexlistUpdated {
  508. var invitelist []string
  509. for mask := range channel.lists[InviteMask].masks {
  510. invitelist = append(invitelist, mask)
  511. }
  512. chanInfo.Invitelist = invitelist
  513. }
  514. server.saveChannelNoMutex(tx, channel.nameCasefolded, *chanInfo)
  515. return nil
  516. })
  517. }
  518. server.registeredChannelsMutex.Unlock()
  519. // send out changes
  520. if len(applied) > 0 {
  521. //TODO(dan): we should change the name of String and make it return a slice here
  522. args := append([]string{channel.name}, strings.Split(applied.String(), " ")...)
  523. for member := range channel.members {
  524. member.Send(nil, client.nickMaskString, "MODE", args...)
  525. }
  526. } else {
  527. //TODO(dan): we should just make ModeString return a slice here
  528. args := append([]string{client.nick, channel.name}, strings.Split(channel.modeStringNoLock(client), " ")...)
  529. client.Send(nil, client.nickMaskString, RPL_CHANNELMODEIS, args...)
  530. client.Send(nil, client.nickMaskString, RPL_CHANNELCREATED, client.nick, channel.name, strconv.FormatInt(channel.createdTime.Unix(), 10))
  531. }
  532. return false
  533. }