Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

chanserv.go 28KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827
  1. // Copyright (c) 2017 Daniel Oaks <daniel@danieloaks.net>
  2. // released under the MIT license
  3. package irc
  4. import (
  5. "fmt"
  6. "regexp"
  7. "sort"
  8. "strings"
  9. "time"
  10. "github.com/goshuirc/irc-go/ircfmt"
  11. "github.com/oragono/oragono/irc/modes"
  12. "github.com/oragono/oragono/irc/sno"
  13. "github.com/oragono/oragono/irc/utils"
  14. )
  15. const chanservHelp = `ChanServ lets you register and manage channels.`
  16. func chanregEnabled(config *Config) bool {
  17. return config.Channels.Registration.Enabled
  18. }
  19. var (
  20. chanservCommands = map[string]*serviceCommand{
  21. "op": {
  22. handler: csOpHandler,
  23. help: `Syntax: $bOP #channel [nickname]$b
  24. OP makes the given nickname, or yourself, a channel admin. You can only use
  25. this command if you're the founder of the channel.`,
  26. helpShort: `$bOP$b makes the given user (or yourself) a channel admin.`,
  27. authRequired: true,
  28. enabled: chanregEnabled,
  29. minParams: 1,
  30. },
  31. "deop": {
  32. handler: csDeopHandler,
  33. help: `Syntax: $bDEOP #channel [nickname]$b
  34. DEOP removes the given nickname, or yourself, the channel admin. You can only use
  35. this command if you're the founder of the channel.`,
  36. helpShort: `$bDEOP$b removes the given user (or yourself) from a channel admin.`,
  37. enabled: chanregEnabled,
  38. minParams: 1,
  39. },
  40. "register": {
  41. handler: csRegisterHandler,
  42. help: `Syntax: $bREGISTER #channel$b
  43. REGISTER lets you own the given channel. If you rejoin this channel, you'll be
  44. given admin privs on it. Modes set on the channel and the topic will also be
  45. remembered.`,
  46. helpShort: `$bREGISTER$b lets you own a given channel.`,
  47. authRequired: true,
  48. enabled: chanregEnabled,
  49. minParams: 1,
  50. },
  51. "unregister": {
  52. handler: csUnregisterHandler,
  53. help: `Syntax: $bUNREGISTER #channel [code]$b
  54. UNREGISTER deletes a channel registration, allowing someone else to claim it.
  55. To prevent accidental unregistrations, a verification code is required;
  56. invoking the command without a code will display the necessary code.`,
  57. helpShort: `$bUNREGISTER$b deletes a channel registration.`,
  58. enabled: chanregEnabled,
  59. minParams: 1,
  60. },
  61. "drop": {
  62. aliasOf: "unregister",
  63. },
  64. "amode": {
  65. handler: csAmodeHandler,
  66. help: `Syntax: $bAMODE #channel [mode change] [account]$b
  67. AMODE lists or modifies persistent mode settings that affect channel members.
  68. For example, $bAMODE #channel +o dan$b grants the the holder of the "dan"
  69. account the +o operator mode every time they join #channel. To list current
  70. accounts and modes, use $bAMODE #channel$b. Note that users are always
  71. referenced by their registered account names, not their nicknames.`,
  72. helpShort: `$bAMODE$b modifies persistent mode settings for channel members.`,
  73. enabled: chanregEnabled,
  74. minParams: 1,
  75. },
  76. "clear": {
  77. handler: csClearHandler,
  78. help: `Syntax: $bCLEAR #channel target$b
  79. CLEAR removes users or settings from a channel. Specifically:
  80. $bCLEAR #channel users$b kicks all users except for you.
  81. $bCLEAR #channel access$b resets all stored bans, invites, ban exceptions,
  82. and persistent user-mode grants made with CS AMODE.`,
  83. helpShort: `$bCLEAR$b removes users or settings from a channel.`,
  84. enabled: chanregEnabled,
  85. minParams: 2,
  86. },
  87. "transfer": {
  88. handler: csTransferHandler,
  89. help: `Syntax: $bTRANSFER [accept] #channel user [code]$b
  90. TRANSFER transfers ownership of a channel from one user to another.
  91. To prevent accidental transfers, a verification code is required. For
  92. example, $bTRANSFER #channel alice$b displays the required confirmation
  93. code, then $bTRANSFER #channel alice 2930242125$b initiates the transfer.
  94. Unless you are an IRC operator with the correct permissions, alice must
  95. then accept the transfer, which she can do with $bTRANSFER accept #channel$b.
  96. To cancel a pending transfer, transfer the channel to yourself.`,
  97. helpShort: `$bTRANSFER$b transfers ownership of a channel to another user.`,
  98. enabled: chanregEnabled,
  99. minParams: 2,
  100. },
  101. "purge": {
  102. handler: csPurgeHandler,
  103. help: `Syntax: $bPURGE <ADD | DEL | LIST> #channel [code] [reason]$b
  104. PURGE ADD blacklists a channel from the server, making it impossible to join
  105. or otherwise interact with the channel. If the channel currently has members,
  106. they will be kicked from it. PURGE may also be applied preemptively to
  107. channels that do not currently have members. A purge can be undone with
  108. PURGE DEL. To list purged channels, use PURGE LIST.`,
  109. helpShort: `$bPURGE$b blacklists a channel from the server.`,
  110. capabs: []string{"chanreg"},
  111. minParams: 1,
  112. maxParams: 3,
  113. unsplitFinalParam: true,
  114. },
  115. "list": {
  116. handler: csListHandler,
  117. help: `Syntax: $bLIST [regex]$b
  118. LIST returns the list of registered channels, which match the given regex.
  119. If no regex is provided, all registered channels are returned.`,
  120. helpShort: `$bLIST$b searches the list of registered channels.`,
  121. capabs: []string{"chanreg"},
  122. minParams: 0,
  123. },
  124. "info": {
  125. handler: csInfoHandler,
  126. help: `Syntax: $INFO #channel$b
  127. INFO displays info about a registered channel.`,
  128. helpShort: `$bINFO$b displays info about a registered channel.`,
  129. enabled: chanregEnabled,
  130. minParams: 1,
  131. },
  132. "get": {
  133. handler: csGetHandler,
  134. help: `Syntax: $bGET #channel <setting>$b
  135. GET queries the current values of the channel settings. For more information
  136. on the settings and their possible values, see HELP SET.`,
  137. helpShort: `$bGET$b queries the current values of a channel's settings`,
  138. enabled: chanregEnabled,
  139. minParams: 2,
  140. },
  141. "set": {
  142. handler: csSetHandler,
  143. helpShort: `$bSET$b modifies a channel's settings`,
  144. // these are broken out as separate strings so they can be translated separately
  145. helpStrings: []string{
  146. `Syntax $bSET #channel <setting> <value>$b
  147. SET modifies a channel's settings. The following settings are available:`,
  148. `$bHISTORY$b
  149. 'history' lets you control how channel history is stored. Your options are:
  150. 1. 'off' [no history]
  151. 2. 'ephemeral' [a limited amount of temporary history, not stored on disk]
  152. 3. 'on' [history stored in a permanent database, if available]
  153. 4. 'default' [use the server default]`,
  154. `$bQUERY-CUTOFF$b
  155. 'query-cutoff' lets you restrict how much channel history can be retrieved
  156. by unprivileged users. Your options are:
  157. 1. 'none' [no restrictions]
  158. 2. 'registration-time' [users can view history from after their account was
  159. registered, plus a grace period]
  160. 3. 'join-time' [users can biew history from after they joined the
  161. channel; note that history will be effectively
  162. unavailable to clients that are not always-on]
  163. 4. 'default' [use the server default]`,
  164. },
  165. enabled: chanregEnabled,
  166. minParams: 3,
  167. },
  168. }
  169. )
  170. func csAmodeHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  171. channelName := params[0]
  172. channel := server.channels.Get(channelName)
  173. if channel == nil {
  174. service.Notice(rb, client.t("Channel does not exist"))
  175. return
  176. } else if channel.Founder() == "" {
  177. service.Notice(rb, client.t("Channel is not registered"))
  178. return
  179. }
  180. modeChanges, unknown := modes.ParseChannelModeChanges(params[1:]...)
  181. var change modes.ModeChange
  182. if len(modeChanges) > 1 || len(unknown) > 0 {
  183. service.Notice(rb, client.t("Invalid mode change"))
  184. return
  185. } else if len(modeChanges) == 1 {
  186. change = modeChanges[0]
  187. } else {
  188. change = modes.ModeChange{Op: modes.List}
  189. }
  190. // normalize and validate the account argument
  191. accountIsValid := false
  192. change.Arg, _ = CasefoldName(change.Arg)
  193. switch change.Op {
  194. case modes.List:
  195. accountIsValid = true
  196. case modes.Add:
  197. // if we're adding a mode, the account must exist
  198. if change.Arg != "" {
  199. _, err := server.accounts.LoadAccount(change.Arg)
  200. accountIsValid = (err == nil)
  201. }
  202. case modes.Remove:
  203. // allow removal of accounts that may have been deleted
  204. accountIsValid = (change.Arg != "")
  205. }
  206. if !accountIsValid {
  207. service.Notice(rb, client.t("Account does not exist"))
  208. return
  209. }
  210. affectedModes, err := channel.ProcessAccountToUmodeChange(client, change)
  211. if err == errInsufficientPrivs {
  212. service.Notice(rb, client.t("Insufficient privileges"))
  213. return
  214. } else if err != nil {
  215. service.Notice(rb, client.t("Internal error"))
  216. return
  217. }
  218. switch change.Op {
  219. case modes.List:
  220. // sort the persistent modes in descending order of priority
  221. sort.Slice(affectedModes, func(i, j int) bool {
  222. return umodeGreaterThan(affectedModes[i].Mode, affectedModes[j].Mode)
  223. })
  224. service.Notice(rb, fmt.Sprintf(client.t("Channel %[1]s has %[2]d persistent modes set"), channelName, len(affectedModes)))
  225. for _, modeChange := range affectedModes {
  226. service.Notice(rb, fmt.Sprintf(client.t("Account %[1]s receives mode +%[2]s"), modeChange.Arg, string(modeChange.Mode)))
  227. }
  228. case modes.Add, modes.Remove:
  229. if len(affectedModes) > 0 {
  230. service.Notice(rb, fmt.Sprintf(client.t("Successfully set persistent mode %[1]s on %[2]s"), strings.Join([]string{string(change.Op), string(change.Mode)}, ""), change.Arg))
  231. // #729: apply change to current membership
  232. for _, member := range channel.Members() {
  233. if member.Account() == change.Arg {
  234. applied, change := channel.applyModeToMember(client, change, rb)
  235. if applied {
  236. announceCmodeChanges(channel, modes.ModeChanges{change}, server.name, "*", "", rb)
  237. }
  238. }
  239. }
  240. } else {
  241. service.Notice(rb, client.t("No changes were made"))
  242. }
  243. }
  244. }
  245. func csOpHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  246. channelInfo := server.channels.Get(params[0])
  247. if channelInfo == nil {
  248. service.Notice(rb, client.t("Channel does not exist"))
  249. return
  250. }
  251. channelName := channelInfo.Name()
  252. clientAccount := client.Account()
  253. if clientAccount == "" || clientAccount != channelInfo.Founder() {
  254. service.Notice(rb, client.t("Only the channel founder can do this"))
  255. return
  256. }
  257. var target *Client
  258. if len(params) > 1 {
  259. target = server.clients.Get(params[1])
  260. if target == nil {
  261. service.Notice(rb, client.t("Could not find given client"))
  262. return
  263. }
  264. } else {
  265. target = client
  266. }
  267. // give them privs
  268. givenMode := modes.ChannelOperator
  269. if clientAccount == target.Account() {
  270. givenMode = modes.ChannelFounder
  271. }
  272. applied, change := channelInfo.applyModeToMember(client,
  273. modes.ModeChange{Mode: givenMode,
  274. Op: modes.Add,
  275. Arg: target.NickCasefolded(),
  276. },
  277. rb)
  278. if applied {
  279. announceCmodeChanges(channelInfo, modes.ModeChanges{change}, server.name, "*", "", rb)
  280. }
  281. service.Notice(rb, client.t("Successfully granted operator privileges"))
  282. tnick := target.Nick()
  283. server.logger.Info("services", fmt.Sprintf("Client %s op'd [%s] in channel %s", client.Nick(), tnick, channelName))
  284. server.snomasks.Send(sno.LocalChannels, fmt.Sprintf(ircfmt.Unescape("Client $c[grey][$r%s$c[grey]] CS OP'd $c[grey][$r%s$c[grey]] in channel $c[grey][$r%s$c[grey]]"), client.NickMaskString(), tnick, channelName))
  285. }
  286. func csDeopHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  287. channel := server.channels.Get(params[0])
  288. if channel == nil {
  289. service.Notice(rb, client.t("Channel does not exist"))
  290. return
  291. }
  292. if !channel.hasClient(client) {
  293. service.Notice(rb, client.t("You're not on that channel"))
  294. return
  295. }
  296. var target *Client
  297. if len(params) > 1 {
  298. target = server.clients.Get(params[1])
  299. if target == nil {
  300. service.Notice(rb, client.t("Could not find given client"))
  301. return
  302. }
  303. } else {
  304. target = client
  305. }
  306. present, _, cumodes := channel.ClientStatus(target)
  307. if !present || len(cumodes) == 0 {
  308. service.Notice(rb, client.t("Target has no privileges to remove"))
  309. return
  310. }
  311. tnick := target.Nick()
  312. modeChanges := make(modes.ModeChanges, len(cumodes))
  313. for i, mode := range cumodes {
  314. modeChanges[i] = modes.ModeChange{
  315. Mode: mode,
  316. Op: modes.Remove,
  317. Arg: tnick,
  318. }
  319. }
  320. // use the user's own permissions for the check, then announce
  321. // the changes as coming from chanserv
  322. applied := channel.ApplyChannelModeChanges(client, false, modeChanges, rb)
  323. details := client.Details()
  324. announceCmodeChanges(channel, applied, details.nickMask, details.accountName, details.account, rb)
  325. if len(applied) == 0 {
  326. return
  327. }
  328. service.Notice(rb, client.t("Successfully removed operator privileges"))
  329. }
  330. func csRegisterHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  331. if server.Config().Channels.Registration.OperatorOnly && !client.HasRoleCapabs("chanreg") {
  332. service.Notice(rb, client.t("Channel registration is restricted to server operators"))
  333. return
  334. }
  335. channelName := params[0]
  336. channelInfo := server.channels.Get(channelName)
  337. if channelInfo == nil {
  338. service.Notice(rb, client.t("No such channel"))
  339. return
  340. }
  341. if !channelInfo.ClientIsAtLeast(client, modes.ChannelOperator) {
  342. service.Notice(rb, client.t("You must be an oper on the channel to register it"))
  343. return
  344. }
  345. account := client.Account()
  346. if !checkChanLimit(service, client, rb) {
  347. return
  348. }
  349. // this provides the synchronization that allows exactly one registration of the channel:
  350. err := server.channels.SetRegistered(channelName, account)
  351. if err != nil {
  352. service.Notice(rb, err.Error())
  353. return
  354. }
  355. service.Notice(rb, fmt.Sprintf(client.t("Channel %s successfully registered"), channelName))
  356. server.logger.Info("services", fmt.Sprintf("Client %s registered channel %s", client.Nick(), channelName))
  357. server.snomasks.Send(sno.LocalChannels, fmt.Sprintf(ircfmt.Unescape("Channel registered $c[grey][$r%s$c[grey]] by $c[grey][$r%s$c[grey]]"), channelName, client.nickMaskString))
  358. // give them founder privs
  359. applied, change := channelInfo.applyModeToMember(client,
  360. modes.ModeChange{
  361. Mode: modes.ChannelFounder,
  362. Op: modes.Add,
  363. Arg: client.NickCasefolded(),
  364. },
  365. rb)
  366. if applied {
  367. announceCmodeChanges(channelInfo, modes.ModeChanges{change}, service.prefix, "*", "", rb)
  368. }
  369. }
  370. // check whether a client has already registered too many channels
  371. func checkChanLimit(service *ircService, client *Client, rb *ResponseBuffer) (ok bool) {
  372. account := client.Account()
  373. channelsAlreadyRegistered := client.server.accounts.ChannelsForAccount(account)
  374. ok = len(channelsAlreadyRegistered) < client.server.Config().Channels.Registration.MaxChannelsPerAccount || client.HasRoleCapabs("chanreg")
  375. if !ok {
  376. service.Notice(rb, client.t("You have already registered the maximum number of channels; try dropping some with /CS UNREGISTER"))
  377. }
  378. return
  379. }
  380. func csPrivsCheck(service *ircService, channel RegisteredChannel, client *Client, rb *ResponseBuffer) (success bool) {
  381. founder := channel.Founder
  382. if founder == "" {
  383. service.Notice(rb, client.t("That channel is not registered"))
  384. return false
  385. }
  386. if client.HasRoleCapabs("chanreg") {
  387. return true
  388. }
  389. if founder != client.Account() {
  390. service.Notice(rb, client.t("Insufficient privileges"))
  391. return false
  392. }
  393. return true
  394. }
  395. func csUnregisterHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  396. channelName := params[0]
  397. var verificationCode string
  398. if len(params) > 1 {
  399. verificationCode = params[1]
  400. }
  401. channel := server.channels.Get(channelName)
  402. if channel == nil {
  403. service.Notice(rb, client.t("No such channel"))
  404. return
  405. }
  406. info := channel.ExportRegistration(0)
  407. channelKey := info.NameCasefolded
  408. if !csPrivsCheck(service, info, client, rb) {
  409. return
  410. }
  411. expectedCode := utils.ConfirmationCode(info.Name, info.RegisteredAt)
  412. if expectedCode != verificationCode {
  413. service.Notice(rb, ircfmt.Unescape(client.t("$bWarning: unregistering this channel will remove all stored channel attributes.$b")))
  414. service.Notice(rb, fmt.Sprintf(client.t("To confirm, run this command: %s"), fmt.Sprintf("/CS UNREGISTER %s %s", channelKey, expectedCode)))
  415. return
  416. }
  417. server.channels.SetUnregistered(channelKey, info.Founder)
  418. service.Notice(rb, fmt.Sprintf(client.t("Channel %s is now unregistered"), channelKey))
  419. }
  420. func csClearHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  421. channel := server.channels.Get(params[0])
  422. if channel == nil {
  423. service.Notice(rb, client.t("Channel does not exist"))
  424. return
  425. }
  426. if !csPrivsCheck(service, channel.ExportRegistration(0), client, rb) {
  427. return
  428. }
  429. switch strings.ToLower(params[1]) {
  430. case "access":
  431. channel.resetAccess()
  432. service.Notice(rb, client.t("Successfully reset channel access"))
  433. case "users":
  434. for _, target := range channel.Members() {
  435. if target != client {
  436. channel.Kick(client, target, "Cleared by ChanServ", rb, true)
  437. }
  438. }
  439. default:
  440. service.Notice(rb, client.t("Invalid parameters"))
  441. }
  442. }
  443. func csTransferHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  444. if strings.ToLower(params[0]) == "accept" {
  445. processTransferAccept(service, client, params[1], rb)
  446. return
  447. }
  448. chname := params[0]
  449. channel := server.channels.Get(chname)
  450. if channel == nil {
  451. service.Notice(rb, client.t("Channel does not exist"))
  452. return
  453. }
  454. regInfo := channel.ExportRegistration(0)
  455. chname = regInfo.Name
  456. account := client.Account()
  457. isFounder := account != "" && account == regInfo.Founder
  458. hasPrivs := client.HasRoleCapabs("chanreg")
  459. if !(isFounder || hasPrivs) {
  460. service.Notice(rb, client.t("Insufficient privileges"))
  461. return
  462. }
  463. target := params[1]
  464. targetAccount, err := server.accounts.LoadAccount(params[1])
  465. if err != nil {
  466. service.Notice(rb, client.t("Account does not exist"))
  467. return
  468. }
  469. if targetAccount.NameCasefolded != account {
  470. expectedCode := utils.ConfirmationCode(regInfo.Name, regInfo.RegisteredAt)
  471. codeValidated := 2 < len(params) && params[2] == expectedCode
  472. if !codeValidated {
  473. service.Notice(rb, ircfmt.Unescape(client.t("$bWarning: you are about to transfer control of your channel to another user.$b")))
  474. service.Notice(rb, fmt.Sprintf(client.t("To confirm your channel transfer, type: /CS TRANSFER %[1]s %[2]s %[3]s"), chname, target, expectedCode))
  475. return
  476. }
  477. }
  478. status, err := channel.Transfer(client, target, hasPrivs)
  479. if err == nil {
  480. switch status {
  481. case channelTransferComplete:
  482. service.Notice(rb, fmt.Sprintf(client.t("Successfully transferred channel %[1]s to account %[2]s"), chname, target))
  483. case channelTransferPending:
  484. sendTransferPendingNotice(service, server, target, chname)
  485. service.Notice(rb, fmt.Sprintf(client.t("Transfer of channel %[1]s to account %[2]s succeeded, pending acceptance"), chname, target))
  486. case channelTransferCancelled:
  487. service.Notice(rb, fmt.Sprintf(client.t("Cancelled pending transfer of channel %s"), chname))
  488. }
  489. } else {
  490. service.Notice(rb, client.t("Could not transfer channel"))
  491. }
  492. }
  493. func sendTransferPendingNotice(service *ircService, server *Server, account, chname string) {
  494. clients := server.accounts.AccountToClients(account)
  495. if len(clients) == 0 {
  496. return
  497. }
  498. var client *Client
  499. for _, candidate := range clients {
  500. client = candidate
  501. if candidate.NickCasefolded() == candidate.Account() {
  502. break // prefer the login where the nick is the account
  503. }
  504. }
  505. client.Send(nil, service.prefix, "NOTICE", client.Nick(), fmt.Sprintf(client.t("You have been offered ownership of channel %[1]s. To accept, /CS TRANSFER ACCEPT %[1]s"), chname))
  506. }
  507. func processTransferAccept(service *ircService, client *Client, chname string, rb *ResponseBuffer) {
  508. channel := client.server.channels.Get(chname)
  509. if channel == nil {
  510. service.Notice(rb, client.t("Channel does not exist"))
  511. return
  512. }
  513. if !checkChanLimit(service, client, rb) {
  514. return
  515. }
  516. switch channel.AcceptTransfer(client) {
  517. case nil:
  518. service.Notice(rb, fmt.Sprintf(client.t("Successfully accepted ownership of channel %s"), channel.Name()))
  519. case errChannelTransferNotOffered:
  520. service.Notice(rb, fmt.Sprintf(client.t("You weren't offered ownership of channel %s"), channel.Name()))
  521. default:
  522. service.Notice(rb, fmt.Sprintf(client.t("Could not accept ownership of channel %s"), channel.Name()))
  523. }
  524. }
  525. func csPurgeHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  526. oper := client.Oper()
  527. if oper == nil {
  528. return // should be impossible because you need oper capabs for this
  529. }
  530. switch strings.ToLower(params[0]) {
  531. case "add":
  532. csPurgeAddHandler(service, client, params[1:], oper.Name, rb)
  533. case "del", "remove":
  534. csPurgeDelHandler(service, client, params[1:], oper.Name, rb)
  535. case "list":
  536. csPurgeListHandler(service, client, rb)
  537. default:
  538. service.Notice(rb, client.t("Invalid parameters"))
  539. }
  540. }
  541. func csPurgeAddHandler(service *ircService, client *Client, params []string, operName string, rb *ResponseBuffer) {
  542. if len(params) == 0 {
  543. service.Notice(rb, client.t("Invalid parameters"))
  544. return
  545. }
  546. chname := params[0]
  547. params = params[1:]
  548. channel := client.server.channels.Get(chname) // possibly nil
  549. var ctime time.Time
  550. if channel != nil {
  551. chname = channel.Name()
  552. ctime = channel.Ctime()
  553. }
  554. code := utils.ConfirmationCode(chname, ctime)
  555. if len(params) == 0 || params[0] != code {
  556. service.Notice(rb, ircfmt.Unescape(client.t("$bWarning: you are about to empty this channel and remove it from the server.$b")))
  557. service.Notice(rb, fmt.Sprintf(client.t("To confirm, run this command: %s"), fmt.Sprintf("/CS PURGE ADD %s %s", chname, code)))
  558. return
  559. }
  560. params = params[1:]
  561. var reason string
  562. if 1 < len(params) {
  563. reason = params[1]
  564. }
  565. purgeRecord := ChannelPurgeRecord{
  566. Oper: operName,
  567. PurgedAt: time.Now().UTC(),
  568. Reason: reason,
  569. }
  570. switch client.server.channels.Purge(chname, purgeRecord) {
  571. case nil:
  572. if channel != nil { // channel need not exist to be purged
  573. for _, target := range channel.Members() {
  574. channel.Kick(client, target, "Cleared by ChanServ", rb, true)
  575. }
  576. }
  577. service.Notice(rb, fmt.Sprintf(client.t("Successfully purged channel %s from the server"), chname))
  578. case errInvalidChannelName:
  579. service.Notice(rb, fmt.Sprintf(client.t("Can't purge invalid channel %s"), chname))
  580. default:
  581. service.Notice(rb, client.t("An error occurred"))
  582. }
  583. }
  584. func csPurgeDelHandler(service *ircService, client *Client, params []string, operName string, rb *ResponseBuffer) {
  585. if len(params) == 0 {
  586. service.Notice(rb, client.t("Invalid parameters"))
  587. return
  588. }
  589. chname := params[0]
  590. switch client.server.channels.Unpurge(chname) {
  591. case nil:
  592. service.Notice(rb, fmt.Sprintf(client.t("Successfully unpurged channel %s from the server"), chname))
  593. case errNoSuchChannel:
  594. service.Notice(rb, fmt.Sprintf(client.t("Channel %s wasn't previously purged from the server"), chname))
  595. default:
  596. service.Notice(rb, client.t("An error occurred"))
  597. }
  598. }
  599. func csPurgeListHandler(service *ircService, client *Client, rb *ResponseBuffer) {
  600. l := client.server.channels.ListPurged()
  601. service.Notice(rb, fmt.Sprintf(client.t("There are %d purged channel(s)."), len(l)))
  602. for i, c := range l {
  603. service.Notice(rb, fmt.Sprintf("%d: %s", i+1, c))
  604. }
  605. }
  606. func csListHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  607. if !client.HasRoleCapabs("chanreg") {
  608. service.Notice(rb, client.t("Insufficient privileges"))
  609. return
  610. }
  611. var searchRegex *regexp.Regexp
  612. if len(params) > 0 {
  613. var err error
  614. searchRegex, err = regexp.Compile(params[0])
  615. if err != nil {
  616. service.Notice(rb, client.t("Invalid regex"))
  617. return
  618. }
  619. }
  620. service.Notice(rb, ircfmt.Unescape(client.t("*** $bChanServ LIST$b ***")))
  621. channels := server.channelRegistry.AllChannels()
  622. for _, channel := range channels {
  623. if searchRegex == nil || searchRegex.MatchString(channel) {
  624. service.Notice(rb, fmt.Sprintf(" %s", channel))
  625. }
  626. }
  627. service.Notice(rb, ircfmt.Unescape(client.t("*** $bEnd of ChanServ LIST$b ***")))
  628. }
  629. func csInfoHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  630. chname, err := CasefoldChannel(params[0])
  631. if err != nil {
  632. service.Notice(rb, client.t("Invalid channel name"))
  633. return
  634. }
  635. // purge status
  636. if client.HasRoleCapabs("chanreg") {
  637. purgeRecord, err := server.channelRegistry.LoadPurgeRecord(chname)
  638. if err == nil {
  639. service.Notice(rb, fmt.Sprintf(client.t("Channel %s was purged by the server operators and cannot be used"), chname))
  640. service.Notice(rb, fmt.Sprintf(client.t("Purged by operator: %s"), purgeRecord.Oper))
  641. service.Notice(rb, fmt.Sprintf(client.t("Purged at: %s"), purgeRecord.PurgedAt.Format(time.RFC1123)))
  642. if purgeRecord.Reason != "" {
  643. service.Notice(rb, fmt.Sprintf(client.t("Purge reason: %s"), purgeRecord.Reason))
  644. }
  645. }
  646. } else {
  647. if server.channels.IsPurged(chname) {
  648. service.Notice(rb, fmt.Sprintf(client.t("Channel %s was purged by the server operators and cannot be used"), chname))
  649. }
  650. }
  651. var chinfo RegisteredChannel
  652. channel := server.channels.Get(params[0])
  653. if channel != nil {
  654. chinfo = channel.ExportRegistration(0)
  655. } else {
  656. chinfo, err = server.channelRegistry.LoadChannel(chname)
  657. if err != nil && !(err == errNoSuchChannel || err == errFeatureDisabled) {
  658. service.Notice(rb, client.t("An error occurred"))
  659. return
  660. }
  661. }
  662. // channel exists but is unregistered, or doesn't exist:
  663. if chinfo.Founder == "" {
  664. service.Notice(rb, fmt.Sprintf(client.t("Channel %s is not registered"), chname))
  665. return
  666. }
  667. service.Notice(rb, fmt.Sprintf(client.t("Channel %s is registered"), chinfo.Name))
  668. service.Notice(rb, fmt.Sprintf(client.t("Founder: %s"), chinfo.Founder))
  669. service.Notice(rb, fmt.Sprintf(client.t("Registered at: %s"), chinfo.RegisteredAt.Format(time.RFC1123)))
  670. }
  671. func displayChannelSetting(service *ircService, settingName string, settings ChannelSettings, client *Client, rb *ResponseBuffer) {
  672. config := client.server.Config()
  673. switch strings.ToLower(settingName) {
  674. case "history":
  675. effectiveValue := historyEnabled(config.History.Persistent.RegisteredChannels, settings.History)
  676. service.Notice(rb, fmt.Sprintf(client.t("The stored channel history setting is: %s"), historyStatusToString(settings.History)))
  677. service.Notice(rb, fmt.Sprintf(client.t("Given current server settings, the channel history setting is: %s"), historyStatusToString(effectiveValue)))
  678. case "query-cutoff":
  679. effectiveValue := settings.QueryCutoff
  680. if effectiveValue == HistoryCutoffDefault {
  681. effectiveValue = config.History.Restrictions.queryCutoff
  682. }
  683. service.Notice(rb, fmt.Sprintf(client.t("The stored channel history query cutoff setting is: %s"), historyCutoffToString(settings.QueryCutoff)))
  684. service.Notice(rb, fmt.Sprintf(client.t("Given current server settings, the channel history query cutoff setting is: %s"), historyCutoffToString(effectiveValue)))
  685. default:
  686. service.Notice(rb, client.t("Invalid params"))
  687. }
  688. }
  689. func csGetHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  690. chname, setting := params[0], params[1]
  691. channel := server.channels.Get(chname)
  692. if channel == nil {
  693. service.Notice(rb, client.t("No such channel"))
  694. return
  695. }
  696. info := channel.ExportRegistration(IncludeSettings)
  697. if !csPrivsCheck(service, info, client, rb) {
  698. return
  699. }
  700. displayChannelSetting(service, setting, info.Settings, client, rb)
  701. }
  702. func csSetHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  703. chname, setting, value := params[0], params[1], params[2]
  704. channel := server.channels.Get(chname)
  705. if channel == nil {
  706. service.Notice(rb, client.t("No such channel"))
  707. return
  708. }
  709. info := channel.ExportRegistration(IncludeSettings)
  710. settings := info.Settings
  711. if !csPrivsCheck(service, info, client, rb) {
  712. return
  713. }
  714. var err error
  715. switch strings.ToLower(setting) {
  716. case "history":
  717. settings.History, err = historyStatusFromString(value)
  718. if err != nil {
  719. err = errInvalidParams
  720. break
  721. }
  722. channel.SetSettings(settings)
  723. channel.resizeHistory(server.Config())
  724. case "query-cutoff":
  725. settings.QueryCutoff, err = historyCutoffFromString(value)
  726. if err != nil {
  727. err = errInvalidParams
  728. break
  729. }
  730. channel.SetSettings(settings)
  731. }
  732. switch err {
  733. case nil:
  734. service.Notice(rb, client.t("Successfully changed the channel settings"))
  735. displayChannelSetting(service, setting, settings, client, rb)
  736. case errInvalidParams:
  737. service.Notice(rb, client.t("Invalid parameters"))
  738. default:
  739. server.logger.Error("internal", "CS SET error:", err.Error())
  740. service.Notice(rb, client.t("An error occurred"))
  741. }
  742. }