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

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